all repos — mgba @ 5665ac0316df4800bec0e942e4d18ba3fec59310

mGBA Game Boy Advance Emulator

GBA Core: Video log playing
Vicki Pfau vi@endrift.com
Sun, 16 Apr 2017 21:12:27 -0700
commit

5665ac0316df4800bec0e942e4d18ba3fec59310

parent

73947766defe48d122796c602b553af507cdb6d5

M CMakeLists.txtCMakeLists.txt

@@ -57,7 +57,7 @@ file(GLOB GBA_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/sio/lockstep.c)

file(GLOB GB_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/sio/lockstep.c) file(GLOB GB_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/renderers/*.c) file(GLOB THIRD_PARTY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/inih/*.c) -set(CLI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/commandline.c) +file(GLOB EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/*.c) set(CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-mem.c) set(VFS_SRC) source_group("ARM core" FILES ${ARM_SRC})

@@ -674,7 +674,7 @@ ${GB_SIO_SRC})

endif() list(APPEND SRC ${FEATURE_SRC} - ${CLI_SRC}) + ${EXTRA_SRC}) endif() if(NOT SKIP_LIBRARY)

@@ -771,7 +771,7 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/qt ${CMAKE_CURRENT_BINARY_DIR}/qt)

endif() if(BUILD_PERF) - set(PERF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/perf-main.c ${CLI_SRC}) + set(PERF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/perf-main.c) if(UNIX AND NOT APPLE) list(APPEND PERF_LIB rt) endif()
M include/mgba/core/video-logger.hsrc/feature/video-logger.h

@@ -10,6 +10,8 @@ #include <mgba-util/common.h>

CXX_GUARD_START +#define mVL_MAX_CHANNELS 32 + enum mVideoLoggerDirtyType { DIRTY_DUMMY = 0, DIRTY_FLUSH,

@@ -61,25 +63,28 @@ struct mVideoLogContext {

void* initialState; size_t initialStateSize; uint32_t nChannels; - struct mVideoLogChannel channels[32]; + struct mVideoLogChannel channels[mVL_MAX_CHANNELS]; }; struct mVideoLogHeader { char magic[4]; + uint32_t reserved; uint32_t platform; - uint32_t nChannels; + uint32_t padding; uint32_t initialStatePointer; - uint32_t channelPointers[32]; + uint32_t initialStateSize; + uint32_t nChannels; + uint32_t channelPointers[mVL_MAX_CHANNELS]; }; struct mVideoLogChannelHeader { uint32_t type; uint32_t channelInitialStatePointer; + uint32_t channelInitialStateSize; uint32_t channelSize; - uint32_t reserved; }; -void mVideoLoggerRendererCreate(struct mVideoLogger* logger); +void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly); void mVideoLoggerRendererInit(struct mVideoLogger* logger); void mVideoLoggerRendererDeinit(struct mVideoLogger* logger); void mVideoLoggerRendererReset(struct mVideoLogger* logger);

@@ -98,6 +103,9 @@ struct mCore;

struct mVideoLogContext* mVideoLoggerCreate(struct mCore* core); void mVideoLoggerDestroy(struct mCore* core, struct mVideoLogContext*); void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext*, struct VFile*); + +struct mCore* mVideoLogCoreFind(struct VFile*); +bool mVideoLogContextLoad(struct VFile*, struct mVideoLogContext*); CXX_GUARD_END
M include/mgba/gba/core.hinclude/mgba/gba/core.h

@@ -12,6 +12,7 @@ CXX_GUARD_START

struct mCore; struct mCore* GBACoreCreate(void); +struct mCore* GBAVideoLogPlayerCreate(void); CXX_GUARD_END
M include/mgba/internal/gba/renderers/proxy.hinclude/mgba/internal/gba/renderers/proxy.h

@@ -11,7 +11,7 @@

CXX_GUARD_START #include <mgba/internal/gba/video.h> -#include <mgba/core/video-logger.h> +#include "feature/video-logger.h" struct GBAVideoProxyRenderer { struct GBAVideoRenderer d;

@@ -30,7 +30,7 @@ void (*wait)(struct GBAVideoProxyRenderer*);

void (*wake)(struct GBAVideoProxyRenderer*, int y); }; -void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend); +void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend, bool readonly); void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer); void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer);
M src/core/core.csrc/core/core.c

@@ -18,8 +18,11 @@ #ifdef M_CORE_GBA

#include <mgba/gba/core.h> #include <mgba/internal/gba/gba.h> #endif +#ifndef MINIMAL_CORE +#include "feature/video-logger.h" +#endif -static struct mCoreFilter { +const static struct mCoreFilter { bool (*filter)(struct VFile*); struct mCore* (*open)(void); enum mPlatform platform;

@@ -37,7 +40,7 @@ struct mCore* mCoreFindVF(struct VFile* vf) {

if (!vf) { return NULL; } - struct mCoreFilter* filter; + const struct mCoreFilter* filter; for (filter = &_filters[0]; filter->filter; ++filter) { if (filter->filter(vf)) { break;

@@ -46,6 +49,9 @@ }

if (filter->open) { return filter->open(); } +#ifndef MINIMAL_CORE + return mVideoLogCoreFind(vf); +#endif return NULL; }

@@ -53,7 +59,7 @@ enum mPlatform mCoreIsCompatible(struct VFile* vf) {

if (!vf) { return false; } - struct mCoreFilter* filter; + const struct mCoreFilter* filter; for (filter = &_filters[0]; filter->filter; ++filter) { if (filter->filter(vf)) { return filter->platform;
M src/core/video-logger.csrc/feature/video-logger.c

@@ -3,15 +3,30 @@ *

* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include <mgba/core/video-logger.h> +#include "video-logger.h" #include <mgba/core/core.h> #include <mgba-util/memory.h> #include <mgba-util/vfs.h> +#ifdef M_CORE_GBA +#include <mgba/gba/core.h> +#endif + const char mVL_MAGIC[] = "mVL\0"; +const static struct mVLDescriptor { + enum mPlatform platform; + struct mCore* (*open)(void); +} _descriptors[] = { +#ifdef M_CORE_GBA + { PLATFORM_GBA, GBAVideoLogPlayerCreate }, +#endif + { PLATFORM_NONE, 0 } +}; + static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length); +static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t length); static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block); static inline size_t _roundUp(size_t value, int shift) {

@@ -19,8 +34,12 @@ value += (1 << shift) - 1;

return value >> shift; } -void mVideoLoggerRendererCreate(struct mVideoLogger* logger) { - logger->writeData = _writeData; +void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly) { + if (readonly) { + logger->writeData = _writeNull; + } else { + logger->writeData = _writeData; + } logger->readData = _readData; logger->vf = NULL; }

@@ -93,7 +112,7 @@ if (logger->vramDirtyBitmap[i]) {

uint32_t bitmap = logger->vramDirtyBitmap[i]; logger->vramDirtyBitmap[i] = 0; int j; - for (j = 0; j < 32; ++j) { + for (j = 0; j < mVL_MAX_CHANNELS; ++j) { if (!(bitmap & (1 << j))) { continue; }

@@ -145,20 +164,29 @@ default:

return false; } } - return true; + return false; } static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) { return logger->vf->write(logger->vf, data, length) == (ssize_t) length; } +static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t length) { + UNUSED(logger); + UNUSED(data); + UNUSED(length); + return false; +} + static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) { - return logger->vf->read(logger->vf, data, length) == (ssize_t) length || !block; + return logger->vf->read(logger->vf, data, length) == (ssize_t) length; } struct mVideoLogContext* mVideoLoggerCreate(struct mCore* core) { struct mVideoLogContext* context = malloc(sizeof(*context)); - core->startVideoLog(core, context); + if (core) { + core->startVideoLog(core, context); + } return context; }

@@ -182,6 +210,7 @@ if (context->initialStateSize) {

ssize_t written = vf->write(vf, context->initialState, context->initialStateSize); if (written > 0) { STORE_32LE(pointer, 0, &header.initialStatePointer); + STORE_32LE(context->initialStateSize, 0, &header.initialStateSize); pointer += written; } else { header.initialStatePointer = 0;

@@ -191,7 +220,7 @@ header.initialStatePointer = 0;

} size_t i; - for (i = 0; i < context->nChannels && i < 32; ++i) { + for (i = 0; i < context->nChannels && i < mVL_MAX_CHANNELS; ++i) { struct VFile* channel = context->channels[i].channelData; void* block = channel->map(channel, channel->size(channel), MAP_READ);

@@ -203,6 +232,7 @@ if (context->channels[i].initialStateSize) {

ssize_t written = vf->write(vf, context->channels[i].initialState, context->channels[i].initialStateSize); if (written > 0) { STORE_32LE(pointer, 0, &chHeader.channelInitialStatePointer); + STORE_32LE(context->channels[i].initialStateSize, 0, &chHeader.channelInitialStateSize); pointer += written; } else { chHeader.channelInitialStatePointer = 0;

@@ -223,3 +253,109 @@ }

vf->seek(vf, 0, SEEK_SET); vf->write(vf, &header, sizeof(header)); } + +struct mCore* mVideoLogCoreFind(struct VFile* vf) { + if (!vf) { + return NULL; + } + struct mVideoLogHeader header = {{0}}; + vf->seek(vf, 0, SEEK_SET); + ssize_t read = vf->read(vf, &header, sizeof(header)); + if (read != sizeof(header)) { + return NULL; + } + if (memcmp(header.magic, mVL_MAGIC, sizeof(mVL_MAGIC)) != 0) { + return NULL; + } + enum mPlatform platform; + LOAD_32LE(platform, 0, &header.platform); + + const struct mVLDescriptor* descriptor; + for (descriptor = &_descriptors[0]; descriptor->platform != PLATFORM_NONE; ++descriptor) { + if (platform == descriptor->platform) { + break; + } + } + struct mCore* core = NULL; + if (descriptor->open) { + core = descriptor->open(); + } + return core; +} + +bool mVideoLogContextLoad(struct VFile* vf, struct mVideoLogContext* context) { + if (!vf) { + return false; + } + struct mVideoLogHeader header = {{0}}; + vf->seek(vf, 0, SEEK_SET); + ssize_t read = vf->read(vf, &header, sizeof(header)); + if (read != sizeof(header)) { + return false; + } + if (memcmp(header.magic, mVL_MAGIC, sizeof(mVL_MAGIC)) != 0) { + return false; + } + + // TODO: Error check + uint32_t initialStatePointer; + uint32_t initialStateSize; + LOAD_32LE(initialStatePointer, 0, &header.initialStatePointer); + LOAD_32LE(initialStateSize, 0, &header.initialStateSize); + void* initialState = anonymousMemoryMap(initialStateSize); + vf->read(vf, initialState, initialStateSize); + context->initialState = initialState; + context->initialStateSize = initialStateSize; + + uint32_t nChannels; + LOAD_32LE(nChannels, 0, &header.nChannels); + context->nChannels = nChannels; + + size_t i; + for (i = 0; i < nChannels && i < mVL_MAX_CHANNELS; ++i) { + uint32_t channelPointer; + LOAD_32LE(channelPointer, 0, &header.channelPointers[i]); + vf->seek(vf, channelPointer, SEEK_SET); + + struct mVideoLogChannelHeader chHeader; + vf->read(vf, &chHeader, sizeof(chHeader)); + + LOAD_32LE(context->channels[i].type, 0, &chHeader.type); + LOAD_32LE(context->channels[i].initialStateSize, 0, &chHeader.channelInitialStateSize); + + LOAD_32LE(channelPointer, 0, &chHeader.channelInitialStatePointer); + if (channelPointer) { + off_t position = vf->seek(vf, 0, SEEK_CUR); + vf->seek(vf, channelPointer, SEEK_SET); + + context->channels[i].initialState = anonymousMemoryMap(context->channels[i].initialStateSize); + vf->read(vf, context->channels[i].initialState, context->channels[i].initialStateSize); + vf->seek(vf, position, SEEK_SET); + } + + uint32_t channelSize; + LOAD_32LE(channelSize, 0, &chHeader.channelSize); + struct VFile* vfm = VFileMemChunk(0, channelSize); + + while (channelSize) { + uint8_t buffer[2048]; + ssize_t toRead = channelSize; + if (toRead > (ssize_t) sizeof(buffer)) { + toRead = sizeof(buffer); + } + toRead = vf->read(vf, buffer, toRead); + if (toRead > 0) { + channelSize -= toRead; + } else { + break; + } + vfm->write(vfm, buffer, toRead); + } + context->channels[i].channelData = vfm; + } + + for (; i < mVL_MAX_CHANNELS; ++i) { + context->channels[i].channelData = NULL; + } + return true; +}
M src/gba/core.csrc/gba/core.c

@@ -10,6 +10,7 @@ #include <mgba/core/log.h>

#include <mgba/internal/arm/debugger/debugger.h> #include <mgba/internal/gba/cheats.h> #include <mgba/internal/gba/gba.h> +#include <mgba/internal/gba/io.h> #include <mgba/internal/gba/extra/cli.h> #include <mgba/internal/gba/overrides.h> #ifndef DISABLE_THREADING

@@ -44,6 +45,7 @@ struct mCore d;

struct GBAVideoSoftwareRenderer renderer; struct GBAVideoProxyRenderer logProxy; struct mVideoLogContext* logContext; + struct mCoreCallbacks logCallbacks; #ifndef DISABLE_THREADING struct GBAVideoThreadProxyRenderer threadProxy; int threadedVideo;

@@ -654,17 +656,21 @@ struct GBACore* gbacore = (struct GBACore*) core;

struct GBA* gba = core->board; gbacore->logContext = context; - GBAVideoProxyRendererCreate(&gbacore->logProxy, gba->video.renderer); + GBAVideoProxyRendererCreate(&gbacore->logProxy, gba->video.renderer, false); context->initialStateSize = core->stateSize(core); context->initialState = anonymousMemoryMap(context->initialStateSize); core->saveState(core, context->initialState); + struct GBASerializedState* state = context->initialState; + state->id = 0; + state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM; struct VFile* vf = VFileMemChunk(NULL, 0); context->nChannels = 1; context->channels[0].initialState = NULL; context->channels[0].initialStateSize = 0; context->channels[0].channelData = vf; + context->channels[0].type = 0; gbacore->logProxy.logger.vf = vf; gbacore->logProxy.block = false;

@@ -756,3 +762,96 @@ core->startVideoLog = _GBACoreStartVideoLog;

core->endVideoLog = _GBACoreEndVideoLog; return core; } + +#ifndef MINIMAL_CORE +static void _GBAVLPStartFrameCallback(void *context) { + struct mCore* core = context; + struct GBACore* gbacore = (struct GBACore*) core; + struct GBA* gba = core->board; + + if (!mVideoLoggerRendererRun(&gbacore->logProxy.logger)) { + GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy); + gbacore->logProxy.logger.vf->seek(gbacore->logProxy.logger.vf, 0, SEEK_SET); + core->loadState(core, gbacore->logContext->initialState); + GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy); + + // Make sure CPU loop never spins + GBAHalt(gba); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL); + } +} + +static bool _GBAVLPInit(struct mCore* core) { + struct GBACore* gbacore = (struct GBACore*) core; + GBAVideoProxyRendererCreate(&gbacore->logProxy, NULL, true); + memset(&gbacore->logCallbacks, 0, sizeof(gbacore->logCallbacks)); + gbacore->logCallbacks.videoFrameStarted = _GBAVLPStartFrameCallback; + gbacore->logCallbacks.context = core; + if (_GBACoreInit(core)) { + core->addCoreCallbacks(core, &gbacore->logCallbacks); + return true; + } + return false; +} + +static void _GBAVLPDeinit(struct mCore* core) { + struct GBACore* gbacore = (struct GBACore*) core; + if (gbacore->logContext) { + mVideoLoggerDestroy(core, gbacore->logContext); + } + _GBACoreDeinit(core); +} + +static void _GBAVLPReset(struct mCore* core) { + struct GBACore* gbacore = (struct GBACore*) core; + struct GBA* gba = (struct GBA*) core->board; + if (gba->video.renderer == &gbacore->logProxy.d) { + GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy); + } else if (gbacore->renderer.outputBuffer) { + struct GBAVideoRenderer* renderer = &gbacore->renderer.d; + GBAVideoAssociateRenderer(&gba->video, renderer); + } + gbacore->logProxy.logger.vf->seek(gbacore->logProxy.logger.vf, 0, SEEK_SET); + + ARMReset(core->cpu); + core->loadState(core, gbacore->logContext->initialState); + GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy); + + // Make sure CPU loop never spins + GBAHalt(gba); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL); +} + +static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) { + struct GBACore* gbacore = (struct GBACore*) core; + gbacore->logContext = mVideoLoggerCreate(NULL); + if (!mVideoLogContextLoad(vf, gbacore->logContext)) { + mVideoLoggerDestroy(core, gbacore->logContext); + gbacore->logContext = NULL; + return false; + } + gbacore->logProxy.logger.vf = gbacore->logContext->channels[0].channelData; + return true; +} + +static bool _returnTrue(struct VFile* vf) { + UNUSED(vf); + return true; +} + +struct mCore* GBAVideoLogPlayerCreate(void) { + struct mCore* core = GBACoreCreate(); + core->init = _GBAVLPInit; + core->deinit = _GBAVLPDeinit; + core->reset = _GBAVLPReset; + core->loadROM = _GBAVLPLoadROM; + core->isROM = _returnTrue; + return core; +} +#else +struct mCore* GBAVideoLogPlayerCreate(void) { + return false; +} +#endif
M src/gba/memory.csrc/gba/memory.c

@@ -102,7 +102,7 @@ }

} void GBAMemoryReset(struct GBA* gba) { - if (gba->memory.rom || gba->memory.fullBios) { + if (gba->memory.rom || gba->memory.fullBios || !gba->memory.wram) { // Not multiboot if (gba->memory.wram) { mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM);

@@ -274,10 +274,10 @@ cpu->memory.activeMask = SIZE_PALETTE_RAM - 1;

break; case REGION_VRAM: if (address & 0x10000) { - cpu->memory.activeRegion = (uint32_t*) &gba->video.renderer->vram[0x8000]; + cpu->memory.activeRegion = (uint32_t*) &gba->video.vram[0x8000]; cpu->memory.activeMask = 0x00007FFF; } else { - cpu->memory.activeRegion = (uint32_t*) gba->video.renderer->vram; + cpu->memory.activeRegion = (uint32_t*) gba->video.vram; cpu->memory.activeMask = 0x0000FFFF; } break;

@@ -377,9 +377,9 @@ wait += waitstatesRegion[REGION_PALETTE_RAM];

#define LOAD_VRAM \ if ((address & 0x0001FFFF) < SIZE_VRAM) { \ - LOAD_32(value, address & 0x0001FFFC, gba->video.renderer->vram); \ + LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \ } else { \ - LOAD_32(value, address & 0x00017FFC, gba->video.renderer->vram); \ + LOAD_32(value, address & 0x00017FFC, gba->video.vram); \ } \ wait += waitstatesRegion[REGION_VRAM];

@@ -507,9 +507,9 @@ LOAD_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette);

break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - LOAD_16(value, address & 0x0001FFFE, gba->video.renderer->vram); + LOAD_16(value, address & 0x0001FFFE, gba->video.vram); } else { - LOAD_16(value, address & 0x00017FFE, gba->video.renderer->vram); + LOAD_16(value, address & 0x00017FFE, gba->video.vram); } break; case REGION_OAM:

@@ -608,9 +608,9 @@ value = ((uint8_t*) gba->video.palette)[address & (SIZE_PALETTE_RAM - 1)];

break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - value = ((uint8_t*) gba->video.renderer->vram)[address & 0x0001FFFF]; + value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF]; } else { - value = ((uint8_t*) gba->video.renderer->vram)[address & 0x00017FFF]; + value = ((uint8_t*) gba->video.vram)[address & 0x00017FFF]; } break; case REGION_OAM:

@@ -691,11 +691,11 @@ gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 4), value);

#define STORE_VRAM \ if ((address & 0x0001FFFF) < SIZE_VRAM) { \ - STORE_32(value, address & 0x0001FFFC, gba->video.renderer->vram); \ + STORE_32(value, address & 0x0001FFFC, gba->video.vram); \ gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \ gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \ } else { \ - STORE_32(value, address & 0x00017FFC, gba->video.renderer->vram); \ + STORE_32(value, address & 0x00017FFC, gba->video.vram); \ gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \ gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \ } \

@@ -796,10 +796,10 @@ gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value);

break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - STORE_16(value, address & 0x0001FFFE, gba->video.renderer->vram); + STORE_16(value, address & 0x0001FFFE, gba->video.vram); gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); } else { - STORE_16(value, address & 0x00017FFE, gba->video.renderer->vram); + STORE_16(value, address & 0x00017FFE, gba->video.vram); gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); } break;

@@ -1052,11 +1052,11 @@ gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16);

break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - LOAD_32(oldValue, address & 0x0001FFFC, gba->video.renderer->vram); - STORE_32(value, address & 0x0001FFFC, gba->video.renderer->vram); + LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); + STORE_32(value, address & 0x0001FFFC, gba->video.vram); } else { - LOAD_32(oldValue, address & 0x00017FFC, gba->video.renderer->vram); - STORE_32(value, address & 0x00017FFC, gba->video.renderer->vram); + LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram); + STORE_32(value, address & 0x00017FFC, gba->video.vram); } break; case REGION_OAM:

@@ -1121,11 +1121,11 @@ gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value);

break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - LOAD_16(oldValue, address & 0x0001FFFE, gba->video.renderer->vram); - STORE_16(value, address & 0x0001FFFE, gba->video.renderer->vram); + LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram); + STORE_16(value, address & 0x0001FFFE, gba->video.vram); } else { - LOAD_16(oldValue, address & 0x00017FFE, gba->video.renderer->vram); - STORE_16(value, address & 0x00017FFE, gba->video.renderer->vram); + LOAD_16(oldValue, address & 0x00017FFE, gba->video.vram); + STORE_16(value, address & 0x00017FFE, gba->video.vram); } break; case REGION_OAM:
M src/gba/renderers/proxy.csrc/gba/renderers/proxy.c

@@ -24,8 +24,11 @@

static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet); static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address); -void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend) { - mVideoLoggerRendererCreate(&renderer->logger); +void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend, bool readonly) { + mVideoLoggerRendererCreate(&renderer->logger, readonly); + if (readonly) { + renderer->block = true; + } renderer->d.init = GBAVideoProxyRendererInit; renderer->d.reset = GBAVideoProxyRendererReset;

@@ -56,6 +59,11 @@ renderer->logger.paletteSize = SIZE_PALETTE_RAM;

renderer->logger.vramSize = SIZE_VRAM; renderer->logger.oamSize = SIZE_OAM; + renderer->lock = NULL; + renderer->unlock = NULL; + renderer->wait = NULL; + renderer->wake = NULL; + renderer->backend = backend; }

@@ -76,8 +84,8 @@ }

static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) { memcpy(proxyRenderer->logger.oam, &proxyRenderer->d.oam->raw, SIZE_OAM); - memcpy(proxyRenderer->logger.palette, &proxyRenderer->d.palette, SIZE_PALETTE_RAM); - memcpy(proxyRenderer->logger.vram, &proxyRenderer->d.vram, SIZE_VRAM); + memcpy(proxyRenderer->logger.palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM); + memcpy(proxyRenderer->logger.vram, proxyRenderer->d.vram, SIZE_VRAM); mVideoLoggerRendererReset(&proxyRenderer->logger);

@@ -87,11 +95,12 @@ }

} void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) { - if (video->renderer != renderer->backend) { + if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) { return; } - renderer->d.cache = video->renderer->cache; + renderer->backend = video->renderer; video->renderer = &renderer->d; + renderer->d.cache = renderer->backend->cache; renderer->d.palette = video->palette; renderer->d.vram = video->vram; renderer->d.oam = &video->oam;

@@ -203,6 +212,9 @@ return value;

} mVideoLoggerRendererWriteVideoRegister(&proxyRenderer->logger, address, value); + if (!proxyRenderer->block) { + proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value); + } return value; }

@@ -242,47 +254,47 @@ if (!proxyRenderer->block) {

proxyRenderer->backend->drawScanline(proxyRenderer->backend, y); } mVideoLoggerRendererDrawScanline(&proxyRenderer->logger, y); - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wake) { proxyRenderer->wake(proxyRenderer, y); } } void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->lock(proxyRenderer); proxyRenderer->wait(proxyRenderer); } mVideoLoggerRendererFlush(&proxyRenderer->logger); - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->unlock(proxyRenderer); } } static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->lock(proxyRenderer); // Insert an extra item into the queue to make sure it gets flushed mVideoLoggerRendererFlush(&proxyRenderer->logger); proxyRenderer->wait(proxyRenderer); } proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels); - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->unlock(proxyRenderer); } } static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->lock(proxyRenderer); // Insert an extra item into the queue to make sure it gets flushed mVideoLoggerRendererFlush(&proxyRenderer->logger); proxyRenderer->wait(proxyRenderer); } proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->unlock(proxyRenderer); } }
M src/gba/renderers/thread-proxy.csrc/gba/renderers/thread-proxy.c

@@ -27,7 +27,7 @@ static void _wake(struct GBAVideoProxyRenderer* proxyRenderer, int y);

void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) { renderer->d.block = true; - GBAVideoProxyRendererCreate(&renderer->d, backend); + GBAVideoProxyRendererCreate(&renderer->d, backend, false); renderer->d.init = GBAVideoThreadProxyRendererInit; renderer->d.reset = GBAVideoThreadProxyRendererReset;
M src/gba/video.csrc/gba/video.c

@@ -295,7 +295,7 @@ // Nothing to do

} void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state) { - memcpy(state->vram, video->renderer->vram, SIZE_VRAM); + memcpy(state->vram, video->vram, SIZE_VRAM); 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);

@@ -303,7 +303,7 @@ STORE_32(video->frameCounter, 0, &state->video.frameCounter);

} void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState* state) { - memcpy(video->renderer->vram, state->vram, SIZE_VRAM); + memcpy(video->vram, state->vram, SIZE_VRAM); uint16_t value; int i; for (i = 0; i < SIZE_OAM; i += 2) {
M src/platform/qt/GameController.cppsrc/platform/qt/GameController.cpp

@@ -20,7 +20,6 @@ #include <mgba/core/config.h>

#include <mgba/core/directories.h> #include <mgba/core/serialize.h> #include <mgba/core/tile-cache.h> -#include <mgba/core/video-logger.h> #ifdef M_CORE_GBA #include <mgba/gba/interface.h> #include <mgba/internal/gba/gba.h>

@@ -33,6 +32,7 @@ #include <mgba/internal/gb/gb.h>

#include <mgba/internal/gb/renderers/tile-cache.h> #endif #include <mgba-util/vfs.h> +#include "feature/video-logger.h" using namespace QGBA; using namespace std;

@@ -158,6 +158,7 @@ controller->m_multiplayer->detachGame(controller);

} controller->m_patch = QString(); controller->clearOverride(); + controller->endVideoLog(); QMetaObject::invokeMethod(controller->m_audioProcessor, "pause");

@@ -1205,6 +1206,8 @@ void GameController::startVideoLog(const QString& path) {

if (!isLoaded() || m_vl) { return; } + + Interrupter interrupter(this); m_vlPath = path; m_vl = mVideoLoggerCreate(m_threadContext.core); }

@@ -1213,13 +1216,15 @@ void GameController::endVideoLog() {

if (!m_vl) { return; } + + Interrupter interrupter(this); if (isLoaded()) { VFile* vf = VFileDevice::open(m_vlPath, O_WRONLY | O_CREAT | O_TRUNC); mVideoLoggerWrite(m_threadContext.core, m_vl, vf); vf->close(vf); } mVideoLoggerDestroy(m_threadContext.core, m_vl); - m_vf = nullptr; + m_vl = nullptr; } void GameController::pollEvents() {
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -354,6 +354,7 @@ #endif

formats.removeDuplicates(); filters.prepend(tr("All ROMs (%1)").arg(formats.join(QChar(' ')))); + filters.append(tr("%1 Video Logs (*.mvl)").arg(projectName)); return filters.join(";;"); }