GBA Core: Video log playing
jump to
@@ -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()
@@ -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
@@ -12,6 +12,7 @@ CXX_GUARD_START
struct mCore; struct mCore* GBACoreCreate(void); +struct mCore* GBAVideoLogPlayerCreate(void); CXX_GUARD_END
@@ -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);
@@ -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;
@@ -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; +}
@@ -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
@@ -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:
@@ -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); } }
@@ -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;
@@ -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) {
@@ -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() {
@@ -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(";;"); }