GBA Core: Video log playing

This commit is contained in:
Vicki Pfau 2017-04-16 21:12:27 -07:00
parent 73947766de
commit 5665ac0316
13 changed files with 329 additions and 61 deletions

View File

@ -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()

View File

@ -12,6 +12,7 @@ CXX_GUARD_START
struct mCore;
struct mCore* GBACoreCreate(void);
struct mCore* GBAVideoLogPlayerCreate(void);
CXX_GUARD_END

View File

@ -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 @@ 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);

View File

@ -18,8 +18,11 @@
#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 @@ 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;

View File

@ -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 @@ 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;
}

View File

@ -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

View File

@ -10,6 +10,7 @@
#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 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

View File

@ -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:

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -20,7 +20,6 @@
#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/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 @@ 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() {

View File

@ -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(";;");
}