diff --git a/CHANGES b/CHANGES index 113f9ee0d..e29fe29b5 100644 --- a/CHANGES +++ b/CHANGES @@ -24,14 +24,30 @@ Emulation fixes: - ARM: Fix STR storing PC after address calculation - GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320) - GBA Memory: Misaligned SRAM writes are ignored + - GBA Memory: Improve gamepak prefetch timing - GBA Serialize: Fix serializing DMA transfer register - GBA Serialize: Fix audio serialization for desynced FIFOs - GBA Serialize: Fix audio DMA timing deserialization - GBA Video: Fix OAM not invalidating after reset (fixes mgba.io/i/1630) + - GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319) + - GBA Video: Fix Hblank timing + - GBA Video: Fix backdrop blending on lines without sprites (fixes mgba.io/i/1647) + - GBA Video: Fix OpenGL sprite flag priority Other fixes: + - Core: Fix race condition initializing thread proxy - Qt: Only dynamically reset video scale if a game is running - Qt: Fix race condition with proxied video events - Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642) + - Qt: Fix color selection in asset view (fixes mgba.io/i/1648) + - Qt: Fix missing OSD messages + - Qt: Fix crash unloading shaders + - Qt: Fix toggled actions on gamepads (fixes mgba.io/i/1650) + - Qt: Fix extraneous dialog (fixes mgba.io/i/1654) + - Util: Fix crash reading invalid ELFs +Misc: + - Qt: Renderer can be changed while a game is running + - Qt: Fix non-SDL build (fixes mgba.io/i/1656) + - Switch: Make OpenGL scale adjustable while running 0.8.0: (2020-01-21) Features: diff --git a/cinema/gba/bg/lady-sia/baseline_0000.png b/cinema/gba/bg/lady-sia/baseline_0000.png index 56454bbfa..282d9cf3b 100644 Binary files a/cinema/gba/bg/lady-sia/baseline_0000.png and b/cinema/gba/bg/lady-sia/baseline_0000.png differ diff --git a/cinema/gba/bg/lady-sia/baseline_0001.png b/cinema/gba/bg/lady-sia/baseline_0001.png index d1eb4323c..282d9cf3b 100644 Binary files a/cinema/gba/bg/lady-sia/baseline_0001.png and b/cinema/gba/bg/lady-sia/baseline_0001.png differ diff --git a/cinema/gba/bg/lady-sia/baseline_0002.png b/cinema/gba/bg/lady-sia/baseline_0002.png index 80f6dd2cb..14ffb7d46 100644 Binary files a/cinema/gba/bg/lady-sia/baseline_0002.png and b/cinema/gba/bg/lady-sia/baseline_0002.png differ diff --git a/cinema/gba/bg/lady-sia/baseline_0003.png b/cinema/gba/bg/lady-sia/baseline_0003.png index 30d9b4446..2e4d8ed37 100644 Binary files a/cinema/gba/bg/lady-sia/baseline_0003.png and b/cinema/gba/bg/lady-sia/baseline_0003.png differ diff --git a/cinema/gba/bg/lady-sia/baseline_0004.png b/cinema/gba/bg/lady-sia/baseline_0004.png new file mode 100644 index 000000000..6e0793103 Binary files /dev/null and b/cinema/gba/bg/lady-sia/baseline_0004.png differ diff --git a/cinema/gba/bg/lady-sia/baseline_0005.png b/cinema/gba/bg/lady-sia/baseline_0005.png new file mode 100644 index 000000000..b9320fb90 Binary files /dev/null and b/cinema/gba/bg/lady-sia/baseline_0005.png differ diff --git a/cinema/gba/bg/lady-sia/baseline_0006.png b/cinema/gba/bg/lady-sia/baseline_0006.png new file mode 100644 index 000000000..280f0152f Binary files /dev/null and b/cinema/gba/bg/lady-sia/baseline_0006.png differ diff --git a/cinema/gba/bg/lady-sia/baseline_0007.png b/cinema/gba/bg/lady-sia/baseline_0007.png new file mode 100644 index 000000000..b6355a2d5 Binary files /dev/null and b/cinema/gba/bg/lady-sia/baseline_0007.png differ diff --git a/cinema/gba/bg/lady-sia/test.mvl b/cinema/gba/bg/lady-sia/test.mvl index 214133dd2..ced00badf 100644 Binary files a/cinema/gba/bg/lady-sia/test.mvl and b/cinema/gba/bg/lady-sia/test.mvl differ diff --git a/cinema/gba/blend/backdrop-blend/baseline_0000.png b/cinema/gba/blend/backdrop-blend/baseline_0000.png new file mode 100644 index 000000000..577119a28 Binary files /dev/null and b/cinema/gba/blend/backdrop-blend/baseline_0000.png differ diff --git a/cinema/gba/blend/backdrop-blend/baseline_0001.png b/cinema/gba/blend/backdrop-blend/baseline_0001.png new file mode 100644 index 000000000..577119a28 Binary files /dev/null and b/cinema/gba/blend/backdrop-blend/baseline_0001.png differ diff --git a/cinema/gba/blend/backdrop-blend/baseline_0002.png b/cinema/gba/blend/backdrop-blend/baseline_0002.png new file mode 100644 index 000000000..1833c6baf Binary files /dev/null and b/cinema/gba/blend/backdrop-blend/baseline_0002.png differ diff --git a/cinema/gba/blend/backdrop-blend/baseline_0003.png b/cinema/gba/blend/backdrop-blend/baseline_0003.png new file mode 100644 index 000000000..1833c6baf Binary files /dev/null and b/cinema/gba/blend/backdrop-blend/baseline_0003.png differ diff --git a/cinema/gba/blend/backdrop-blend/baseline_0004.png b/cinema/gba/blend/backdrop-blend/baseline_0004.png new file mode 100644 index 000000000..f142054f1 Binary files /dev/null and b/cinema/gba/blend/backdrop-blend/baseline_0004.png differ diff --git a/cinema/gba/blend/backdrop-blend/test.mvl b/cinema/gba/blend/backdrop-blend/test.mvl new file mode 100644 index 000000000..7eef420c9 Binary files /dev/null and b/cinema/gba/blend/backdrop-blend/test.mvl differ diff --git a/cinema/gba/obj/unaligned-256-linear/baseline_0000.png b/cinema/gba/obj/unaligned-256-linear/baseline_0000.png index 4be2744f7..adf0534d0 100644 Binary files a/cinema/gba/obj/unaligned-256-linear/baseline_0000.png and b/cinema/gba/obj/unaligned-256-linear/baseline_0000.png differ diff --git a/cinema/gba/obj/unaligned-256-linear/baseline_0001.png b/cinema/gba/obj/unaligned-256-linear/baseline_0001.png index 4be2744f7..adf0534d0 100644 Binary files a/cinema/gba/obj/unaligned-256-linear/baseline_0001.png and b/cinema/gba/obj/unaligned-256-linear/baseline_0001.png differ diff --git a/cinema/gba/obj/unaligned-256-linear/baseline_0002.png b/cinema/gba/obj/unaligned-256-linear/baseline_0002.png index 5f20a6405..6b3cfab23 100644 Binary files a/cinema/gba/obj/unaligned-256-linear/baseline_0002.png and b/cinema/gba/obj/unaligned-256-linear/baseline_0002.png differ diff --git a/cinema/gba/obj/unaligned-256-linear/baseline_0003.png b/cinema/gba/obj/unaligned-256-linear/baseline_0003.png index 5f20a6405..6b3cfab23 100644 Binary files a/cinema/gba/obj/unaligned-256-linear/baseline_0003.png and b/cinema/gba/obj/unaligned-256-linear/baseline_0003.png differ diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index 141892b64..931ae5b64 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -91,7 +91,6 @@ struct GBA { struct mRTCSource* rtcSource; struct mRumble* rumble; - struct GBARRContext* rr; bool isPristine; size_t pristineRomSize; size_t yankedRomSize; diff --git a/include/mgba/internal/gba/renderers/gl.h b/include/mgba/internal/gba/renderers/gl.h index 7d30a3e1d..9831f3def 100644 --- a/include/mgba/internal/gba/renderers/gl.h +++ b/include/mgba/internal/gba/renderers/gl.h @@ -44,7 +44,6 @@ struct GBAVideoGLAffine { struct GBAVideoGLBackground { GLuint fbo; GLuint tex; - GLuint flags; unsigned index; int enabled; @@ -96,7 +95,6 @@ enum { GBA_GL_BG_CHARBASE, GBA_GL_BG_SIZE, GBA_GL_BG_OFFSET, - GBA_GL_BG_INFLAGS, GBA_GL_BG_TRANSFORM, GBA_GL_BG_RANGE, GBA_GL_BG_MOSAIC, diff --git a/include/mgba/internal/gba/rr/mgm.h b/include/mgba/internal/gba/rr/mgm.h deleted file mode 100644 index fd455a830..000000000 --- a/include/mgba/internal/gba/rr/mgm.h +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef RR_MGM_H -#define RR_MGM_H - -#include - -CXX_GUARD_START - -#include - -struct GBA; -struct VDir; -struct VFile; - -enum GBAMGMTag { - // Playback tags - TAG_INVALID = 0x00, - TAG_INPUT = 0x01, - TAG_FRAME = 0x02, - TAG_LAG = 0x03, - TAG_RESET = 0x04, - - // Stream chunking tags - TAG_BEGIN = 0x10, - TAG_END = 0x11, - TAG_PREVIOUSLY = 0x12, - TAG_NEXT_TIME = 0x13, - TAG_MAX_STREAM = 0x14, - - // Recording information tags - TAG_FRAME_COUNT = 0x20, - TAG_LAG_COUNT = 0x21, - TAG_RR_COUNT = 0x22, - TAG_INIT = 0x24, - TAG_INIT_EX_NIHILO = 0x24 | INIT_EX_NIHILO, - TAG_INIT_FROM_SAVEGAME = 0x24 | INIT_FROM_SAVEGAME, - TAG_INIT_FROM_SAVESTATE = 0x24 | INIT_FROM_SAVESTATE, - TAG_INIT_FROM_BOTH = 0x24 | INIT_FROM_BOTH, - - // User metadata tags - TAG_AUTHOR = 0x30, - TAG_COMMENT = 0x31, - - TAG_EOF = INT_MAX -}; - -struct GBAMGMContext { - struct GBARRContext d; - - // Playback state - bool isPlaying; - bool autorecord; - - // Recording state - bool isRecording; - bool inputThisFrame; - - // Metadata - uint32_t streamId; - - uint32_t maxStreamId; - off_t maxStreamIdOffset; - off_t initFromOffset; - off_t rrCountOffset; - - // Streaming state - struct VDir* streamDir; - struct VFile* metadataFile; - struct VFile* movieStream; - uint16_t currentInput; - enum GBAMGMTag peekedTag; - uint32_t nextTime; - uint32_t previously; -}; - -void GBAMGMContextCreate(struct GBAMGMContext*); - -bool GBAMGMSetStream(struct GBAMGMContext* mgm, struct VDir* stream); -bool GBAMGMCreateStream(struct GBAMGMContext* mgm, enum GBARRInitFrom initFrom); - -CXX_GUARD_END - -#endif diff --git a/include/mgba/internal/gba/rr/rr.h b/include/mgba/internal/gba/rr/rr.h deleted file mode 100644 index b4fc2e627..000000000 --- a/include/mgba/internal/gba/rr/rr.h +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef GBA_RR_H -#define GBA_RR_H - -#include - -CXX_GUARD_START - -#include -#include - -struct VFile; - -mLOG_DECLARE_CATEGORY(GBA_RR); - -enum GBARRInitFrom { - INIT_EX_NIHILO = 0, - INIT_FROM_SAVEGAME = 1, - INIT_FROM_SAVESTATE = 2, - INIT_FROM_BOTH = 3, -}; - -struct GBARRContext { - void (*destroy)(struct GBARRContext*); - - bool (*startPlaying)(struct GBARRContext*, bool autorecord); - void (*stopPlaying)(struct GBARRContext*); - bool (*startRecording)(struct GBARRContext*); - void (*stopRecording)(struct GBARRContext*); - - bool (*isPlaying)(const struct GBARRContext*); - bool (*isRecording)(const struct GBARRContext*); - - void (*nextFrame)(struct GBARRContext*); - void (*logInput)(struct GBARRContext*, uint16_t input); - uint16_t (*queryInput)(struct GBARRContext*); - bool (*queryReset)(struct GBARRContext*); - - void (*stateSaved)(struct GBARRContext*, struct GBASerializedState*); - void (*stateLoaded)(struct GBARRContext*, const struct GBASerializedState*); - - struct VFile* (*openSavedata)(struct GBARRContext* mgm, int flags); - struct VFile* (*openSavestate)(struct GBARRContext* mgm, int flags); - - uint32_t frames; - uint32_t lagFrames; - enum GBARRInitFrom initFrom; - - uint32_t rrCount; - - struct VFile* savedata; -}; - -void GBARRDestroy(struct GBARRContext*); - -void GBARRInitRecord(struct GBA*); -void GBARRInitPlay(struct GBA*); - -CXX_GUARD_END - -#endif diff --git a/include/mgba/internal/gba/rr/vbm.h b/include/mgba/internal/gba/rr/vbm.h deleted file mode 100644 index 66d19b76e..000000000 --- a/include/mgba/internal/gba/rr/vbm.h +++ /dev/null @@ -1,30 +0,0 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef GBA_VBM_H -#define GBA_VBM_H - -#include - -CXX_GUARD_START - -#include - -struct GBAVBMContext { - struct GBARRContext d; - - bool isPlaying; - - struct VFile* vbmFile; - int32_t inputOffset; -}; - -void GBAVBMContextCreate(struct GBAVBMContext*); - -bool GBAVBMSetStream(struct GBAVBMContext*, struct VFile*); - -CXX_GUARD_END - -#endif diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index de682dc4f..672d182cf 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -92,7 +92,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bits 6 - 7: Reserved * 0x001E0 - 0x001FF: Video miscellaneous state * | 0x001E0 - 0x001E3: Next event - * | 0x001E4 - 0x001FB: Reserved + * | 0x001E4 - 0x001F7: Reserved + * | 0x001F8 - 0x001FB: Miscellaneous flags * | 0x001FC - 0x001FF: Frame counter * 0x00200 - 0x00213: Timer 0 * | 0x00200 - 0x00201: Reload value @@ -192,8 +193,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | 0x002F4 - 0x002F7: GBA BIOS bus prefetch * | 0x002F8 - 0x002FB: CPU prefecth (decode slot) * | 0x002FC - 0x002FF: CPU prefetch (fetch slot) - * 0x00300 - 0x00303: Associated movie stream ID for record/replay (or 0 if no stream) - * 0x00304 - 0x00317: Savestate creation time (usec since 1970) + * 0x00300 - 0x00317: Reserved (leave zero) * 0x00318 - 0x0031B: Last prefetched program counter * 0x0031C - 0x0031F: Miscellaneous flags * | bit 0: Is CPU halted? @@ -210,6 +210,9 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * Total size: 0x61000 (397,312) bytes */ +DECL_BITFIELD(GBASerializedVideoFlags, uint32_t); +DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2); + DECL_BITFIELD(GBASerializedHWFlags1, uint16_t); DECL_BIT(GBASerializedHWFlags1, ReadWrite, 0); DECL_BIT(GBASerializedHWFlags1, GyroEdge, 1); @@ -267,7 +270,8 @@ struct GBASerializedState { struct { int32_t nextEvent; - int32_t reserved[6]; + int32_t reserved[5]; + GBASerializedVideoFlags flags; int32_t frameCounter; } video; @@ -322,8 +326,7 @@ struct GBASerializedState { uint32_t biosPrefetch; uint32_t cpuPrefetch[2]; - uint32_t associatedStreamId; - uint32_t reservedRr[5]; + uint32_t reservedCpu[6]; uint32_t lastPrefetchedPc; GBASerializedMiscFlags miscFlags; diff --git a/include/mgba/internal/gba/video.h b/include/mgba/internal/gba/video.h index 676a3c044..3a192b352 100644 --- a/include/mgba/internal/gba/video.h +++ b/include/mgba/internal/gba/video.h @@ -18,8 +18,9 @@ mLOG_DECLARE_CATEGORY(GBA_VIDEO); enum { VIDEO_HBLANK_PIXELS = 68, - VIDEO_HDRAW_LENGTH = 1006, - VIDEO_HBLANK_LENGTH = 226, + VIDEO_HDRAW_LENGTH = 960, + VIDEO_HBLANK_LENGTH = 272, + VIDEO_HBLANK_FLIP = 46, VIDEO_HORIZONTAL_LENGTH = 1232, VIDEO_VBLANK_PIXELS = 68, diff --git a/src/ds/renderers/software.c b/src/ds/renderers/software.c index 0e5198e2f..9e8f180f5 100644 --- a/src/ds/renderers/software.c +++ b/src/ds/renderers/software.c @@ -489,16 +489,16 @@ static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* rend } } - if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 4) { + if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 3) { ++softwareRenderer->bg[0].enabled; } - if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 4) { + if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 3) { ++softwareRenderer->bg[1].enabled; } - if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 4) { + if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 3) { ++softwareRenderer->bg[2].enabled; } - if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 4) { + if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 3) { ++softwareRenderer->bg[3].enabled; } diff --git a/src/feature/thread-proxy.c b/src/feature/thread-proxy.c index ba1785c46..d99e81066 100644 --- a/src/feature/thread-proxy.c +++ b/src/feature/thread-proxy.c @@ -137,8 +137,10 @@ static void _postEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger; MutexLock(&proxyRenderer->mutex); proxyRenderer->event = event; - ConditionWake(&proxyRenderer->toThreadCond); - ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); + while (proxyRenderer->event) { + ConditionWake(&proxyRenderer->toThreadCond); + ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); + } MutexUnlock(&proxyRenderer->mutex); } @@ -179,6 +181,7 @@ static THREAD_ENTRY _proxyThread(void* logger) { ThreadSetName("Proxy Renderer Thread"); MutexLock(&proxyRenderer->mutex); + ConditionWake(&proxyRenderer->fromThreadCond); while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) { ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex); if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) { diff --git a/src/gba/CMakeLists.txt b/src/gba/CMakeLists.txt index 74a79425c..94bb8f09f 100644 --- a/src/gba/CMakeLists.txt +++ b/src/gba/CMakeLists.txt @@ -39,10 +39,7 @@ set(SIO_FILES set(EXTRA_FILES extra/audio-mixer.c extra/battlechip.c - extra/proxy.c - rr/mgm.c - rr/rr.c - rr/vbm.c) + extra/proxy.c) set(DEBUGGER_FILES debugger/cli.c) diff --git a/src/gba/core.c b/src/gba/core.c index cff52111c..a912d3a69 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -355,8 +355,9 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c } return; } -#if defined(BUILD_GLES2) || defined(BUILD_GLES3) + struct GBACore* gbacore = (struct GBACore*) core; +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (strcmp("videoScale", option) == 0) { if (config != &core->config) { mCoreConfigCopyValue(&core->config, config, "videoScale"); @@ -369,6 +370,31 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c return; } #endif + if (strcmp("hwaccelVideo", option) == 0) { + struct GBAVideoRenderer* renderer = NULL; + if (gbacore->renderer.outputBuffer) { + renderer = &gbacore->renderer.d; + } + int fakeBool; +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) + if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) { + mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale); + renderer = &gbacore->glRenderer.d; + } else { + gbacore->glRenderer.scale = 1; + } +#endif +#ifndef MINIMAL_CORE + if (renderer && core->videoLogger) { + gbacore->proxyRenderer.logger = core->videoLogger; + GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, renderer); + renderer = &gbacore->proxyRenderer.d; + } +#endif + if (renderer) { + GBAVideoAssociateRenderer(&gba->video, renderer); + } + } } static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) { @@ -540,8 +566,10 @@ static void _GBACoreReset(struct mCore* core) { int fakeBool; #if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) { - renderer = &gbacore->glRenderer.d; mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale); + renderer = &gbacore->glRenderer.d; + } else { + gbacore->glRenderer.scale = 1; } #endif #ifndef DISABLE_THREADING diff --git a/src/gba/extra/proxy.c b/src/gba/extra/proxy.c index fd773a1d8..d485023f7 100644 --- a/src/gba/extra/proxy.c +++ b/src/gba/extra/proxy.c @@ -157,8 +157,8 @@ void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) { if (!proxyRenderer->logger->block) { proxyRenderer->backend->deinit(proxyRenderer->backend); } else { - proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT); mVideoLoggerRendererFlush(proxyRenderer->logger); + proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT); } mVideoLoggerRendererDeinit(proxyRenderer->logger); diff --git a/src/gba/gba.c b/src/gba/gba.c index 79a05ae1d..aa078a226 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -96,7 +95,6 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) { gba->luminanceSource = 0; gba->rtcSource = 0; gba->rumble = 0; - gba->rr = 0; gba->romVf = 0; gba->biosVf = 0; @@ -170,7 +168,6 @@ void GBADestroy(struct GBA* gba) { GBAVideoDeinit(&gba->video); GBAAudioDeinit(&gba->audio); GBASIODeinit(&gba->sio); - gba->rr = 0; mTimingDeinit(&gba->timing); mCoreCallbacksListDeinit(&gba->coreCallbacks); } @@ -196,10 +193,8 @@ void GBAReset(struct ARMCore* cpu) { cpu->gprs[ARM_SP] = SP_BASE_SYSTEM; struct GBA* gba = (struct GBA*) cpu->master; - if (!gba->rr || (!gba->rr->isPlaying(gba->rr) && !gba->rr->isRecording(gba->rr))) { - gba->memory.savedata.maskWriteback = false; - GBASavedataUnmask(&gba->memory.savedata); - } + gba->memory.savedata.maskWriteback = false; + GBASavedataUnmask(&gba->memory.savedata); gba->cpuBlocked = false; gba->earlyExit = false; @@ -812,10 +807,6 @@ void GBAFrameStarted(struct GBA* gba) { void GBAFrameEnded(struct GBA* gba) { GBASavedataClean(&gba->memory.savedata, gba->video.frameCounter); - if (gba->rr) { - gba->rr->nextFrame(gba->rr); - } - if (gba->cpu->components && gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]) { struct mCheatDevice* device = (struct mCheatDevice*) gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]; size_t i; diff --git a/src/gba/io.c b/src/gba/io.c index bd7608654..0b3371777 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -8,7 +8,6 @@ #include #include #include -#include #include mLOG_DEFINE_CATEGORY(GBA_IO, "GBA I/O", "gba.io"); @@ -734,31 +733,24 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { } } } - if (gba->rr && gba->rr->isPlaying(gba->rr)) { - return 0x3FF ^ gba->rr->queryInput(gba->rr); - } else { - uint16_t input = 0; - if (gba->keyCallback) { - input = gba->keyCallback->readKeys(gba->keyCallback); - if (gba->keySource) { - *gba->keySource = input; - } - } else if (gba->keySource) { - input = *gba->keySource; - if (!gba->allowOpposingDirections) { - unsigned rl = input & 0x030; - unsigned ud = input & 0x0C0; - input &= 0x30F; - if (rl != 0x030) { - input |= rl; - } - if (ud != 0x0C0) { - input |= ud; - } - } + uint16_t input = 0; + if (gba->keyCallback) { + input = gba->keyCallback->readKeys(gba->keyCallback); + if (gba->keySource) { + *gba->keySource = input; } - if (gba->rr && gba->rr->isRecording(gba->rr)) { - gba->rr->logInput(gba->rr, input); + } else if (gba->keySource) { + input = *gba->keySource; + if (!gba->allowOpposingDirections) { + unsigned rl = input & 0x030; + unsigned ud = input & 0x0C0; + input &= 0x30F; + if (rl != 0x030) { + input |= rl; + } + if (ud != 0x0C0) { + input |= ud; + } } return 0x3FF ^ input; } diff --git a/src/gba/memory.c b/src/gba/memory.c index d4f94ef99..26abc9735 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -1626,17 +1626,19 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) { maxLoads -= previousLoads; } - int32_t s = cpu->memory.activeSeqCycles16 + 1; + int32_t s = cpu->memory.activeSeqCycles16; int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16 + 1; // Figure out how many sequential loads we can jam in - int32_t stall = s; + int32_t stall = s + 1; int32_t loads = 1; while (stall < wait && loads < maxLoads) { stall += s; ++loads; } + memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * (loads + previousLoads - 1); + if (stall > wait) { // The wait cannot take less time than the prefetch stalls wait = stall; @@ -1645,10 +1647,9 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) { // This instruction used to have an N, convert it to an S. wait -= n2s; - memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * (loads + previousLoads - 1); - // The next |loads|S waitstates disappear entirely, so long as they're all in a row - cpu->cycles -= (s - 1) * loads; + wait -= stall - 1; + return wait; } diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index c8f628349..29ba0a16f 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -117,7 +117,6 @@ static const struct GBAVideoGLUniform _uniformsMode0[] = { { "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 } }; @@ -130,10 +129,8 @@ static const char* const _renderMode0 = "uniform int charBase;\n" "uniform int size;\n" "uniform int offset[160];\n" - "uniform ivec4 inflags;\n" "uniform ivec2 mosaic;\n" "OUT(0) out vec4 color;\n" - "OUT(1) out ivec4 flags;\n" "vec4 renderTile(int tile, int paletteId, ivec2 localCoord);\n" @@ -171,7 +168,6 @@ static const char* const _renderMode0 = " }\n" " int tile = int(map.a * 15.9) + int(map.b * 15.9) * 16 + (tileFlags & 0x3) * 256;\n" " color = renderTile(tile, int(map.r * 15.9), coord & 7);\n" - " flags = inflags;\n" "}"; static const char* const _fetchTileOverflow = @@ -199,7 +195,6 @@ static const struct GBAVideoGLUniform _uniformsMode2[] = { { "screenBase", GBA_GL_BG_SCREENBASE, }, { "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, }, @@ -235,12 +230,10 @@ static const char* const _renderMode2 = "uniform int screenBase;\n" "uniform int charBase;\n" "uniform int size;\n" - "uniform ivec4 inflags;\n" "uniform ivec4 transform[160];\n" "uniform ivec2 range;\n" "uniform ivec2 mosaic;\n" "OUT(0) out vec4 color;\n" - "OUT(1) out ivec4 flags;\n" "vec4 fetchTile(ivec2 coord);\n" "vec2 interpolate(ivec2 arr[4], float x);\n" @@ -284,7 +277,6 @@ static const char* const _renderMode2 = " vec2 mixedTransform = interpolate(mat, lin);\n" " vec2 mixedOffset = interpolate(offset, lin);\n" " color = fetchTile(ivec2(mixedTransform * incoord.x + mixedOffset));\n" - " flags = inflags;\n" "}"; static const struct GBAVideoGLUniform _uniformsMode35[] = { @@ -293,7 +285,6 @@ static const struct GBAVideoGLUniform _uniformsMode35[] = { { "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, }, @@ -306,12 +297,10 @@ static const char* const _renderMode35 = "uniform sampler2D vram;\n" "uniform int charBase;\n" "uniform ivec2 size;\n" - "uniform ivec4 inflags;\n" "uniform ivec4 transform[160];\n" "uniform ivec2 range;\n" "uniform ivec2 mosaic;\n" "OUT(0) out vec4 color;\n" - "OUT(1) out ivec4 flags;\n" "vec2 interpolate(ivec2 arr[4], float x);\n" "void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n" @@ -347,7 +336,6 @@ static const char* const _renderMode35 = " 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(float(sixteen & 0x1F) / 31., float((sixteen >> 5) & 0x1F) / 31., float((sixteen >> 10) & 0x1F) / 31., 1.);\n" - " flags = inflags;\n" "}"; static const struct GBAVideoGLUniform _uniformsMode4[] = { @@ -357,7 +345,6 @@ static const struct GBAVideoGLUniform _uniformsMode4[] = { { "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, }, @@ -371,12 +358,10 @@ static const char* const _renderMode4 = "uniform int palette[256];\n" "uniform int charBase;\n" "uniform ivec2 size;\n" - "uniform ivec4 inflags;\n" "uniform ivec4 transform[160];\n" "uniform ivec2 range;\n" "uniform ivec2 mosaic;\n" "OUT(0) out vec4 color;\n" - "OUT(1) out ivec4 flags;\n" "vec2 interpolate(ivec2 arr[4], float x);\n" "void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n" @@ -413,7 +398,6 @@ static const char* const _renderMode4 = " ivec2 entry = ivec2(twoEntries[3 - 2 * (address & 1)] * 15.9, twoEntries[2 - 2 * (address & 1)] * 15.9);\n" " int paletteEntry = palette[entry.y * 16 + entry.x];\n" " color = vec4(PALETTE_ENTRY(paletteEntry), 1.);\n" - " flags = inflags;\n" "}"; static const struct GBAVideoGLUniform _uniformsObj[] = { @@ -567,7 +551,7 @@ static const struct GBAVideoGLUniform _uniformsFinalize[] = { { "maxPos", GBA_GL_VS_MAXPOS, }, { "scale", GBA_GL_FINALIZE_SCALE, }, { "layers", GBA_GL_FINALIZE_LAYERS, }, - { "flags", GBA_GL_FINALIZE_FLAGS, }, + { "objFlags", GBA_GL_FINALIZE_FLAGS, }, { "window", GBA_GL_FINALIZE_WINDOW, }, { "backdrop", GBA_GL_FINALIZE_BACKDROP, }, { "backdropFlags", GBA_GL_FINALIZE_BACKDROPFLAGS, }, @@ -578,7 +562,7 @@ static const char* const _finalize = "in vec2 texCoord;\n" "uniform int scale;\n" "uniform sampler2D layers[5];\n" - "uniform isampler2D flags[5];\n" + "uniform isampler2D objFlags;\n" "uniform isampler2D window;\n" "uniform sampler2D backdrop;\n" "uniform isampler2D backdropFlags;\n" @@ -610,35 +594,35 @@ static const char* const _finalize = " if ((layerWindow & 16) != 0) {\n" " vec4 pix = texelFetch(layers[4], coord, 0);\n" " if (pix.a != 0.) {\n" - " ivec4 inflags = ivec4(texelFetch(flags[4], coord, 0));\n" + " ivec4 inflags = ivec4(texelFetch(objFlags, coord, 0));\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " }\n" " }\n" " if ((layerWindow & 1) != 0) {\n" " vec4 pix = texelFetch(layers[0], coord, 0);\n" " if (pix.a != 0.) {\n" - " ivec4 inflags = ivec4(texelFetch(flags[0], coord, 0).xyz, 0);\n" + " ivec4 inflags = ivec4(texelFetch(backdropFlags, ivec2(1, texCoord.y), 0));\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " }\n" " }\n" " if ((layerWindow & 2) != 0) {\n" " vec4 pix = texelFetch(layers[1], coord, 0);\n" " if (pix.a != 0.) {\n" - " ivec4 inflags = ivec4(texelFetch(flags[1], coord, 0).xyz, 0);\n" + " ivec4 inflags = ivec4(texelFetch(backdropFlags, ivec2(2, texCoord.y), 0));\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " }\n" " }\n" " if ((layerWindow & 4) != 0) {\n" " vec4 pix = texelFetch(layers[2], coord, 0);\n" " if (pix.a != 0.) {\n" - " ivec4 inflags = ivec4(texelFetch(flags[2], coord, 0).xyz, 0);\n" + " ivec4 inflags = ivec4(texelFetch(backdropFlags, ivec2(3, texCoord.y), 0));\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " }\n" " }\n" " if ((layerWindow & 8) != 0) {\n" " vec4 pix = texelFetch(layers[3], coord, 0);\n" " if (pix.a != 0.) {\n" - " ivec4 inflags = ivec4(texelFetch(flags[3], coord, 0).xyz, 0);\n" + " ivec4 inflags = ivec4(texelFetch(backdropFlags, ivec2(4, texCoord.y), 0));\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " }\n" " }\n" @@ -747,7 +731,7 @@ static void _initFramebufferTextureEx(GLuint tex, GLenum internalFormat, GLenum 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, internalFormat, scale > 0 ? GBA_VIDEO_HORIZONTAL_PIXELS * scale : 1, GBA_VIDEO_VERTICAL_PIXELS * (scale > 0 ? scale : 1), 0, format, type, 0); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, scale > 0 ? GBA_VIDEO_HORIZONTAL_PIXELS * scale : 8, GBA_VIDEO_VERTICAL_PIXELS * (scale > 0 ? scale : 1), 0, format, type, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0); } @@ -764,7 +748,7 @@ static void _initFramebuffers(struct GBAVideoGLRenderer* glRenderer) { glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]); _initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_BACKDROP_COLOR], GL_RGB, GL_COLOR_ATTACHMENT0, 0); - _initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_BACKDROP_FLAGS], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, glRenderer->scale); + _initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_BACKDROP_FLAGS], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, 0); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_WINDOW]); _initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT0, glRenderer->scale); @@ -777,7 +761,6 @@ static void _initFramebuffers(struct GBAVideoGLRenderer* glRenderer) { struct GBAVideoGLBackground* bg = &glRenderer->bg[i]; glBindFramebuffer(GL_FRAMEBUFFER, bg->fbo); _initFramebufferTexture(bg->tex, GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale); - _initFramebufferTextureEx(bg->flags, GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, glRenderer->scale); } glBindFramebuffer(GL_FRAMEBUFFER, 0); } @@ -825,7 +808,6 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) { bg->affine.sy = 0; glGenFramebuffers(1, &bg->fbo); glGenTextures(1, &bg->tex); - glGenTextures(1, &bg->flags); } _initFramebuffers(glRenderer); @@ -924,7 +906,6 @@ void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer) { struct GBAVideoGLBackground* bg = &glRenderer->bg[i]; glDeleteFramebuffers(1, &bg->fbo); glDeleteTextures(1, &bg->tex); - glDeleteTextures(1, &bg->flags); } } @@ -1400,12 +1381,12 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { #endif glClearStencil(0); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]); - glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); for (i = 0; i < 4; ++i) { glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->bg[i].fbo); - glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); glClear(GL_COLOR_BUFFER_BIT); } } @@ -1428,11 +1409,21 @@ void _drawScanlines(struct GBAVideoGLRenderer* glRenderer, int y) { glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); glClearBufferfv(GL_COLOR, 0, (GLfloat[]) { ((backdrop >> 16) & 0xF8) / 248., ((backdrop >> 8) & 0xF8) / 248., (backdrop & 0xF8) / 248., 1.f }); glClearBufferiv(GL_COLOR, 1, (GLint[]) { 32, glRenderer->target1Bd | (glRenderer->target2Bd * 2) | (glRenderer->blendEffect * 4), glRenderer->blda, 0 }); + + glDrawBuffers(2, (GLenum[]) { GL_NONE, GL_COLOR_ATTACHMENT1 }); + int i; + for (i = 0; i < 4; ++i) { + glScissor(i + 1, glRenderer->firstY, i + 2, y - glRenderer->firstY + 1); + glClearBufferiv(GL_COLOR, 1, (GLint[]) { glRenderer->bg[i].priority, + glRenderer->bg[i].target1 | (glRenderer->bg[i].target2 << 1) | (glRenderer->blendEffect << 2), + glRenderer->blda, 0 }); + } + + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); GBAVideoGLRendererDrawWindow(glRenderer, y); if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) { - int i; glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glEnable(GL_STENCIL_TEST); glDepthFunc(GL_LESS); @@ -1615,33 +1606,25 @@ void _finalizeLayers(struct GBAVideoGLRenderer* renderer) { glActiveTexture(GL_TEXTURE0 + 3); glBindTexture(GL_TEXTURE_2D, renderer->bg[0].tex); glActiveTexture(GL_TEXTURE0 + 4); - glBindTexture(GL_TEXTURE_2D, renderer->bg[0].flags); - glActiveTexture(GL_TEXTURE0 + 5); glBindTexture(GL_TEXTURE_2D, renderer->bg[1].tex); - glActiveTexture(GL_TEXTURE0 + 6); - glBindTexture(GL_TEXTURE_2D, renderer->bg[1].flags); - glActiveTexture(GL_TEXTURE0 + 7); + glActiveTexture(GL_TEXTURE0 + 5); glBindTexture(GL_TEXTURE_2D, renderer->bg[2].tex); - glActiveTexture(GL_TEXTURE0 + 8); - glBindTexture(GL_TEXTURE_2D, renderer->bg[2].flags); - glActiveTexture(GL_TEXTURE0 + 9); + glActiveTexture(GL_TEXTURE0 + 6); glBindTexture(GL_TEXTURE_2D, renderer->bg[3].tex); - glActiveTexture(GL_TEXTURE0 + 10); - glBindTexture(GL_TEXTURE_2D, renderer->bg[3].flags); - glActiveTexture(GL_TEXTURE0 + 11); + glActiveTexture(GL_TEXTURE0 + 7); glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_COLOR]); - glActiveTexture(GL_TEXTURE0 + 12); + glActiveTexture(GL_TEXTURE0 + 8); glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_FLAGS]); 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 }); + glUniform1iv(uniforms[GBA_GL_FINALIZE_LAYERS], 5, (GLint[]) { 3, 4, 5, 6, 1 }); + glUniform1i(uniforms[GBA_GL_FINALIZE_FLAGS], 2); glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0); glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0); - glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROP], 11); - glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROPFLAGS], 12); + glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROP], 7); + glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROPFLAGS], 8); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -1734,13 +1717,13 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB glStencilFunc(GL_EQUAL, 1, 1); glUseProgram(shader->program); glDrawBuffers(2, (GLenum[]) { GL_NONE, GL_COLOR_ATTACHMENT1 }); - glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE); glBindVertexArray(shader->vao); glUniform2i(uniforms[GBA_GL_VS_LOC], totalHeight, 0); glUniform2i(uniforms[GBA_GL_VS_MAXPOS], totalWidth, totalHeight); - glUniform4i(uniforms[GBA_GL_OBJ_INFLAGS], GBAObjAttributesCGetPriority(sprite->c), 0, 0, 0); + glUniform4i(uniforms[GBA_GL_OBJ_INFLAGS], GBAObjAttributesCGetPriority(sprite->c), + (renderer->target1Obj || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT) | (renderer->target2Obj * 2) | (renderer->blendEffect * 4), + renderer->blda, GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); } @@ -1758,10 +1741,7 @@ void _prepareBackground(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBa } else { glUniform2i(uniforms[GBA_GL_BG_MOSAIC], 0, 0); } - glUniform4i(uniforms[GBA_GL_BG_INFLAGS], background->priority, - background->target1 | (background->target2 * 2) | (renderer->blendEffect * 4), - renderer->blda, 0); - glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); + glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); } void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { diff --git a/src/gba/renderers/software-private.h b/src/gba/renderers/software-private.h index 79228a233..42904d41a 100644 --- a/src/gba/renderers/software-private.h +++ b/src/gba/renderers/software-private.h @@ -234,7 +234,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re } #define TEST_LAYER_ENABLED(X) \ - (softwareRenderer->bg[X].enabled == 4 && \ + (softwareRenderer->bg[X].enabled == 3 && \ (GBAWindowControlIsBg ## X ## Enable(softwareRenderer->currentWindow.packed) || \ (GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (softwareRenderer->objwin.packed))) && \ softwareRenderer->bg[X].priority == priority) diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 2321236f8..b05a7efb3 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -691,19 +691,19 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy; } - if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 4) { + if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 3) { ++softwareRenderer->bg[0].enabled; DIRTY_SCANLINE(softwareRenderer, y); } - if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 4) { + if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 3) { ++softwareRenderer->bg[1].enabled; DIRTY_SCANLINE(softwareRenderer, y); } - if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 4) { + if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 3) { ++softwareRenderer->bg[2].enabled; DIRTY_SCANLINE(softwareRenderer, y); } - if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 4) { + if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 3) { ++softwareRenderer->bg[3].enabled; DIRTY_SCANLINE(softwareRenderer, y); } @@ -737,16 +737,16 @@ static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* rendere softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy; if (softwareRenderer->bg[0].enabled > 0) { - softwareRenderer->bg[0].enabled = 4; + softwareRenderer->bg[0].enabled = 3; } if (softwareRenderer->bg[1].enabled > 0) { - softwareRenderer->bg[1].enabled = 4; + softwareRenderer->bg[1].enabled = 3; } if (softwareRenderer->bg[2].enabled > 0) { - softwareRenderer->bg[2].enabled = 4; + softwareRenderer->bg[2].enabled = 3; } if (softwareRenderer->bg[3].enabled > 0) { - softwareRenderer->bg[3].enabled = 4; + softwareRenderer->bg[3].enabled = 3; } } @@ -773,7 +773,7 @@ static void _enableBg(struct GBAVideoSoftwareRenderer* renderer, int bg, bool ac } else if (!wasActive && active) { if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) { // TODO: Investigate in more depth how switching background works in different modes - renderer->bg[bg].enabled = 4; + renderer->bg[bg].enabled = 3; } else { renderer->bg[bg].enabled = 1; } diff --git a/src/gba/rr/mgm.c b/src/gba/rr/mgm.c deleted file mode 100644 index d56e979bb..000000000 --- a/src/gba/rr/mgm.c +++ /dev/null @@ -1,590 +0,0 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include - -#include -#include -#include - -#define BINARY_EXT ".mgm" -#define BINARY_MAGIC "GBAb" -#define METADATA_FILENAME "metadata" BINARY_EXT - -enum { - INVALID_INPUT = 0x8000 -}; - -static void GBAMGMContextDestroy(struct GBARRContext*); - -static bool GBAMGMStartPlaying(struct GBARRContext*, bool autorecord); -static void GBAMGMStopPlaying(struct GBARRContext*); -static bool GBAMGMStartRecording(struct GBARRContext*); -static void GBAMGMStopRecording(struct GBARRContext*); - -static bool GBAMGMIsPlaying(const struct GBARRContext*); -static bool GBAMGMIsRecording(const struct GBARRContext*); - -static void GBAMGMNextFrame(struct GBARRContext*); -static void GBAMGMLogInput(struct GBARRContext*, uint16_t input); -static uint16_t GBAMGMQueryInput(struct GBARRContext*); -static bool GBAMGMQueryReset(struct GBARRContext*); - -static void GBAMGMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state); -static void GBAMGMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state); - -static bool _loadStream(struct GBAMGMContext*, uint32_t streamId); -static bool _incrementStream(struct GBAMGMContext*, bool recursive); -static bool _finishSegment(struct GBAMGMContext*); -static bool _skipSegment(struct GBAMGMContext*); -static bool _markRerecord(struct GBAMGMContext*); - -static bool _emitMagic(struct GBAMGMContext*, struct VFile* vf); -static bool _verifyMagic(struct GBAMGMContext*, struct VFile* vf); -static enum GBAMGMTag _readTag(struct GBAMGMContext*, struct VFile* vf); -static bool _seekTag(struct GBAMGMContext*, struct VFile* vf, enum GBAMGMTag tag); -static bool _emitTag(struct GBAMGMContext*, struct VFile* vf, uint8_t tag); -static bool _emitEnd(struct GBAMGMContext*, struct VFile* vf); - -static bool _parseMetadata(struct GBAMGMContext*, struct VFile* vf); - -static bool _markStreamNext(struct GBAMGMContext*, uint32_t newStreamId, bool recursive); -static void _streamEndReached(struct GBAMGMContext*); - -static struct VFile* GBAMGMOpenSavedata(struct GBARRContext*, int flags); -static struct VFile* GBAMGMOpenSavestate(struct GBARRContext*, int flags); - -void GBAMGMContextCreate(struct GBAMGMContext* mgm) { - memset(mgm, 0, sizeof(*mgm)); - - mgm->d.destroy = GBAMGMContextDestroy; - - mgm->d.startPlaying = GBAMGMStartPlaying; - mgm->d.stopPlaying = GBAMGMStopPlaying; - mgm->d.startRecording = GBAMGMStartRecording; - mgm->d.stopRecording = GBAMGMStopRecording; - - mgm->d.isPlaying = GBAMGMIsPlaying; - mgm->d.isRecording = GBAMGMIsRecording; - - mgm->d.nextFrame = GBAMGMNextFrame; - mgm->d.logInput = GBAMGMLogInput; - mgm->d.queryInput = GBAMGMQueryInput; - mgm->d.queryReset = GBAMGMQueryReset; - - mgm->d.stateSaved = GBAMGMStateSaved; - mgm->d.stateLoaded = GBAMGMStateLoaded; - - mgm->d.openSavedata = GBAMGMOpenSavedata; - mgm->d.openSavestate = GBAMGMOpenSavestate; -} - -void GBAMGMContextDestroy(struct GBARRContext* rr) { - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - if (mgm->metadataFile) { - mgm->metadataFile->close(mgm->metadataFile); - } -} - -bool GBAMGMSetStream(struct GBAMGMContext* mgm, struct VDir* stream) { - if (mgm->movieStream && !mgm->movieStream->close(mgm->movieStream)) { - return false; - } - - if (mgm->metadataFile && !mgm->metadataFile->close(mgm->metadataFile)) { - return false; - } - - mgm->streamDir = stream; - mgm->metadataFile = mgm->streamDir->openFile(mgm->streamDir, METADATA_FILENAME, O_CREAT | O_RDWR); - mgm->currentInput = INVALID_INPUT; - if (!_parseMetadata(mgm, mgm->metadataFile)) { - mgm->metadataFile->close(mgm->metadataFile); - mgm->metadataFile = 0; - mgm->maxStreamId = 0; - } - mgm->streamId = 1; - mgm->movieStream = 0; - return true; -} - -bool GBAMGMCreateStream(struct GBAMGMContext* mgm, enum GBARRInitFrom initFrom) { - if (mgm->metadataFile) { - mgm->metadataFile->truncate(mgm->metadataFile, 0); - } else { - mgm->metadataFile = mgm->streamDir->openFile(mgm->streamDir, METADATA_FILENAME, O_CREAT | O_TRUNC | O_RDWR); - } - _emitMagic(mgm, mgm->metadataFile); - - mgm->d.initFrom = initFrom; - mgm->initFromOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR); - _emitTag(mgm, mgm->metadataFile, TAG_INIT | initFrom); - - mgm->streamId = 0; - mgm->maxStreamId = 0; - _emitTag(mgm, mgm->metadataFile, TAG_MAX_STREAM); - mgm->maxStreamIdOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR); - mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId)); - - mgm->d.rrCount = 0; - _emitTag(mgm, mgm->metadataFile, TAG_RR_COUNT); - mgm->rrCountOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR); - mgm->metadataFile->write(mgm->metadataFile, &mgm->d.rrCount, sizeof(mgm->d.rrCount)); - return true; -} - -bool _loadStream(struct GBAMGMContext* mgm, uint32_t streamId) { - if (mgm->movieStream && !mgm->movieStream->close(mgm->movieStream)) { - return false; - } - mgm->movieStream = 0; - mgm->streamId = streamId; - mgm->currentInput = INVALID_INPUT; - char buffer[14]; - snprintf(buffer, sizeof(buffer), "%u" BINARY_EXT, streamId); - if (mgm->d.isRecording(&mgm->d)) { - int flags = O_CREAT | O_RDWR; - if (streamId > mgm->maxStreamId) { - flags |= O_TRUNC; - } - mgm->movieStream = mgm->streamDir->openFile(mgm->streamDir, buffer, flags); - } else if (mgm->d.isPlaying(&mgm->d)) { - mgm->movieStream = mgm->streamDir->openFile(mgm->streamDir, buffer, O_RDONLY); - mgm->peekedTag = TAG_INVALID; - if (!mgm->movieStream || !_verifyMagic(mgm, mgm->movieStream) || !_seekTag(mgm, mgm->movieStream, TAG_BEGIN)) { - mgm->d.stopPlaying(&mgm->d); - } - } - mLOG(GBA_RR, DEBUG, "Loading segment: %u", streamId); - mgm->d.frames = 0; - mgm->d.lagFrames = 0; - return true; -} - -bool _incrementStream(struct GBAMGMContext* mgm, bool recursive) { - uint32_t newStreamId = mgm->maxStreamId + 1; - uint32_t oldStreamId = mgm->streamId; - if (mgm->d.isRecording(&mgm->d) && mgm->movieStream) { - if (!_markStreamNext(mgm, newStreamId, recursive)) { - return false; - } - } - if (!_loadStream(mgm, newStreamId)) { - return false; - } - mLOG(GBA_RR, DEBUG, "New segment: %u", newStreamId); - _emitMagic(mgm, mgm->movieStream); - mgm->maxStreamId = newStreamId; - _emitTag(mgm, mgm->movieStream, TAG_PREVIOUSLY); - mgm->movieStream->write(mgm->movieStream, &oldStreamId, sizeof(oldStreamId)); - _emitTag(mgm, mgm->movieStream, TAG_BEGIN); - - mgm->metadataFile->seek(mgm->metadataFile, mgm->maxStreamIdOffset, SEEK_SET); - mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId)); - mgm->previously = oldStreamId; - return true; -} - -bool GBAMGMStartPlaying(struct GBARRContext* rr, bool autorecord) { - if (rr->isRecording(rr) || rr->isPlaying(rr)) { - return false; - } - - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - mgm->isPlaying = true; - if (!_loadStream(mgm, 1)) { - mgm->isPlaying = false; - return false; - } - mgm->autorecord = autorecord; - return true; -} - -void GBAMGMStopPlaying(struct GBARRContext* rr) { - if (!rr->isPlaying(rr)) { - return; - } - - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - mgm->isPlaying = false; - if (mgm->movieStream) { - mgm->movieStream->close(mgm->movieStream); - mgm->movieStream = 0; - } -} - -bool GBAMGMStartRecording(struct GBARRContext* rr) { - if (rr->isRecording(rr) || rr->isPlaying(rr)) { - return false; - } - - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - if (!mgm->maxStreamIdOffset) { - _emitTag(mgm, mgm->metadataFile, TAG_MAX_STREAM); - mgm->maxStreamIdOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR); - mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId)); - } - - mgm->isRecording = true; - return _incrementStream(mgm, false); -} - -void GBAMGMStopRecording(struct GBARRContext* rr) { - if (!rr->isRecording(rr)) { - return; - } - - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - mgm->isRecording = false; - if (mgm->movieStream) { - _emitEnd(mgm, mgm->movieStream); - mgm->movieStream->close(mgm->movieStream); - mgm->movieStream = 0; - } -} - -bool GBAMGMIsPlaying(const struct GBARRContext* rr) { - const struct GBAMGMContext* mgm = (const struct GBAMGMContext*) rr; - return mgm->isPlaying; -} - -bool GBAMGMIsRecording(const struct GBARRContext* rr) { - const struct GBAMGMContext* mgm = (const struct GBAMGMContext*) rr; - return mgm->isRecording; -} - -void GBAMGMNextFrame(struct GBARRContext* rr) { - if (!rr->isRecording(rr) && !rr->isPlaying(rr)) { - return; - } - - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - if (rr->isPlaying(rr)) { - while (mgm->peekedTag == TAG_INPUT) { - _readTag(mgm, mgm->movieStream); - mLOG(GBA_RR, WARN, "Desync detected!"); - } - if (mgm->peekedTag == TAG_LAG) { - mLOG(GBA_RR, DEBUG, "Lag frame marked in stream"); - if (mgm->inputThisFrame) { - mLOG(GBA_RR, WARN, "Lag frame in stream does not match movie"); - } - } - } - - ++mgm->d.frames; - mLOG(GBA_RR, DEBUG, "Frame: %u", mgm->d.frames); - if (!mgm->inputThisFrame) { - ++mgm->d.lagFrames; - mLOG(GBA_RR, DEBUG, "Lag frame: %u", mgm->d.lagFrames); - } - - if (rr->isRecording(rr)) { - if (!mgm->inputThisFrame) { - _emitTag(mgm, mgm->movieStream, TAG_LAG); - } - _emitTag(mgm, mgm->movieStream, TAG_FRAME); - mgm->inputThisFrame = false; - } else { - if (!_seekTag(mgm, mgm->movieStream, TAG_FRAME)) { - _streamEndReached(mgm); - } - } -} - -void GBAMGMLogInput(struct GBARRContext* rr, uint16_t keys) { - if (!rr->isRecording(rr)) { - return; - } - - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - if (keys != mgm->currentInput) { - _emitTag(mgm, mgm->movieStream, TAG_INPUT); - mgm->movieStream->write(mgm->movieStream, &keys, sizeof(keys)); - mgm->currentInput = keys; - } - mLOG(GBA_RR, DEBUG, "Input log: %03X", mgm->currentInput); - mgm->inputThisFrame = true; -} - -uint16_t GBAMGMQueryInput(struct GBARRContext* rr) { - if (!rr->isPlaying(rr)) { - return 0; - } - - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - if (mgm->peekedTag == TAG_INPUT) { - _readTag(mgm, mgm->movieStream); - } - mgm->inputThisFrame = true; - if (mgm->currentInput == INVALID_INPUT) { - mLOG(GBA_RR, WARN, "Stream did not specify input"); - } - mLOG(GBA_RR, DEBUG, "Input replay: %03X", mgm->currentInput); - return mgm->currentInput; -} - -bool GBAMGMQueryReset(struct GBARRContext* rr) { - if (!rr->isPlaying(rr)) { - return 0; - } - - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - return mgm->peekedTag == TAG_RESET; -} - -void GBAMGMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state) { - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - if (rr->isRecording(rr)) { - state->associatedStreamId = mgm->streamId; - _finishSegment(mgm); - } -} - -void GBAMGMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state) { - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - if (rr->isRecording(rr)) { - if (state->associatedStreamId != mgm->streamId) { - _loadStream(mgm, state->associatedStreamId); - _incrementStream(mgm, true); - } else { - _finishSegment(mgm); - } - _markRerecord(mgm); - } else if (rr->isPlaying(rr)) { - _loadStream(mgm, state->associatedStreamId); - _skipSegment(mgm); - } -} - -bool _finishSegment(struct GBAMGMContext* mgm) { - if (mgm->movieStream) { - if (!_emitEnd(mgm, mgm->movieStream)) { - return false; - } - } - return _incrementStream(mgm, false); -} - -bool _skipSegment(struct GBAMGMContext* mgm) { - mgm->nextTime = 0; - while (_readTag(mgm, mgm->movieStream) != TAG_EOF); - if (!mgm->nextTime || !_loadStream(mgm, mgm->nextTime)) { - _streamEndReached(mgm); - return false; - } - return true; -} - -bool _markRerecord(struct GBAMGMContext* mgm) { - ++mgm->d.rrCount; - mgm->metadataFile->seek(mgm->metadataFile, mgm->rrCountOffset, SEEK_SET); - mgm->metadataFile->write(mgm->metadataFile, &mgm->d.rrCount, sizeof(mgm->d.rrCount)); - return true; -} - -bool _emitMagic(struct GBAMGMContext* mgm, struct VFile* vf) { - UNUSED(mgm); - return vf->write(vf, BINARY_MAGIC, 4) == 4; -} - -bool _verifyMagic(struct GBAMGMContext* mgm, struct VFile* vf) { - UNUSED(mgm); - char buffer[4]; - if (vf->read(vf, buffer, sizeof(buffer)) != sizeof(buffer)) { - return false; - } - if (memcmp(buffer, BINARY_MAGIC, sizeof(buffer)) != 0) { - return false; - } - return true; -} - -enum GBAMGMTag _readTag(struct GBAMGMContext* mgm, struct VFile* vf) { - if (!mgm || !vf) { - return TAG_EOF; - } - - enum GBAMGMTag tag = mgm->peekedTag; - switch (tag) { - case TAG_INPUT: - vf->read(vf, &mgm->currentInput, sizeof(uint16_t)); - break; - case TAG_PREVIOUSLY: - vf->read(vf, &mgm->previously, sizeof(mgm->previously)); - break; - case TAG_NEXT_TIME: - vf->read(vf, &mgm->nextTime, sizeof(mgm->nextTime)); - break; - case TAG_MAX_STREAM: - vf->read(vf, &mgm->maxStreamId, sizeof(mgm->maxStreamId)); - break; - case TAG_FRAME_COUNT: - vf->read(vf, &mgm->d.frames, sizeof(mgm->d.frames)); - break; - case TAG_LAG_COUNT: - vf->read(vf, &mgm->d.lagFrames, sizeof(mgm->d.lagFrames)); - break; - case TAG_RR_COUNT: - vf->read(vf, &mgm->d.rrCount, sizeof(mgm->d.rrCount)); - break; - - case TAG_INIT_EX_NIHILO: - mgm->d.initFrom = INIT_EX_NIHILO; - break; - case TAG_INIT_FROM_SAVEGAME: - mgm->d.initFrom = INIT_FROM_SAVEGAME; - break; - case TAG_INIT_FROM_SAVESTATE: - mgm->d.initFrom = INIT_FROM_SAVESTATE; - break; - case TAG_INIT_FROM_BOTH: - mgm->d.initFrom = INIT_FROM_BOTH; - break; - - // To be spec'd - case TAG_AUTHOR: - case TAG_COMMENT: - break; - - // Empty markers - case TAG_FRAME: - case TAG_LAG: - case TAG_RESET: - case TAG_BEGIN: - case TAG_END: - case TAG_INVALID: - case TAG_EOF: - break; - } - - uint8_t tagBuffer; - if (vf->read(vf, &tagBuffer, 1) != 1) { - mgm->peekedTag = TAG_EOF; - } else { - mgm->peekedTag = tagBuffer; - } - - if (mgm->peekedTag == TAG_END) { - _skipSegment(mgm); - } - return tag; -} - -bool _seekTag(struct GBAMGMContext* mgm, struct VFile* vf, enum GBAMGMTag tag) { - enum GBAMGMTag readTag; - while ((readTag = _readTag(mgm, vf)) != tag) { - if (readTag == TAG_EOF) { - return false; - } - } - return true; -} - -bool _emitTag(struct GBAMGMContext* mgm, struct VFile* vf, uint8_t tag) { - UNUSED(mgm); - return vf->write(vf, &tag, sizeof(tag)) == sizeof(tag); -} - -bool _parseMetadata(struct GBAMGMContext* mgm, struct VFile* vf) { - if (!_verifyMagic(mgm, vf)) { - return false; - } - while (_readTag(mgm, vf) != TAG_EOF) { - switch (mgm->peekedTag) { - case TAG_MAX_STREAM: - mgm->maxStreamIdOffset = vf->seek(vf, 0, SEEK_CUR); - break; - case TAG_INIT_EX_NIHILO: - case TAG_INIT_FROM_SAVEGAME: - case TAG_INIT_FROM_SAVESTATE: - case TAG_INIT_FROM_BOTH: - mgm->initFromOffset = vf->seek(vf, 0, SEEK_CUR); - break; - case TAG_RR_COUNT: - mgm->rrCountOffset = vf->seek(vf, 0, SEEK_CUR); - break; - default: - break; - } - } - return true; -} - -bool _emitEnd(struct GBAMGMContext* mgm, struct VFile* vf) { - // TODO: Error check - _emitTag(mgm, vf, TAG_END); - _emitTag(mgm, vf, TAG_FRAME_COUNT); - vf->write(vf, &mgm->d.frames, sizeof(mgm->d.frames)); - _emitTag(mgm, vf, TAG_LAG_COUNT); - vf->write(vf, &mgm->d.lagFrames, sizeof(mgm->d.lagFrames)); - _emitTag(mgm, vf, TAG_NEXT_TIME); - - uint32_t newStreamId = 0; - vf->write(vf, &newStreamId, sizeof(newStreamId)); - return true; -} - -bool _markStreamNext(struct GBAMGMContext* mgm, uint32_t newStreamId, bool recursive) { - if (mgm->movieStream->seek(mgm->movieStream, -sizeof(newStreamId) - 1, SEEK_END) < 0) { - return false; - } - - uint8_t tagBuffer; - if (mgm->movieStream->read(mgm->movieStream, &tagBuffer, 1) != 1) { - return false; - } - if (tagBuffer != TAG_NEXT_TIME) { - return false; - } - if (mgm->movieStream->write(mgm->movieStream, &newStreamId, sizeof(newStreamId)) != sizeof(newStreamId)) { - return false; - } - if (recursive) { - if (mgm->movieStream->seek(mgm->movieStream, 0, SEEK_SET) < 0) { - return false; - } - if (!_verifyMagic(mgm, mgm->movieStream)) { - return false; - } - _readTag(mgm, mgm->movieStream); - if (_readTag(mgm, mgm->movieStream) != TAG_PREVIOUSLY) { - return false; - } - if (mgm->previously == 0) { - return true; - } - uint32_t currentStreamId = mgm->streamId; - if (!_loadStream(mgm, mgm->previously)) { - return false; - } - return _markStreamNext(mgm, currentStreamId, mgm->previously); - } - return true; -} - -void _streamEndReached(struct GBAMGMContext* mgm) { - if (!mgm->d.isPlaying(&mgm->d)) { - return; - } - - uint32_t endStreamId = mgm->streamId; - mgm->d.stopPlaying(&mgm->d); - if (mgm->autorecord) { - mgm->isRecording = true; - _loadStream(mgm, endStreamId); - _incrementStream(mgm, false); - } -} - -struct VFile* GBAMGMOpenSavedata(struct GBARRContext* rr, int flags) { - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - return mgm->streamDir->openFile(mgm->streamDir, "movie.sav", flags); -} - -struct VFile* GBAMGMOpenSavestate(struct GBARRContext* rr, int flags) { - struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr; - return mgm->streamDir->openFile(mgm->streamDir, "movie.ssm", flags); -} diff --git a/src/gba/rr/rr.c b/src/gba/rr/rr.c deleted file mode 100644 index 7ed957753..000000000 --- a/src/gba/rr/rr.c +++ /dev/null @@ -1,77 +0,0 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include - -#include -#include -#include - -mLOG_DEFINE_CATEGORY(GBA_RR, "GBA RR", "gba.rr"); - -void GBARRInitRecord(struct GBA* gba) { - if (!gba || !gba->rr) { - return; - } - - if (gba->rr->initFrom & INIT_FROM_SAVEGAME) { - if (gba->rr->savedata) { - gba->rr->savedata->close(gba->rr->savedata); - } - gba->rr->savedata = gba->rr->openSavedata(gba->rr, O_TRUNC | O_CREAT | O_WRONLY); - GBASavedataClone(&gba->memory.savedata, gba->rr->savedata); - gba->rr->savedata->close(gba->rr->savedata); - gba->rr->savedata = gba->rr->openSavedata(gba->rr, O_RDONLY); - GBASavedataMask(&gba->memory.savedata, gba->rr->savedata, false); - } else { - GBASavedataMask(&gba->memory.savedata, 0, false); - } - - if (gba->rr->initFrom & INIT_FROM_SAVESTATE) { - struct VFile* vf = gba->rr->openSavestate(gba->rr, O_TRUNC | O_CREAT | O_RDWR); - //GBASaveStateNamed(gba, vf, SAVESTATE_SAVEDATA); - vf->close(vf); - } else { - ARMReset(gba->cpu); - } -} - -void GBARRInitPlay(struct GBA* gba) { - if (!gba || !gba->rr) { - return; - } - - if (gba->rr->initFrom & INIT_FROM_SAVEGAME) { - if (gba->rr->savedata) { - gba->rr->savedata->close(gba->rr->savedata); - } - gba->rr->savedata = gba->rr->openSavedata(gba->rr, O_RDONLY); - GBASavedataMask(&gba->memory.savedata, gba->rr->savedata, false); - } else { - GBASavedataMask(&gba->memory.savedata, 0, false); - } - - if (gba->rr->initFrom & INIT_FROM_SAVESTATE) { - struct VFile* vf = gba->rr->openSavestate(gba->rr, O_RDONLY); - //GBALoadStateNamed(gba, vf, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA); - vf->close(vf); - } else { - ARMReset(gba->cpu); - } -} - -void GBARRDestroy(struct GBARRContext* rr) { - if (rr->isPlaying(rr)) { - rr->stopPlaying(rr); - } - if (rr->isRecording(rr)) { - rr->stopRecording(rr); - } - if (rr->savedata) { - rr->savedata->close(rr->savedata); - rr->savedata = 0; - } - rr->destroy(rr); -} diff --git a/src/gba/rr/vbm.c b/src/gba/rr/vbm.c deleted file mode 100644 index 2845b74bc..000000000 --- a/src/gba/rr/vbm.c +++ /dev/null @@ -1,274 +0,0 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include - -#include -#include -#include - -#ifdef USE_ZLIB -#include -#endif - -static const char VBM_MAGIC[] = "VBM\x1A"; - -static void GBAVBMContextDestroy(struct GBARRContext*); - -static bool GBAVBMStartPlaying(struct GBARRContext*, bool autorecord); -static void GBAVBMStopPlaying(struct GBARRContext*); -static bool GBAVBMStartRecording(struct GBARRContext*); -static void GBAVBMStopRecording(struct GBARRContext*); - -static bool GBAVBMIsPlaying(const struct GBARRContext*); -static bool GBAVBMIsRecording(const struct GBARRContext*); - -static void GBAVBMNextFrame(struct GBARRContext*); -static uint16_t GBAVBMQueryInput(struct GBARRContext*); -static bool GBAVBMQueryReset(struct GBARRContext*); - -static void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state); -static void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state); - -static struct VFile* GBAVBMOpenSavedata(struct GBARRContext*, int flags); -static struct VFile* GBAVBMOpenSavestate(struct GBARRContext*, int flags); - -void GBAVBMContextCreate(struct GBAVBMContext* vbm) { - memset(vbm, 0, sizeof(*vbm)); - - vbm->d.destroy = GBAVBMContextDestroy; - - vbm->d.startPlaying = GBAVBMStartPlaying; - vbm->d.stopPlaying = GBAVBMStopPlaying; - vbm->d.startRecording = GBAVBMStartRecording; - vbm->d.stopRecording = GBAVBMStopRecording; - - vbm->d.isPlaying = GBAVBMIsPlaying; - vbm->d.isRecording = GBAVBMIsRecording; - - vbm->d.nextFrame = GBAVBMNextFrame; - vbm->d.logInput = 0; - vbm->d.queryInput = GBAVBMQueryInput; - vbm->d.queryReset = GBAVBMQueryReset; - - vbm->d.stateSaved = GBAVBMStateSaved; - vbm->d.stateLoaded = GBAVBMStateLoaded; - - vbm->d.openSavedata = GBAVBMOpenSavedata; - vbm->d.openSavestate = GBAVBMOpenSavestate; -} - -bool GBAVBMStartPlaying(struct GBARRContext* rr, bool autorecord) { - if (rr->isRecording(rr) || rr->isPlaying(rr) || autorecord) { - return false; - } - - struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; - vbm->isPlaying = true; - vbm->vbmFile->seek(vbm->vbmFile, vbm->inputOffset, SEEK_SET); - return true; -} - -void GBAVBMStopPlaying(struct GBARRContext* rr) { - if (!rr->isPlaying(rr)) { - return; - } - - struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; - vbm->isPlaying = false; -} - -bool GBAVBMStartRecording(struct GBARRContext* rr) { - UNUSED(rr); - return false; -} - -void GBAVBMStopRecording(struct GBARRContext* rr) { - UNUSED(rr); -} - -bool GBAVBMIsPlaying(const struct GBARRContext* rr) { - struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; - return vbm->isPlaying; -} - -bool GBAVBMIsRecording(const struct GBARRContext* rr) { - UNUSED(rr); - return false; -} - -void GBAVBMNextFrame(struct GBARRContext* rr) { - if (!rr->isPlaying(rr)) { - return; - } - - struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; - vbm->vbmFile->seek(vbm->vbmFile, sizeof(uint16_t), SEEK_CUR); -} - -uint16_t GBAVBMQueryInput(struct GBARRContext* rr) { - if (!rr->isPlaying(rr)) { - return 0; - } - - struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; - uint16_t input; - vbm->vbmFile->read(vbm->vbmFile, &input, sizeof(input)); - vbm->vbmFile->seek(vbm->vbmFile, -sizeof(input), SEEK_CUR); - return input & 0x3FF; -} - -bool GBAVBMQueryReset(struct GBARRContext* rr) { - if (!rr->isPlaying(rr)) { - return false; - } - - struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; - uint16_t input; - vbm->vbmFile->read(vbm->vbmFile, &input, sizeof(input)); - vbm->vbmFile->seek(vbm->vbmFile, -sizeof(input), SEEK_CUR); - return input & 0x800; -} - -void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state) { - UNUSED(rr); - UNUSED(state); -} - -void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state) { - UNUSED(rr); - UNUSED(state); -} - -struct VFile* GBAVBMOpenSavedata(struct GBARRContext* rr, int flags) { - UNUSED(flags); -#ifndef USE_ZLIB - UNUSED(rr); - return 0; -#else - struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; - off_t pos = vbm->vbmFile->seek(vbm->vbmFile, 0, SEEK_CUR); - uint32_t saveType, flashSize, sramOffset; - vbm->vbmFile->seek(vbm->vbmFile, 0x18, SEEK_SET); - vbm->vbmFile->read(vbm->vbmFile, &saveType, sizeof(saveType)); - vbm->vbmFile->read(vbm->vbmFile, &flashSize, sizeof(flashSize)); - vbm->vbmFile->seek(vbm->vbmFile, 0x38, SEEK_SET); - vbm->vbmFile->read(vbm->vbmFile, &sramOffset, sizeof(sramOffset)); - if (!sramOffset) { - vbm->vbmFile->seek(vbm->vbmFile, pos, SEEK_SET); - return 0; - } - vbm->vbmFile->seek(vbm->vbmFile, sramOffset, SEEK_SET); - struct VFile* save = VFileMemChunk(0, 0); - size_t size; - switch (saveType) { - case 1: - size = SIZE_CART_SRAM; - break; - case 2: - size = flashSize; - if (size > SIZE_CART_FLASH1M) { - size = SIZE_CART_FLASH1M; - } - break; - case 3: - size = SIZE_CART_EEPROM; - break; - default: - size = SIZE_CART_FLASH1M; - break; - } - uLong zlen = vbm->inputOffset - sramOffset; - char buffer[8761]; - char* zbuffer = malloc(zlen); - vbm->vbmFile->read(vbm->vbmFile, zbuffer, zlen); - z_stream zstr; - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; - zstr.avail_in = zlen; - zstr.next_in = (Bytef*) zbuffer; - zstr.avail_out = 0; - inflateInit2(&zstr, 31); - // Skip header, we know where the save file is - zstr.avail_out = sizeof(buffer); - zstr.next_out = (Bytef*) &buffer; - int err = inflate(&zstr, 0); - while (err != Z_STREAM_END && !zstr.avail_out) { - zstr.avail_out = sizeof(buffer); - zstr.next_out = (Bytef*) &buffer; - int err = inflate(&zstr, 0); - if (err < 0) { - break; - } - save->write(save, buffer, sizeof(buffer) - zstr.avail_out); - } - inflateEnd(&zstr); - vbm->vbmFile->seek(vbm->vbmFile, pos, SEEK_SET); - return save; -#endif -} - -struct VFile* GBAVBMOpenSavestate(struct GBARRContext* rr, int flags) { - UNUSED(rr); - UNUSED(flags); - return 0; -} - -void GBAVBMContextDestroy(struct GBARRContext* rr) { - struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; - if (vbm->vbmFile) { - vbm->vbmFile->close(vbm->vbmFile); - } -} - -bool GBAVBMSetStream(struct GBAVBMContext* vbm, struct VFile* vf) { - vf->seek(vf, 0, SEEK_SET); - char magic[4]; - vf->read(vf, magic, sizeof(magic)); - if (memcmp(magic, VBM_MAGIC, sizeof(magic)) != 0) { - return false; - } - - uint32_t id; - vf->read(vf, &id, sizeof(id)); - if (id != 1) { - return false; - } - - vf->seek(vf, 4, SEEK_CUR); - vf->read(vf, &vbm->d.frames, sizeof(vbm->d.frames)); - vf->read(vf, &vbm->d.rrCount, sizeof(vbm->d.rrCount)); - - uint8_t flags; - vf->read(vf, &flags, sizeof(flags)); - if (flags & 2) { -#ifdef USE_ZLIB - vbm->d.initFrom = INIT_FROM_SAVEGAME; -#else - // zlib is needed to parse the savegame - return false; -#endif - } - if (flags & 1) { - // Incompatible savestate format - return false; - } - - vf->seek(vf, 1, SEEK_CUR); - vf->read(vf, &flags, sizeof(flags)); - if ((flags & 0x7) != 1) { - // Non-GBA movie - return false; - } - - // TODO: parse more flags - - vf->seek(vf, 0x3C, SEEK_SET); - vf->read(vf, &vbm->inputOffset, sizeof(vbm->inputOffset)); - vf->seek(vf, vbm->inputOffset, SEEK_SET); - vbm->vbmFile = vf; - return true; -} diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 60b8524ca..bd543482a 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -73,11 +72,6 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { GBAVideoSerialize(&gba->video, state); GBAAudioSerialize(&gba->audio, state); GBASavedataSerialize(&gba->memory.savedata, state); - - state->associatedStreamId = 0; - if (gba->rr) { - gba->rr->stateSaved(gba->rr, state); - } } bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { @@ -195,10 +189,6 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { GBAAudioDeserialize(&gba->audio, state); GBASavedataDeserialize(&gba->memory.savedata, state); - if (gba->rr) { - gba->rr->stateLoaded(gba->rr, state); - } - gba->timing.reroot = gba->timing.root; gba->timing.root = NULL; diff --git a/src/gba/video.c b/src/gba/video.c index b02e4bd0a..f301c4fff 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -31,6 +31,7 @@ static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, si static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels); static void _startHblank(struct mTiming*, void* context, uint32_t cyclesLate); +static void _midHblank(struct mTiming*, void* context, uint32_t cyclesLate); static void _startHdraw(struct mTiming*, void* context, uint32_t cyclesLate); static uint16_t _zeroes[0x2000] = {}; @@ -137,10 +138,17 @@ void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer* video->renderer->init(video->renderer); } -void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) { +void _midHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBAVideo* video = context; GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; dispstat = GBARegisterDISPSTATClearInHblank(dispstat); + video->p->memory.io[REG_DISPSTAT >> 1] = dispstat; + video->event.callback = _startHdraw; + mTimingSchedule(timing, &video->event, VIDEO_HBLANK_FLIP - cyclesLate); +} + +void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) { + struct GBAVideo* video = context; video->event.callback = _startHblank; mTimingSchedule(timing, &video->event, VIDEO_HDRAW_LENGTH - cyclesLate); @@ -150,6 +158,11 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) { } video->p->memory.io[REG_VCOUNT >> 1] = video->vcount; + if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) { + video->renderer->drawScanline(video->renderer, video->vcount); + } + + GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) { dispstat = GBARegisterDISPSTATFillVcounter(dispstat); if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) { @@ -190,16 +203,12 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBAVideo* video = context; - GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; - dispstat = GBARegisterDISPSTATFillInHblank(dispstat); - video->event.callback = _startHdraw; - mTimingSchedule(timing, &video->event, VIDEO_HBLANK_LENGTH - cyclesLate); + video->event.callback = _midHblank; + mTimingSchedule(timing, &video->event, VIDEO_HBLANK_LENGTH - VIDEO_HBLANK_FLIP - cyclesLate); // Begin Hblank + GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; dispstat = GBARegisterDISPSTATFillInHblank(dispstat); - if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) { - video->renderer->drawScanline(video->renderer, video->vcount); - } if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) { GBADMARunHblank(video->p, -cyclesLate); @@ -324,6 +333,15 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* memcpy(state->oam, video->oam.raw, SIZE_OAM); memcpy(state->pram, video->palette, SIZE_PALETTE_RAM); STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent); + int32_t flags = 0; + if (video->event.callback == _startHdraw) { + flags = GBASerializedVideoFlagsSetMode(flags, 1); + } else if (video->event.callback == _startHblank) { + flags = GBASerializedVideoFlagsSetMode(flags, 2); + } else if (video->event.callback == _midHblank) { + flags = GBASerializedVideoFlagsSetMode(flags, 3); + } + STORE_32(flags, 0, &state->video.flags); STORE_32(video->frameCounter, 0, &state->video.frameCounter); } @@ -341,14 +359,28 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState } LOAD_32(video->frameCounter, 0, &state->video.frameCounter); + int32_t flags; + LOAD_32(flags, 0, &state->video.flags); + GBARegisterDISPSTAT dispstat = state->io[REG_DISPSTAT >> 1]; + switch (GBASerializedVideoFlagsGetMode(flags)) { + case 0: + if (GBARegisterDISPSTATIsInHblank(dispstat)) { + video->event.callback = _startHdraw; + } else { + video->event.callback = _startHblank; + } + case 1: + video->event.callback = _startHdraw; + break; + case 2: + video->event.callback = _startHblank; + break; + case 3: + video->event.callback = _midHblank; + break; + } uint32_t when; LOAD_32(when, 0, &state->video.nextEvent); - GBARegisterDISPSTAT dispstat = state->io[REG_DISPSTAT >> 1]; - if (GBARegisterDISPSTATIsInHblank(dispstat)) { - video->event.callback = _startHdraw; - } else { - video->event.callback = _startHblank; - } mTimingSchedule(&video->p->timing, &video->event, when); LOAD_16(video->vcount, REG_VCOUNT, state->io); diff --git a/src/platform/qt/AssetTile.cpp b/src/platform/qt/AssetTile.cpp index 23ff4ff56..cda468757 100644 --- a/src/platform/qt/AssetTile.cpp +++ b/src/platform/qt/AssetTile.cpp @@ -137,9 +137,9 @@ void AssetTile::selectColor(int index) { m_ui.color->setColor(0, color); m_ui.color->update(); - uint32_t r = M_R8(color); - uint32_t g = M_G8(color); - uint32_t b = M_B8(color); + uint32_t r = ((color & 0xF8) * 0x21) >> 5; + uint32_t g = (((color >> 8) & 0xF8) * 0x21) >> 5; + uint32_t b = (((color >> 16) & 0xF8) * 0x21) >> 5; m_ui.r->setText(tr("0x%0 (%1)").arg(r, 2, 16, QChar('0')).arg(r, 2, 10, QChar('0'))); m_ui.g->setText(tr("0x%0 (%1)").arg(g, 2, 16, QChar('0')).arg(g, 2, 10, QChar('0'))); m_ui.b->setText(tr("0x%0 (%1)").arg(b, 2, 16, QChar('0')).arg(b, 2, 10, QChar('0'))); diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 1e7f97299..c875e2366 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -81,9 +81,7 @@ CoreController::CoreController(mCore* core, QObject* parent) controller->m_resetActions.clear(); - if (!controller->m_hwaccel) { - context->core->setVideoBuffer(context->core, reinterpret_cast(controller->m_activeBuffer.data()), controller->screenDimensions().width()); - } + context->core->setVideoBuffer(context->core, reinterpret_cast(controller->m_activeBuffer.data()), controller->screenDimensions().width()); QMetaObject::invokeMethod(controller, "didReset"); controller->finishFrame(); @@ -363,14 +361,12 @@ void CoreController::setLogger(LogController* logger) { } void CoreController::start() { - if (!m_hwaccel) { - QSize size(256, 384); - m_activeBuffer.resize(size.width() * size.height() * sizeof(color_t)); - m_activeBuffer.fill(0xFF); - m_completeBuffer = m_activeBuffer; + QSize size(256, 384); + m_activeBuffer.resize(size.width() * size.height() * sizeof(color_t)); + m_activeBuffer.fill(0xFF); + m_completeBuffer = m_activeBuffer; - m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast(m_activeBuffer.data()), size.width()); - } + m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast(m_activeBuffer.data()), size.width()); if (!m_patched) { mCoreAutoloadPatch(m_threadContext.core); @@ -848,11 +844,23 @@ void CoreController::endVideoLog(bool closeVf) { void CoreController::setFramebufferHandle(int fb) { Interrupter interrupter(this); if (fb < 0) { + if (!m_hwaccel) { + return; + } + mCoreConfigSetIntValue(&m_threadContext.core->config, "hwaccelVideo", 0); + m_threadContext.core->setVideoGLTex(m_threadContext.core, -1); m_hwaccel = false; } else { + mCoreConfigSetIntValue(&m_threadContext.core->config, "hwaccelVideo", 1); m_threadContext.core->setVideoGLTex(m_threadContext.core, fb); + if (m_hwaccel) { + return; + } m_hwaccel = true; } + if (hasStarted()) { + m_threadContext.core->reloadConfigOption(m_threadContext.core, "hwaccelVideo", NULL); + } } void CoreController::updateKeys() { diff --git a/src/platform/qt/CoreManager.cpp b/src/platform/qt/CoreManager.cpp index a77334cc5..6b2580d3c 100644 --- a/src/platform/qt/CoreManager.cpp +++ b/src/platform/qt/CoreManager.cpp @@ -83,6 +83,7 @@ CoreController* CoreManager::loadGame(VFile* vf, const QString& path, const QStr mCore* core = mCoreFindVF(vf); if (!core) { + LOG(QT, ERROR) << tr("Could not load game. Are you sure it's in the correct format?"); return nullptr; } diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 4176defbe..1f129a56a 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -578,10 +578,19 @@ void PainterGL::clearShaders() { return; } #ifdef BUILD_GLES2 + if (!m_started) { + m_gl->makeCurrent(m_surface); +#if defined(_WIN32) && defined(USE_EPOXY) + epoxy_handle_external_wglMakeCurrent(); +#endif + } if (m_shader.passes) { mGLES2ShaderDetach(reinterpret_cast(m_backend)); mGLES2ShaderFree(&m_shader); } + if (!m_started) { + m_gl->doneCurrent(); + } #endif } diff --git a/src/platform/qt/LogController.cpp b/src/platform/qt/LogController.cpp index dfd472c2f..994f484f7 100644 --- a/src/platform/qt/LogController.cpp +++ b/src/platform/qt/LogController.cpp @@ -45,6 +45,12 @@ LogController::Stream LogController::operator()(int category, int level) { void LogController::load(const ConfigController* config) { mLogFilterLoad(&m_filter, config->config()); + if (!levels(mLogCategoryById("gba.bios"))) { + mLogFilterSet(&m_filter, "gba.bios", mLOG_STUB | mLOG_FATAL); + } + if (!levels(mLogCategoryById("core.status"))) { + mLogFilterSet(&m_filter, "core.status", mLOG_ALL & ~mLOG_DEBUG); + } setLogFile(config->getOption("logFile")); logToStdout(config->getOption("logToStdout").toInt()); logToFile(config->getOption("logToFile").toInt()); @@ -114,7 +120,7 @@ void LogController::disableLevels(int levels, int category) { void LogController::clearLevels(int category) { auto id = mLogCategoryId(category); - mLogFilterReset (&m_filter, id); + mLogFilterReset(&m_filter, id); } void LogController::logToFile(bool log) { diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index da93659d5..b1e86c21b 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -487,12 +487,13 @@ void SettingsView::updateConfig() { } int videoScale = m_controller->getOption("videoScale", 1).toInt(); + saveSetting("videoScale", m_ui.videoScale); + int hwaccelVideo = m_controller->getOption("hwaccelVideo").toInt(); + saveSetting("hwaccelVideo", m_ui.hwaccelVideo->currentIndex()); if (hwaccelVideo != m_ui.hwaccelVideo->currentIndex()) { emit videoRendererChanged(); } - saveSetting("videoScale", m_ui.videoScale); - saveSetting("hwaccelVideo", m_ui.hwaccelVideo->currentIndex()); m_logModel.save(m_controller); m_logModel.logger()->setLogFile(m_ui.logFile->text()); diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index 95a025b0a..f7f7f84c7 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -159,7 +159,11 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) { } Action* action = item.value()->action(); if (action) { - action->trigger(); + if (m_actions->isHeld(action->name())) { + action->trigger(true); + } else { + action->trigger(!action->isActive()); + } } event->accept(); return true; @@ -170,7 +174,7 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) { return false; } Action* action = item.value()->action(); - if (action) { + if (action && m_actions->isHeld(action->name())) { action->trigger(false); } event->accept(); @@ -184,7 +188,15 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) { } Action* action = item.value()->action(); if (action) { - action->trigger(gae->isNew()); + if (gae->isNew()) { + if (m_actions->isHeld(action->name())) { + action->trigger(true); + } else { + action->trigger(!action->isActive()); + } + } else if (m_actions->isHeld(action->name())) { + action->trigger(false); + } } event->accept(); return true; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 7473144dd..bcfacbba3 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -510,7 +510,7 @@ void Window::openSettingsWindow() { connect(settingsWindow, &SettingsView::audioDriverChanged, this, &Window::reloadAudioDriver); connect(settingsWindow, &SettingsView::cameraDriverChanged, this, &Window::mustRestart); connect(settingsWindow, &SettingsView::cameraChanged, &m_inputController, &InputController::setCamera); - connect(settingsWindow, &SettingsView::videoRendererChanged, this, &Window::mustRestart); + connect(settingsWindow, &SettingsView::videoRendererChanged, this, &Window::changeRenderer); connect(settingsWindow, &SettingsView::languageChanged, this, &Window::mustRestart); connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig); #ifdef USE_SQLITE3 @@ -926,8 +926,8 @@ void Window::gameCrashed(const QString& errorMessage) { } void Window::gameFailed() { - QMessageBox* fail = new QMessageBox(QMessageBox::Warning, tr("Couldn't Load"), - tr("Could not load game. Are you sure it's in the correct format?"), + QMessageBox* fail = new QMessageBox(QMessageBox::Warning, tr("Couldn't Start"), + tr("Could not start game."), QMessageBox::Ok, this, Qt::Sheet); fail->setAttribute(Qt::WA_DeleteOnClose); fail->show(); @@ -949,10 +949,6 @@ void Window::unimplementedBiosCall(int call) { void Window::reloadDisplayDriver() { if (m_controller) { - if (m_controller->hardwareAccelerated()) { - mustRestart(); - return; - } m_display->stopDrawing(); detachWidget(m_display.get()); } @@ -990,14 +986,7 @@ void Window::reloadDisplayDriver() { #endif if (m_controller) { - 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); - connect(m_controller.get(), &CoreController::paused, m_display.get(), &Display::pauseDrawing); - connect(m_controller.get(), &CoreController::unpaused, m_display.get(), &Display::unpauseDrawing); - connect(m_controller.get(), &CoreController::frameAvailable, m_display.get(), &Display::framePosted); - connect(m_controller.get(), &CoreController::statusPosted, m_display.get(), &Display::showMessage); - connect(m_controller.get(), &CoreController::didReset, m_display.get(), &Display::resizeContext); + attachDisplay(); attachWidget(m_display.get()); m_display->startDrawing(m_controller); @@ -1028,6 +1017,27 @@ void Window::reloadAudioDriver() { connect(m_controller.get(), &CoreController::fastForwardChanged, m_audioProcessor.get(), &AudioProcessor::inputParametersChanged); } +void Window::changeRenderer() { + if (!m_controller) { + return; + } + if (m_config->getOption("hwaccelVideo").toInt() && m_display->supportsShaders() && m_controller->supportsFeature(CoreController::Feature::OPENGL)) { + std::shared_ptr proxy = m_display->videoProxy(); + if (!proxy) { + proxy = std::make_shared(); + } + m_display->setVideoProxy(proxy); + proxy->attach(m_controller.get()); + + int fb = m_display->framebufferHandle(); + if (fb >= 0) { + m_controller->setFramebufferHandle(fb); + } + } else { + m_controller->setFramebufferHandle(-1); + } +} + void Window::tryMakePortable() { QMessageBox* confirm = new QMessageBox(QMessageBox::Question, tr("Really make portable?"), tr("This will make the emulator load its configuration from the same directory as the executable. Do you want to continue?"), @@ -1267,10 +1277,10 @@ void Window::setupMenu(QMenuBar* menubar) { #ifdef M_CORE_GBA m_actions.addSeparator("file"); - Action* importShark = addGameAction(tr("Import GameShark Save"), "importShark", this, &Window::importSharkport, "file"); + Action* importShark = addGameAction(tr("Import GameShark Save..."), "importShark", this, &Window::importSharkport, "file"); m_platformActions.insert(PLATFORM_GBA, importShark); - Action* exportShark = addGameAction(tr("Export GameShark Save"), "exportShark", this, &Window::exportSharkport, "file"); + Action* exportShark = addGameAction(tr("Export GameShark Save..."), "exportShark", this, &Window::exportSharkport, "file"); m_platformActions.insert(PLATFORM_GBA, exportShark); #endif @@ -1828,7 +1838,6 @@ void Window::updateFrame() { void Window::setController(CoreController* controller, const QString& fname) { if (!controller) { - gameFailed(); return; } if (m_pendingClose) { @@ -1851,17 +1860,6 @@ void Window::setController(CoreController* controller, const QString& fname) { reloadDisplayDriver(); } - if (m_config->getOption("hwaccelVideo").toInt() && m_display->supportsShaders() && controller->supportsFeature(CoreController::Feature::OPENGL)) { - std::shared_ptr proxy = std::make_shared(); - m_display->setVideoProxy(proxy); - proxy->attach(controller); - - int fb = m_display->framebufferHandle(); - if (fb >= 0) { - controller->setFramebufferHandle(fb); - } - } - m_controller = std::shared_ptr(controller); m_inputController.recalibrateAxes(); m_controller->setInputController(&m_inputController); @@ -1901,14 +1899,7 @@ void Window::setController(CoreController* controller, const QString& fname) { emit paused(false); }); - 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); - connect(m_controller.get(), &CoreController::paused, m_display.get(), &Display::pauseDrawing); - connect(m_controller.get(), &CoreController::unpaused, m_display.get(), &Display::unpauseDrawing); - connect(m_controller.get(), &CoreController::frameAvailable, m_display.get(), &Display::framePosted); - connect(m_controller.get(), &CoreController::statusPosted, m_display.get(), &Display::showMessage); - connect(m_controller.get(), &CoreController::didReset, m_display.get(), &Display::resizeContext); + attachDisplay(); connect(m_controller.get(), &CoreController::unpaused, &m_inputController, &InputController::suspendScreensaver); connect(m_controller.get(), &CoreController::frameAvailable, this, &Window::recordFrame); @@ -1965,6 +1956,18 @@ void Window::setController(CoreController* controller, const QString& fname) { } } +void Window::attachDisplay() { + 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); + connect(m_controller.get(), &CoreController::paused, m_display.get(), &Display::pauseDrawing); + connect(m_controller.get(), &CoreController::unpaused, m_display.get(), &Display::unpauseDrawing); + connect(m_controller.get(), &CoreController::frameAvailable, m_display.get(), &Display::framePosted); + connect(m_controller.get(), &CoreController::statusPosted, m_display.get(), &Display::showMessage); + connect(m_controller.get(), &CoreController::didReset, m_display.get(), &Display::resizeContext); + changeRenderer(); +} + WindowBackground::WindowBackground(QWidget* parent) : QWidget(parent) { diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 521456e64..16a7d9814 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -130,6 +130,7 @@ private slots: void reloadAudioDriver(); void reloadDisplayDriver(); + void changeRenderer(); void tryMakePortable(); void mustRestart(); @@ -155,6 +156,7 @@ private: void updateMRU(); void openView(QWidget* widget); + void attachDisplay(); template std::function openTView(A... arg); template std::function openControllerTView(A... arg); diff --git a/src/platform/qt/input/InputController.cpp b/src/platform/qt/input/InputController.cpp index 69e03e4ea..0e0312957 100644 --- a/src/platform/qt/input/InputController.cpp +++ b/src/platform/qt/input/InputController.cpp @@ -387,9 +387,21 @@ void InputController::setPreferredGamepad(uint32_t type, int index) { if (!m_config) { return; } +#ifdef BUILD_SDL char name[34] = {0}; +#if SDL_VERSION_ATLEAST(2, 0, 0) SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick), name, sizeof(name)); +#else + const char* name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick)); + if (!name) { + return; + } +#endif mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name); +#else + UNUSED(type); + UNUSED(index); +#endif } mRumble* InputController::rumble() { diff --git a/src/platform/qt/ts/mgba-ru.ts b/src/platform/qt/ts/mgba-ru.ts new file mode 100644 index 000000000..965bbb920 --- /dev/null +++ b/src/platform/qt/ts/mgba-ru.ts @@ -0,0 +1,5383 @@ + + + + + AboutScreen + + + About + + + + + <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> + + + + + Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> + + + + + {projectName} + + + + + {projectName} would like to thank the following patrons from Patreon: + + + + + © 2013 – 2020 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 +Game Boy Advance is a registered trademark of Nintendo Co., Ltd. + + + + + {projectVersion} + + + + + {logo} + + + + + {projectName} is an open-source Game Boy Advance emulator + + + + + {patrons} + + + + + ArchiveInspector + + + Open in archive... + + + + + Loading... + + + + + AssetTile + + + AssetTile + + + + + Tile # + + + + + + 0 + + + + + Palette # + + + + + Address + + + + + 0x06000000 + + + + + Red + + + + + Green + + + + + Blue + + + + + + + 0x00 (00) + + + + + BattleChipView + + + BattleChip Gate + + + + + Chip name + + + + + Insert + + + + + Save + + + + + Load + + + + + Add + + + + + Remove + + + + + Gate type + + + + + Ba&ttleChip Gate + + + + + Progress &Gate + + + + + Beast &Link Gate + + + + + Inserted + + + + + Chip ID + + + + + Update Chip data + + + + + Show advanced + + + + + CheatsView + + + Cheats + + + + + Remove + + + + + Save + + + + + Load + + + + + Add New Set + + + + + Add + + + + + Enter codes here... + + + + + DebuggerConsole + + + Debugger + + + + + Enter command (try `help` for more info) + + + + + Break + + + + + FrameView + + + Inspect frame + + + + + × + + + + + Magnification + + + + + Freeze frame + + + + + Backdrop color + + + + + Disable scanline effects + + + + + Export + + + + + Reset + + + + + GIFView + + + Record GIF + + + + + Frameskip + + + + + Start + + + + + Stop + + + + + Select File + + + + + IOViewer + + + I/O Viewer + + + + + 0x0000 + + + + + 2 + + + + + 5 + + + + + 4 + + + + + 7 + + + + + 0 + + + + + 9 + + + + + 1 + + + + + 3 + + + + + 8 + + + + + C + + + + + E + + + + + 6 + + + + + D + + + + + F + + + + + A + + + + + B + + + + + LibraryTree + + + Name + + + + + Location + + + + + Platform + + + + + Size + + + + + CRC32 + + + + + LoadSaveState + + + + %1 State + + + + + + + + + + + + + No Save + + + + + 5 + + + + + 6 + + + + + 8 + + + + + 4 + + + + + 1 + + + + + 3 + + + + + 7 + + + + + 9 + + + + + 2 + + + + + Cancel + + + + + LogView + + + Logs + + + + + Enabled Levels + + + + + Debug + + + + + Stub + + + + + Info + + + + + Warning + + + + + Error + + + + + Fatal + + + + + Game Error + + + + + Clear + + + + + Max Lines + + + + + MapView + + + Maps + + + + + × + + + + + Magnification + + + + + Export + + + + + Copy + + + + + MemoryDump + + + Save Memory Range + + + + + Start Address: + + + + + : + + + + + + 0x + + + + + Byte Count: + + + + + Dump across banks + + + + + MemorySearch + + + Memory Search + + + + + Address + + + + + Current Value + + + + + + Type + + + + + Value + + + + + Numeric + + + + + Text + + + + + Width + + + + + + Guess + + + + + 1 Byte (8-bit) + + + + + 2 Bytes (16-bit) + + + + + 4 Bytes (32-bit) + + + + + Number type + + + + + Decimal + + + + + Hexadecimal + + + + + Search type + + + + + Equal to value + + + + + Greater than value + + + + + Less than value + + + + + Unknown/changed + + + + + Changed by value + + + + + Unchanged + + + + + Increased + + + + + Decreased + + + + + Search ROM + + + + + New Search + + + + + Search Within + + + + + Open in Memory Viewer + + + + + Refresh + + + + + MemoryView + + + Memory + + + + + Inspect Address: + + + + + : + + + + + 0x + + + + + Set Alignment: + + + + + &1 Byte + + + + + &2 Bytes + + + + + &4 Bytes + + + + + Unsigned Integer: + + + + + Signed Integer: + + + + + String: + + + + + Load TBL + + + + + Copy Selection + + + + + Paste + + + + + Save Selection + + + + + Save Range + + + + + Load + + + + + ObjView + + + Sprites + + + + + Copy + + + + + Geometry + + + + + Position + + + + + + + + 0 + + + + + , + + + + + Dimensions + + + + + + 8 + + + + + + × + + + + + Tile + + + + + Export + + + + + Attributes + + + + + Transform + + + + + Off + + + + + Palette + + + + + Double Size + + + + + + + + Return, Ctrl+R + + + + + Flipped + + + + + H + + + + + V + + + + + Mode + + + + + Normal + + + + + Mosaic + + + + + Enabled + + + + + Priority + + + + + Address + + + + + 0x07000000 + + + + + Magnification + + + + + OverrideView + + + Game Overrides + + + + + Game Boy Advance + + + + + + + + Autodetect + + + + + Realtime clock + + + + + Gyroscope + + + + + Tilt + + + + + Light sensor + + + + + Rumble + + + + + Save type + + + + + + None + + + + + SRAM + + + + + Flash 512kb + + + + + Flash 1Mb + + + + + EEPROM + + + + + Idle loop + + + + + Game Boy Player features + + + + + Game Boy + + + + + Game Boy model + + + + + Game Boy (DMG) + + + + + Super Game Boy (SGB) + + + + + Game Boy Color (CGB) + + + + + Game Boy Advance (AGB) + + + + + Memory bank controller + + + + + MBC1 + + + + + MBC2 + + + + + MBC3 + + + + + MBC3 + RTC + + + + + MBC5 + + + + + MBC5 + Rumble + + + + + MBC6 + + + + + MBC7 + + + + + MMM01 + + + + + Pocket Cam + + + + + TAMA5 + + + + + HuC-1 + + + + + HuC-3 + + + + + Background Colors + + + + + Sprite Colors 1 + + + + + Sprite Colors 2 + + + + + PaletteView + + + Palette + + + + + Background + + + + + Objects + + + + + Selection + + + + + Red + + + + + Green + + + + + Blue + + + + + + + 0x00 (00) + + + + + 16-bit value + + + + + Hex code + + + + + Palette index + + + + + 0x0000 + + + + + #000000 + + + + + 000 + + + + + Export BG + + + + + Export OBJ + + + + + PlacementControl + + + Adjust placement + + + + + All + + + + + Offset + + + + + X + + + + + Y + + + + + PrinterView + + + Game Boy Printer + + + + + Hurry up! + + + + + Tear off + + + + + × + + + + + Magnification + + + + + QGBA::AssetTile + + + %0%1%2 + + + + + + + 0x%0 (%1) + + + + + QGBA::AudioDevice + + + Can't set format of context-less audio device + + + + + Audio device is missing its core + + + + + Writing data to read-only audio device + + + + + QGBA::AudioProcessorQt + + + Can't start an audio processor without input + + + + + QGBA::AudioProcessorSDL + + + Can't start an audio processor without input + + + + + QGBA::BattleChipView + + + BattleChip data missing + + + + + BattleChip data is missing. BattleChip Gates will still work, but some graphics will be missing. Would you like to download the data now? + + + + + + Select deck file + + + + + Incompatible deck + + + + + The selected deck is not compatible with this Chip Gate + + + + + QGBA::CheatsModel + + + (untitled) + + + + + Failed to open cheats file: %1 + + + + + QGBA::CheatsView + + + + Add GameShark + + + + + Add Pro Action Replay + + + + + Add CodeBreaker + + + + + Add GameGenie + + + + + + Select cheats file + + + + + QGBA::CoreController + + + Failed to open save file: %1 + + + + + Failed to open game file: %1 + + + + + Failed to open snapshot file for reading: %1 + + + + + Failed to open snapshot file for writing: %1 + + + + + QGBA::CoreManager + + + Failed to open game file: %1 + + + + + QGBA::FrameView + + + Export frame + + + + + Portable Network Graphics (*.png) + + + + + None + + + + + Background + + + + + Window + + + + + Sprite + + + + + Backdrop + + + + + %1 %2 + + + + + QGBA::GBAApp + + + Enable Discord Rich Presence + + + + + QGBA::GBAKeyEditor + + + Clear Button + + + + + Clear Analog + + + + + Refresh + + + + + Set all + + + + + QGBA::GDBWindow + + + Server settings + + + + + Local port + + + + + Bind address + + + + + Break + + + + + Stop + + + + + Start + + + + + Crash + + + + + Could not start GDB server + + + + + QGBA::GIFView + + + Failed to open output GIF file: %1 + + + + + Select output file + + + + + Graphics Interchange Format (*.gif) + + + + + QGBA::IOViewer + + + Background mode + + + + + Mode 0: 4 tile layers + + + + + Mode 1: 2 tile layers + 1 rotated/scaled tile layer + + + + + Mode 2: 2 rotated/scaled tile layers + + + + + Mode 3: Full 15-bit bitmap + + + + + Mode 4: Full 8-bit bitmap + + + + + Mode 5: Small 15-bit bitmap + + + + + CGB Mode + + + + + Frame select + + + + + Unlocked HBlank + + + + + Linear OBJ tile mapping + + + + + Force blank screen + + + + + Enable background 0 + + + + + Enable background 1 + + + + + Enable background 2 + + + + + Enable background 3 + + + + + Enable OBJ + + + + + Enable Window 0 + + + + + Enable Window 1 + + + + + Enable OBJ Window + + + + + Currently in VBlank + + + + + Currently in HBlank + + + + + Currently in VCounter + + + + + Enable VBlank IRQ generation + + + + + Enable HBlank IRQ generation + + + + + Enable VCounter IRQ generation + + + + + VCounter scanline + + + + + Current scanline + + + + + + + + Priority + + + + + + + + Tile data base (* 16kB) + + + + + + + + Enable mosaic + + + + + + + + Enable 256-color + + + + + + + + Tile map base (* 2kB) + + + + + + + + Background dimensions + + + + + + Overflow wraps + + + + + + + + Horizontal offset + + + + + + + + Vertical offset + + + + + + + + + + + + + + + + Fractional part + + + + + + + + + + + + Integer part + + + + + + + + Integer part (bottom) + + + + + + + + Integer part (top) + + + + + + End x + + + + + + Start x + + + + + + End y + + + + + + Start y + + + + + Window 0 enable BG 0 + + + + + Window 0 enable BG 1 + + + + + Window 0 enable BG 2 + + + + + Window 0 enable BG 3 + + + + + Window 0 enable OBJ + + + + + Window 0 enable blend + + + + + Window 1 enable BG 0 + + + + + Window 1 enable BG 1 + + + + + Window 1 enable BG 2 + + + + + Window 1 enable BG 3 + + + + + Window 1 enable OBJ + + + + + Window 1 enable blend + + + + + Outside window enable BG 0 + + + + + Outside window enable BG 1 + + + + + Outside window enable BG 2 + + + + + Outside window enable BG 3 + + + + + Outside window enable OBJ + + + + + Outside window enable blend + + + + + OBJ window enable BG 0 + + + + + OBJ window enable BG 1 + + + + + OBJ window enable BG 2 + + + + + OBJ window enable BG 3 + + + + + OBJ window enable OBJ + + + + + OBJ window enable blend + + + + + Background mosaic size vertical + + + + + Background mosaic size horizontal + + + + + Object mosaic size vertical + + + + + Object mosaic size horizontal + + + + + BG 0 target 1 + + + + + BG 1 target 1 + + + + + BG 2 target 1 + + + + + BG 3 target 1 + + + + + OBJ target 1 + + + + + Backdrop target 1 + + + + + Blend mode + + + + + Disabled + + + + + Additive blending + + + + + Brighten + + + + + Darken + + + + + BG 0 target 2 + + + + + BG 1 target 2 + + + + + BG 2 target 2 + + + + + BG 3 target 2 + + + + + OBJ target 2 + + + + + Backdrop target 2 + + + + + Blend A (target 1) + + + + + Blend B (target 2) + + + + + Blend Y + + + + + Sweep shifts + + + + + Sweep subtract + + + + + Sweep time (in 1/128s) + + + + + + + + Sound length + + + + + + Duty cycle + + + + + + + Envelope step time + + + + + + + Envelope increase + + + + + + + Initial volume + + + + + + + Sound frequency + + + + + + + + Timed + + + + + + + + Reset + + + + + Double-size wave table + + + + + Active wave table + + + + + Enable channel 3 + + + + + Volume + + + + + 0% + + + + + + 100% + + + + + + 50% + + + + + + 25% + + + + + + + + 75% + + + + + Clock divider + + + + + Register stages + + + + + 15 + + + + + 7 + + + + + Shifter frequency + + + + + PSG volume right + + + + + PSG volume left + + + + + Enable channel 1 right + + + + + Enable channel 2 right + + + + + Enable channel 3 right + + + + + Enable channel 4 right + + + + + Enable channel 1 left + + + + + Enable channel 2 left + + + + + Enable channel 3 left + + + + + Enable channel 4 left + + + + + PSG master volume + + + + + Loud channel A + + + + + Loud channel B + + + + + Enable channel A right + + + + + Enable channel A left + + + + + Channel A timer + + + + + + 0 + + + + + + + + + + + + + 1 + + + + + Channel A reset + + + + + Enable channel B right + + + + + Enable channel B left + + + + + Channel B timer + + + + + Channel B reset + + + + + Active channel 1 + + + + + Active channel 2 + + + + + Active channel 3 + + + + + Active channel 4 + + + + + Enable audio + + + + + Bias + + + + + Resolution + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sample + + + + + + + + + + + + Address (bottom) + + + + + + + + + + + + Address (top) + + + + + + + + Word count + + + + + + + + Destination offset + + + + + + + + + + + + Increment + + + + + + + + + + + + Decrement + + + + + + + + + + + + Fixed + + + + + + + + Increment and reload + + + + + + + + Source offset + + + + + + + + Repeat + + + + + + + + 32-bit + + + + + + + + Start timing + + + + + + + + Immediate + + + + + + + + + + VBlank + + + + + + + + + + HBlank + + + + + + + + + + + + + IRQ + + + + + + + + + + + + Enable + + + + + + + Audio FIFO + + + + + Video Capture + + + + + DRQ + + + + + + + + Value + + + + + + + + Scale + + + + + + + + 1/64 + + + + + + + + 1/256 + + + + + + + + 1/1024 + + + + + + + Cascade + + + + + + A + + + + + + B + + + + + + Select + + + + + + Start + + + + + + Right + + + + + + Left + + + + + + Up + + + + + + Down + + + + + + R + + + + + + L + + + + + Condition + + + + + SC + + + + + SD + + + + + SI + + + + + SO + + + + + + VCounter + + + + + + Timer 0 + + + + + + Timer 1 + + + + + + Timer 2 + + + + + + Timer 3 + + + + + + SIO + + + + + + DMA 0 + + + + + + DMA 1 + + + + + + DMA 2 + + + + + + DMA 3 + + + + + + Keypad + + + + + + Gamepak + + + + + SRAM wait + + + + + + + + + 4 + + + + + + + + 3 + + + + + + + + + 2 + + + + + + + + + 8 + + + + + Cart 0 non-sequential + + + + + Cart 0 sequential + + + + + Cart 1 non-sequential + + + + + Cart 1 sequential + + + + + Cart 2 non-sequential + + + + + Cart 2 sequential + + + + + PHI terminal + + + + + Disable + + + + + 4.19MHz + + + + + 8.38MHz + + + + + 16.78MHz + + + + + Gamepak prefetch + + + + + Enable IRQs + + + + + QGBA::KeyEditor + + + + --- + + + + + QGBA::LoadSaveState + + + Load State + + + + + Save State + + + + + Empty + + + + + Corrupted + + + + + Slot %1 + + + + + QGBA::LogConfigModel + + + + Default + + + + + Fatal + + + + + Error + + + + + Warning + + + + + Info + + + + + Debug + + + + + Stub + + + + + Game Error + + + + + QGBA::LogController + + + [%1] %2: %3 + + + + + An error occurred + + + + + DEBUG + + + + + STUB + + + + + INFO + + + + + WARN + + + + + ERROR + + + + + FATAL + + + + + GAME ERROR + + + + + QGBA::MapView + + + Priority + + + + + + Map base + + + + + + Tile base + + + + + Size + + + + + + Offset + + + + + Xform + + + + + Map Addr. + + + + + Mirror + + + + + None + + + + + Both + + + + + Horizontal + + + + + Vertical + + + + + + + N/A + + + + + Export map + + + + + Portable Network Graphics (*.png) + + + + + QGBA::MemoryDump + + + Save memory region + + + + + Failed to open output file: %1 + + + + + QGBA::MemoryModel + + + Copy selection + + + + + Save selection + + + + + Paste + + + + + Load + + + + + All + + + + + Load TBL + + + + + Save selected memory + + + + + Failed to open output file: %1 + + + + + Load memory + + + + + Failed to open input file: %1 + + + + + TBL + + + + + ISO-8859-1 + + + + + QGBA::MemorySearch + + + (%0/%1×) + + + + + (⅟%0×) + + + + + (%0×) + + + + + %1 byte%2 + + + + + QGBA::ObjView + + + + 0x%0 + + + + + Off + + + + + Normal + + + + + Trans + + + + + OBJWIN + + + + + Invalid + + + + + + N/A + + + + + Export sprite + + + + + Portable Network Graphics (*.png) + + + + + QGBA::PaletteView + + + #%0 + + + + + 0x%0 + + + + + %0 + + + + + + + 0x%0 (%1) + + + + + Export palette + + + + + Windows PAL (*.pal);;Adobe Color Table (*.act) + + + + + Failed to open output palette file: %1 + + + + + QGBA::PrinterView + + + Save Printout + + + + + Portable Network Graphics (*.png) + + + + + QGBA::ROMInfo + + + + + + + (unknown) + + + + + + bytes + + + + + (no database present) + + + + + QGBA::SettingsView + + + + Qt Multimedia + + + + + SDL + + + + + Software (Qt) + + + + + OpenGL + + + + + OpenGL (force version 1.x) + + + + + None (Still Image) + + + + + Keyboard + + + + + Controllers + + + + + Shortcuts + + + + + + Shaders + + + + + Select BIOS + + + + + (%1×%2) + + + + + QGBA::ShaderSelector + + + No shader active + + + + + Load shader + + + + + No shader loaded + + + + + by %1 + + + + + Preprocessing + + + + + Pass %1 + + + + + QGBA::ShortcutModel + + + Action + + + + + Keyboard + + + + + Gamepad + + + + + QGBA::TileView + + + Export tiles + + + + + + Portable Network Graphics (*.png) + + + + + Export tile + + + + + QGBA::VideoView + + + Failed to open output video file: %1 + + + + + Native (%0x%1) + + + + + Select output file + + + + + QGBA::Window + + + Game Boy Advance ROMs (%1) + + + + + Game Boy ROMs (%1) + + + + + All ROMs (%1) + + + + + %1 Video Logs (*.mvl) + + + + + Archives (%1) + + + + + + + Select ROM + + + + + Select folder + + + + + Game Boy Advance save files (%1) + + + + + + + Select save + + + + + mGBA savestate files (%1) + + + + + + Select savestate + + + + + Select patch + + + + + Patches (*.ips *.ups *.bps) + + + + + Select image + + + + + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) + + + + + + GameShark saves (*.sps *.xps) + + + + + Select video log + + + + + Video logs (*.mvl) + + + + + Crash + + + + + The game has crashed with the following error: + +%1 + + + + + Couldn't Load + + + + + Could not load game. Are you sure it's in the correct format? + + + + + Unimplemented BIOS call + + + + + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. + + + + + Really make portable? + + + + + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? + + + + + Restart needed + + + + + Some changes will not take effect until the emulator is restarted. + + + + + - Player %1 of %2 + + + + + %1 - %2 + + + + + %1 - %2 - %3 + + + + + %1 - %2 (%3 fps) - %4 + + + + + &File + + + + + Load &ROM... + + + + + Load ROM in archive... + + + + + Add folder to library... + + + + + Load alternate save... + + + + + Load temporary save... + + + + + Load &patch... + + + + + Boot BIOS + + + + + Replace ROM... + + + + + ROM &info... + + + + + Recent + + + + + Make portable + + + + + &Load state + + + + + Load state file... + + + + + &Save state + + + + + Save state file... + + + + + Quick load + + + + + Quick save + + + + + Load recent + + + + + Save recent + + + + + Undo load state + + + + + Undo save state + + + + + + State &%1 + + + + + Load camera image... + + + + + Import GameShark Save... + + + + + Export GameShark Save... + + + + + New multiplayer window + + + + + About... + + + + + E&xit + + + + + &Emulation + + + + + &Reset + + + + + Sh&utdown + + + + + Yank game pak + + + + + &Pause + + + + + &Next frame + + + + + Fast forward (held) + + + + + &Fast forward + + + + + Fast forward speed + + + + + Unbounded + + + + + %0x + + + + + Rewind (held) + + + + + Re&wind + + + + + Step backwards + + + + + Sync to &video + + + + + Sync to &audio + + + + + Solar sensor + + + + + Increase solar level + + + + + Decrease solar level + + + + + Brightest solar level + + + + + Darkest solar level + + + + + Brightness %1 + + + + + Game Boy Printer... + + + + + BattleChip Gate... + + + + + Audio/&Video + + + + + Frame size + + + + + %1× + + + + + Toggle fullscreen + + + + + Lock aspect ratio + + + + + Force integer scaling + + + + + Interframe blending + + + + + Bilinear filtering + + + + + Frame&skip + + + + + Mute + + + + + FPS target + + + + + Native (59.7275) + + + + + Take &screenshot + + + + + F12 + + + + + Record A/V... + + + + + Record GIF... + + + + + Video layers + + + + + Audio channels + + + + + Adjust layer placement... + + + + + &Tools + + + + + View &logs... + + + + + Game &overrides... + + + + + Game Pak sensors... + + + + + &Cheats... + + + + + Settings... + + + + + Open debugger console... + + + + + Start &GDB server... + + + + + View &palette... + + + + + View &sprites... + + + + + View &tiles... + + + + + View &map... + + + + + &Frame inspector... + + + + + View memory... + + + + + Search memory... + + + + + View &I/O registers... + + + + + Record debug video log... + + + + + Stop debug video log + + + + + Exit fullscreen + + + + + GameShark Button (held) + + + + + Autofire + + + + + Autofire A + + + + + Autofire B + + + + + Autofire L + + + + + Autofire R + + + + + Autofire Start + + + + + Autofire Select + + + + + Autofire Up + + + + + Autofire Right + + + + + Autofire Down + + + + + Autofire Left + + + + + Clear + + + + + QObject + + + GBA + + + + + GB + + + + + ? + + + + + ROMInfo + + + ROM Info + + + + + Game name: + + + + + {NAME} + + + + + Internal name: + + + + + {TITLE} + + + + + Game ID: + + + + + {ID} + + + + + File size: + + + + + {SIZE} + + + + + CRC32: + + + + + {CRC} + + + + + SensorView + + + Sensors + + + + + Realtime clock + + + + + Fixed time + + + + + System time + + + + + Start time at + + + + + Now + + + + + MM/dd/yy hh:mm:ss AP + + + + + Light sensor + + + + + Brightness + + + + + Tilt sensor + + + + + + Set Y + + + + + + Set X + + + + + Gyroscope + + + + + Sensitivity + + + + + SettingsView + + + Settings + + + + + Audio/Video + + + + + Interface + + + + + Emulation + + + + + Enhancements + + + + + BIOS + + + + + Paths + + + + + Logging + + + + + Game Boy + + + + + Audio driver: + + + + + Audio buffer: + + + + + + 1536 + + + + + 512 + + + + + 768 + + + + + 1024 + + + + + 2048 + + + + + 3072 + + + + + 4096 + + + + + samples + + + + + Sample rate: + + + + + + 44100 + + + + + 22050 + + + + + 32000 + + + + + 48000 + + + + + Hz + + + + + Volume: + + + + + + Mute + + + + + Fast forward volume: + + + + + Display driver: + + + + + Frameskip: + + + + + Skip every + + + + + + frames + + + + + FPS target: + + + + + frames per second + + + + + Sync: + + + + + Video + + + + + Audio + + + + + Lock aspect ratio + + + + + Force integer scaling + + + + + Bilinear filtering + + + + + Native (59.7275) + + + + + Interframe blending + + + + + Language + + + + + English + + + + + Library: + + + + + List view + + + + + Tree view + + + + + Show when no game open + + + + + Clear cache + + + + + Allow opposing input directions + + + + + Suspend screensaver + + + + + Pause when inactive + + + + + Pause when minimized + + + + + Show FPS in title bar + + + + + Enable Discord Rich Presence + + + + + Automatically save state + + + + + Automatically load state + + + + + Automatically save cheats + + + + + Automatically load cheats + + + + + Show OSD messages + + + + + Fast forward speed: + + + + + + + × + + + + + + Unbounded + + + + + Fast forward (held) speed: + + + + + Autofire interval: + + + + + Enable rewind + + + + + Rewind history: + + + + + Idle loops: + + + + + Run all + + + + + Remove known + + + + + Detect and remove + + + + + Preload entire ROM into memory + + + + + Savestate extra data: + + + + + + Screenshot + + + + + + Save data + + + + + + Cheat codes + + + + + Load extra data: + + + + + Video renderer: + + + + + Software + + + + + OpenGL + + + + + OpenGL enhancements + + + + + High-resolution scale: + + + + + (240×160) + + + + + XQ GBA audio (experimental) + + + + + GB BIOS file: + + + + + + + + + + + + + Browse + + + + + Use BIOS file if found + + + + + Skip BIOS intro + + + + + GBA BIOS file: + + + + + GBC BIOS file: + + + + + SGB BIOS file: + + + + + Save games + + + + + + + + + Same directory as the ROM + + + + + Save states + + + + + Screenshots + + + + + Patches + + + + + Cheats + + + + + Log to file + + + + + Log to console + + + + + Select Log File + + + + + Game Boy model: + + + + + + + Autodetect + + + + + + + Game Boy (DMG) + + + + + + + Super Game Boy (SGB) + + + + + + + Game Boy Color (CGB) + + + + + + + Game Boy Advance (AGB) + + + + + Super Game Boy model: + + + + + Game Boy Color model: + + + + + Default BG colors: + + + + + Super Game Boy borders + + + + + Camera driver: + + + + + Default sprite colors 1: + + + + + Default sprite colors 2: + + + + + Use GBC colors in GB games + + + + + Camera: + + + + + ShaderSelector + + + Shaders + + + + + Active Shader: + + + + + Name + + + + + Author + + + + + Description + + + + + Unload Shader + + + + + Load New Shader + + + + + ShortcutView + + + Edit Shortcuts + + + + + Keyboard + + + + + Gamepad + + + + + Clear + + + + + TileView + + + Tiles + + + + + Export Selected + + + + + Export All + + + + + 256 colors + + + + + × + + + + + Magnification + + + + + Tiles per row + + + + + Fit to window + + + + + Copy Selected + + + + + Copy All + + + + + VideoView + + + Record Video + + + + + Start + + + + + Stop + + + + + Select File + + + + + Presets + + + + + High &Quality + + + + + &YouTube + + + + + + WebM + + + + + &Lossless + + + + + &1080p + + + + + &720p + + + + + &480p + + + + + &Native + + + + + Format + + + + + MKV + + + + + AVI + + + + + MP4 + + + + + h.264 + + + + + h.264 (NVENC) + + + + + HEVC + + + + + HEVC (NVENC) + + + + + VP8 + + + + + VP9 + + + + + FFV1 + + + + + FLAC + + + + + Opus + + + + + Vorbis + + + + + MP3 + + + + + AAC + + + + + Uncompressed + + + + + Bitrate (kbps) + + + + + VBR + + + + + ABR + + + + + Dimensions + + + + + : + + + + + × + + + + + Lock aspect ratio + + + + + Show advanced + + + + diff --git a/src/platform/switch/main.c b/src/platform/switch/main.c index 45560cab4..e27450fd1 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -320,6 +320,11 @@ static void _gameLoaded(struct mGUIRunner* runner) { } } + int scale; + if (mCoreConfigGetUIntValue(&runner->config, "videoScale", &scale)) { + runner->core->reloadConfigOption(runner->core, "videoScale", &runner->config); + } + rumble.up = 0; rumble.down = 0; } @@ -857,7 +862,7 @@ int main(int argc, char* argv[]) { .nStates = 16 }, { - .title = "GPU-accelerated renderer (experimental, requires game reload)", + .title = "GPU-accelerated renderer (requires game reload)", .data = "hwaccelVideo", .submenu = 0, .state = 0, diff --git a/src/util/elf-read.c b/src/util/elf-read.c index 8ac38774d..520c01ca3 100644 --- a/src/util/elf-read.c +++ b/src/util/elf-read.c @@ -84,6 +84,9 @@ void ELFGetProgramHeaders(struct ELF* elf, struct ELFProgramHeaders* ph) { ELFProgramHeadersClear(ph); Elf32_Ehdr* hdr = elf32_getehdr(elf->e); Elf32_Phdr* phdr = elf32_getphdr(elf->e); + if (!hdr || !phdr) { + return; + } ELFProgramHeadersResize(ph, hdr->e_phnum); memcpy(ELFProgramHeadersGetPointer(ph, 0), phdr, sizeof(*phdr) * hdr->e_phnum); }