mirror of https://github.com/mgba-emu/mgba.git
GBA Core: Video log playing
This commit is contained in:
parent
73947766de
commit
5665ac0316
|
@ -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()
|
||||
|
|
|
@ -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 @@ 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 @@
|
|||
#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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
101
src/gba/core.c
101
src/gba/core.c
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 @@ 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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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(";;");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue