From 5665ac0316df4800bec0e942e4d18ba3fec59310 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Apr 2017 21:12:27 -0700 Subject: [PATCH] GBA Core: Video log playing --- CMakeLists.txt | 6 +- include/mgba/gba/core.h | 1 + include/mgba/internal/gba/renderers/proxy.h | 4 +- src/core/core.c | 12 +- src/{core => feature}/video-logger.c | 152 +++++++++++++++++- .../mgba/core => src/feature}/video-logger.h | 18 ++- src/gba/core.c | 101 +++++++++++- src/gba/memory.c | 42 ++--- src/gba/renderers/proxy.c | 38 +++-- src/gba/renderers/thread-proxy.c | 2 +- src/gba/video.c | 4 +- src/platform/qt/GameController.cpp | 9 +- src/platform/qt/Window.cpp | 1 + 13 files changed, 329 insertions(+), 61 deletions(-) rename src/{core => feature}/video-logger.c (61%) rename {include/mgba/core => src/feature}/video-logger.h (86%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04a4f1c06..90222c0f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.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 @@ if(NOT MINIMAL_CORE) endif() list(APPEND SRC ${FEATURE_SRC} - ${CLI_SRC}) + ${EXTRA_SRC}) endif() if(NOT SKIP_LIBRARY) @@ -771,7 +771,7 @@ if(BUILD_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() diff --git a/include/mgba/gba/core.h b/include/mgba/gba/core.h index cb4c1a862..65a19f447 100644 --- a/include/mgba/gba/core.h +++ b/include/mgba/gba/core.h @@ -12,6 +12,7 @@ CXX_GUARD_START struct mCore; struct mCore* GBACoreCreate(void); +struct mCore* GBAVideoLogPlayerCreate(void); CXX_GUARD_END diff --git a/include/mgba/internal/gba/renderers/proxy.h b/include/mgba/internal/gba/renderers/proxy.h index 06b8b6bcc..2ff3c8761 100644 --- a/include/mgba/internal/gba/renderers/proxy.h +++ b/include/mgba/internal/gba/renderers/proxy.h @@ -11,7 +11,7 @@ CXX_GUARD_START #include -#include +#include "feature/video-logger.h" struct GBAVideoProxyRenderer { struct GBAVideoRenderer d; @@ -30,7 +30,7 @@ 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); diff --git a/src/core/core.c b/src/core/core.c index 439f89c3e..c07198366 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -18,8 +18,11 @@ #include #include #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 @@ struct mCore* mCoreFindVF(struct VFile* vf) { 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; diff --git a/src/core/video-logger.c b/src/feature/video-logger.c similarity index 61% rename from src/core/video-logger.c rename to src/feature/video-logger.c index 15359402b..2b9ce2cc8 100644 --- a/src/core/video-logger.c +++ b/src/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 +#include "video-logger.h" #include #include #include +#ifdef M_CORE_GBA +#include +#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 @@ static inline size_t _roundUp(size_t value, int shift) { 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 @@ void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y) { 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 @@ bool mVideoLoggerRendererRun(struct mVideoLogger* logger) { 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 @@ void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, str 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 @@ void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, str } 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 @@ void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, str 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 @@ void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, str 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; +} diff --git a/include/mgba/core/video-logger.h b/src/feature/video-logger.h similarity index 86% rename from include/mgba/core/video-logger.h rename to src/feature/video-logger.h index f204e9bb5..3a08baca1 100644 --- a/include/mgba/core/video-logger.h +++ b/src/feature/video-logger.h @@ -10,6 +10,8 @@ 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); @@ -99,6 +104,9 @@ 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 #endif diff --git a/src/gba/core.c b/src/gba/core.c index 7b1417ab6..26b99ec23 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #ifndef DISABLE_THREADING @@ -44,6 +45,7 @@ struct GBACore { struct GBAVideoSoftwareRenderer renderer; struct GBAVideoProxyRenderer logProxy; struct mVideoLogContext* logContext; + struct mCoreCallbacks logCallbacks; #ifndef DISABLE_THREADING struct GBAVideoThreadProxyRenderer threadProxy; int threadedVideo; @@ -654,17 +656,21 @@ static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* c 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 @@ struct mCore* GBACoreCreate(void) { 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 diff --git a/src/gba/memory.c b/src/gba/memory.c index a6280254e..db4a30122 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -102,7 +102,7 @@ void GBAMemoryDeinit(struct GBA* gba) { } 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 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { 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 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { #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 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { 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 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { 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 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { #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 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle 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 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o 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 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o 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: diff --git a/src/gba/renderers/proxy.c b/src/gba/renderers/proxy.c index 6e543c884..f1eaa8bf7 100644 --- a/src/gba/renderers/proxy.c +++ b/src/gba/renderers/proxy.c @@ -24,8 +24,11 @@ static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, si 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 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct 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 _init(struct GBAVideoProxyRenderer* proxyRenderer) { 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 @@ static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) { } 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 @@ uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* render } mVideoLoggerRendererWriteVideoRegister(&proxyRenderer->logger, address, value); + if (!proxyRenderer->block) { + proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value); + } return value; } @@ -242,47 +254,47 @@ void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) 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); } } diff --git a/src/gba/renderers/thread-proxy.c b/src/gba/renderers/thread-proxy.c index a47a3f727..3dc0d6acb 100644 --- a/src/gba/renderers/thread-proxy.c +++ b/src/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; diff --git a/src/gba/video.c b/src/gba/video.c index 935d1fcea..db0614342 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -295,7 +295,7 @@ static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, si } 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 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* } 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) { diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 7aa4657a5..3667400a7 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #ifdef M_CORE_GBA #include #include @@ -33,6 +32,7 @@ #include #endif #include +#include "feature/video-logger.h" using namespace QGBA; using namespace std; @@ -158,6 +158,7 @@ GameController::GameController(QObject* parent) } 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() { diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 17800c082..04872eb99 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -354,6 +354,7 @@ QString Window::getFilters() const { formats.removeDuplicates(); filters.prepend(tr("All ROMs (%1)").arg(formats.join(QChar(' ')))); + filters.append(tr("%1 Video Logs (*.mvl)").arg(projectName)); return filters.join(";;"); }