Core: New mVL file format

This commit is contained in:
Vicki Pfau 2017-04-22 21:41:48 -07:00
parent 3021996a49
commit deffdc79a0
9 changed files with 412 additions and 224 deletions

View File

@ -26,6 +26,7 @@ void CircleBufferClear(struct CircleBuffer* buffer);
int CircleBufferWrite8(struct CircleBuffer* buffer, int8_t value);
int CircleBufferWrite16(struct CircleBuffer* buffer, int16_t value);
int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value);
size_t CircleBufferWrite(struct CircleBuffer* buffer, const void* input, size_t length);
int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value);
int CircleBufferRead16(struct CircleBuffer* buffer, int16_t* value);
int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value);

View File

@ -39,7 +39,6 @@ void mVideoThreadProxyCreate(struct mVideoThreadProxy* renderer) {
renderer->d.writeData = _writeData;
renderer->d.readData = _readData;
renderer->d.vf = NULL;
}
void mVideoThreadProxyInit(struct mVideoLogger* logger) {

View File

@ -8,6 +8,7 @@
#include <mgba/core/core.h>
#include <mgba-util/memory.h>
#include <mgba-util/vfs.h>
#include <mgba-util/math.h>
#ifdef M_CORE_GBA
#include <mgba/gba/core.h>
@ -16,6 +17,8 @@
#include <mgba/gb/core.h>
#endif
#define BUFFER_BASE_SIZE 0x8000
const char mVL_MAGIC[] = "mVL\0";
const static struct mVLDescriptor {
@ -31,10 +34,64 @@ const static struct mVLDescriptor {
{ PLATFORM_NONE, 0 }
};
enum mVLBlockType {
mVL_BLOCK_DUMMY = 0,
mVL_BLOCK_INITIAL_STATE,
mVL_BLOCK_CHANNEL_HEADER,
mVL_BLOCK_DATA,
mVL_BLOCK_FOOTER = 0x784C566D
};
enum mVLHeaderFlag {
mVL_FLAG_HAS_INITIAL_STATE = 1
};
struct mVLBlockHeader {
uint32_t blockType;
uint32_t length;
uint32_t channelId;
uint32_t flags;
};
struct mVideoLogHeader {
char magic[4];
uint32_t flags;
uint32_t platform;
uint32_t nChannels;
};
struct mVideoLogContext;
struct mVideoLogChannel {
struct mVideoLogContext* p;
uint32_t type;
void* initialState;
size_t initialStateSize;
off_t currentPointer;
size_t bufferRemaining;
struct CircleBuffer buffer;
};
struct mVideoLogContext {
void* initialState;
size_t initialStateSize;
uint32_t nChannels;
struct mVideoLogChannel channels[mVL_MAX_CHANNELS];
uint32_t activeChannel;
struct VFile* backing;
};
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 ssize_t mVideoLoggerReadChannel(struct mVideoLogChannel* channel, void* data, size_t length);
static ssize_t mVideoLoggerWriteChannel(struct mVideoLogChannel* channel, const void* data, size_t length);
static inline size_t _roundUp(size_t value, int shift) {
value += (1 << shift) - 1;
return value >> shift;
@ -48,7 +105,7 @@ void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly) {
logger->writeData = _writeData;
}
logger->readData = _readData;
logger->vf = NULL;
logger->dataContext = NULL;
logger->init = NULL;
logger->deinit = NULL;
@ -235,7 +292,8 @@ bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block) {
}
static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {
return logger->vf->write(logger->vf, data, length) == (ssize_t) length;
struct mVideoLogChannel* channel = logger->dataContext;
return mVideoLoggerWriteChannel(channel, data, length) == (ssize_t) length;
}
static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t length) {
@ -247,92 +305,316 @@ static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t len
static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) {
UNUSED(block);
return logger->vf->read(logger->vf, data, length) == (ssize_t) length;
struct mVideoLogChannel* channel = logger->dataContext;
return mVideoLoggerReadChannel(channel, data, length) == (ssize_t) length;
}
struct mVideoLogContext* mVideoLoggerCreate(struct mCore* core) {
void mVideoLoggerAttachChannel(struct mVideoLogger* logger, struct mVideoLogContext* context, size_t channelId) {
if (channelId >= mVL_MAX_CHANNELS) {
return;
}
logger->dataContext = &context->channels[channelId];
}
struct mVideoLogContext* mVideoLogContextCreate(struct mCore* core) {
struct mVideoLogContext* context = malloc(sizeof(*context));
memset(context, 0, sizeof(*context));
if (core) {
context->initialStateSize = core->stateSize(core);
context->initialState = anonymousMemoryMap(context->initialStateSize);
core->saveState(core, context->initialState);
core->startVideoLog(core, context);
}
context->activeChannel = 0;
return context;
}
void mVideoLoggerDestroy(struct mCore* core, struct mVideoLogContext* context) {
if (core) {
core->endVideoLog(core);
}
free(context);
void mVideoLogContextSetOutput(struct mVideoLogContext* context, struct VFile* vf) {
context->backing = vf;
vf->truncate(vf, 0);
vf->seek(vf, 0, SEEK_SET);
}
void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, struct VFile* vf) {
struct mVideoLogHeader header = {{0}};
memcpy(header.magic, mVL_MAGIC, sizeof(mVL_MAGIC));
void mVideoLogContextWriteHeader(struct mVideoLogContext* context, struct mCore* core) {
struct mVideoLogHeader header = { { 0 } };
memcpy(header.magic, mVL_MAGIC, sizeof(header.magic));
enum mPlatform platform = core->platform(core);
STORE_32LE(platform, 0, &header.platform);
STORE_32LE(context->nChannels, 0, &header.nChannels);
ssize_t pointer = vf->seek(vf, sizeof(header), SEEK_SET);
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;
uint32_t flags = 0;
if (context->initialState) {
flags |= mVL_FLAG_HAS_INITIAL_STATE;
}
} else {
header.initialStatePointer = 0;
STORE_32LE(flags, 0, &header.flags);
context->backing->write(context->backing, &header, sizeof(header));
if (context->initialState) {
struct mVLBlockHeader chheader = { 0 };
STORE_32LE(mVL_BLOCK_INITIAL_STATE, 0, &chheader.blockType);
STORE_32LE(context->initialStateSize, 0, &chheader.length);
context->backing->write(context->backing, &chheader, sizeof(chheader));
context->backing->write(context->backing, context->initialState, context->initialStateSize);
}
size_t 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);
for (i = 0; i < context->nChannels; ++i) {
struct mVLBlockHeader chheader = { 0 };
STORE_32LE(mVL_BLOCK_CHANNEL_HEADER, 0, &chheader.blockType);
STORE_32LE(i, 0, &chheader.channelId);
context->backing->write(context->backing, &chheader, sizeof(chheader));
}
}
struct mVideoLogChannelHeader chHeader = {0};
STORE_32LE(context->channels[i].type, 0, &chHeader.type);
STORE_32LE(channel->size(channel), 0, &chHeader.channelSize);
bool _readBlockHeader(struct mVideoLogContext* context, struct mVLBlockHeader* header) {
struct mVLBlockHeader buffer;
if (context->backing->read(context->backing, &buffer, sizeof(buffer)) != sizeof(buffer)) {
return false;
}
LOAD_32LE(header->blockType, 0, &buffer.blockType);
LOAD_32LE(header->length, 0, &buffer.length);
LOAD_32LE(header->channelId, 0, &buffer.channelId);
LOAD_32LE(header->flags, 0, &buffer.flags);
return true;
}
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;
bool _readHeader(struct mVideoLogContext* context) {
struct mVideoLogHeader header;
context->backing->seek(context->backing, 0, SEEK_SET);
if (context->backing->read(context->backing, &header, sizeof(header)) != sizeof(header)) {
return false;
}
if (memcmp(header.magic, mVL_MAGIC, sizeof(header.magic)) != 0) {
return false;
}
STORE_32LE(pointer, 0, &header.channelPointers[i]);
ssize_t written = vf->write(vf, &chHeader, sizeof(chHeader));
if (written != sizeof(chHeader)) {
continue;
LOAD_32LE(context->nChannels, 0, &header.nChannels);
if (context->nChannels > mVL_MAX_CHANNELS) {
return false;
}
pointer += written;
written = vf->write(vf, block, channel->size(channel));
if (written != channel->size(channel)) {
uint32_t flags;
LOAD_32LE(flags, 0, &header.flags);
if (flags & mVL_FLAG_HAS_INITIAL_STATE) {
struct mVLBlockHeader header;
if (!_readBlockHeader(context, &header)) {
return false;
}
if (header.blockType != mVL_BLOCK_INITIAL_STATE) {
return false;
}
context->initialStateSize = header.length;
context->initialState = anonymousMemoryMap(header.length);
context->backing->read(context->backing, context->initialState, context->initialStateSize);
}
return true;
}
bool mVideoLogContextLoad(struct mVideoLogContext* context, struct VFile* vf) {
context->backing = vf;
if (!_readHeader(context)) {
return false;
}
off_t pointer = context->backing->seek(context->backing, 0, SEEK_CUR);
size_t i;
for (i = 0; i < context->nChannels; ++i) {
CircleBufferInit(&context->channels[i].buffer, BUFFER_BASE_SIZE);
context->channels[i].bufferRemaining = 0;
context->channels[i].currentPointer = pointer;
context->channels[i].p = context;
}
return true;
}
static void _flushBuffer(struct mVideoLogContext* context) {
struct CircleBuffer* buffer = &context->channels[context->activeChannel].buffer;
if (!CircleBufferSize(buffer)) {
return;
}
struct mVLBlockHeader header = { 0 };
STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType);
STORE_32LE(CircleBufferSize(buffer), 0, &header.length);
STORE_32LE(context->activeChannel, 0, &header.channelId);
context->backing->write(context->backing, &header, sizeof(header));
uint8_t writeBuffer[0x1000];
while (CircleBufferSize(buffer)) {
size_t read = CircleBufferRead(buffer, writeBuffer, sizeof(writeBuffer));
context->backing->write(context->backing, writeBuffer, read);
}
}
void mVideoLogContextDestroy(struct mCore* core, struct mVideoLogContext* context) {
_flushBuffer(context);
struct mVLBlockHeader header = { 0 };
STORE_32LE(mVL_BLOCK_FOOTER, 0, &header.blockType);
context->backing->write(context->backing, &header, sizeof(header));
if (core) {
core->endVideoLog(core);
}
if (context->initialState) {
mappedMemoryFree(context->initialState, context->initialStateSize);
}
free(context);
}
void mVideoLogContextRewind(struct mVideoLogContext* context, struct mCore* core) {
_readHeader(context);
if (core) {
core->loadState(core, context->initialState);
}
off_t pointer = context->backing->seek(context->backing, 0, SEEK_CUR);
size_t i;
for (i = 0; i < context->nChannels; ++i) {
CircleBufferClear(&context->channels[i].buffer);
context->channels[i].bufferRemaining = 0;
context->channels[i].currentPointer = pointer;
}
}
void* mVideoLogContextInitialState(struct mVideoLogContext* context, size_t* size) {
if (size) {
*size = context->initialStateSize;
}
return context->initialState;
}
int mVideoLoggerAddChannel(struct mVideoLogContext* context) {
if (context->nChannels >= mVL_MAX_CHANNELS) {
return -1;
}
int chid = context->nChannels;
++context->nChannels;
context->channels[chid].p = context;
CircleBufferInit(&context->channels[chid].buffer, BUFFER_BASE_SIZE);
return chid;
}
static void _readBuffer(struct VFile* vf, struct mVideoLogChannel* channel, size_t length) {
uint8_t buffer[0x1000];
while (length) {
size_t thisRead = sizeof(buffer);
if (thisRead > length) {
thisRead = length;
}
thisRead = vf->read(vf, buffer, thisRead);
if (thisRead <= 0) {
return;
}
size_t thisWrite = CircleBufferWrite(&channel->buffer, buffer, thisRead);
length -= thisWrite;
channel->bufferRemaining -= thisWrite;
channel->currentPointer += thisWrite;
if (thisWrite < thisRead) {
break;
}
pointer += written;
}
vf->seek(vf, 0, SEEK_SET);
vf->write(vf, &header, sizeof(header));
}
static bool _fillBuffer(struct mVideoLogContext* context, size_t channelId, size_t length) {
struct mVideoLogChannel* channel = &context->channels[channelId];
context->backing->seek(context->backing, channel->currentPointer, SEEK_SET);
struct mVLBlockHeader header;
while (length) {
size_t bufferRemaining = channel->bufferRemaining;
if (bufferRemaining) {
if (bufferRemaining > length) {
bufferRemaining = length;
}
_readBuffer(context->backing, channel, bufferRemaining);
length -= bufferRemaining;
continue;
}
if (!_readBlockHeader(context, &header)) {
return false;
}
if (header.blockType == mVL_BLOCK_FOOTER) {
return false;
}
if (header.channelId != channelId || header.blockType != mVL_BLOCK_DATA) {
context->backing->seek(context->backing, header.length, SEEK_CUR);
continue;
}
channel->currentPointer = context->backing->seek(context->backing, 0, SEEK_CUR);
if (!header.length) {
continue;
}
channel->bufferRemaining = header.length;
}
return true;
}
static ssize_t mVideoLoggerReadChannel(struct mVideoLogChannel* channel, void* data, size_t length) {
struct mVideoLogContext* context = channel->p;
unsigned channelId = channel - context->channels;
if (channelId >= mVL_MAX_CHANNELS) {
return 0;
}
if (CircleBufferSize(&channel->buffer) >= length) {
return CircleBufferRead(&channel->buffer, data, length);
}
ssize_t size = 0;
if (CircleBufferSize(&channel->buffer)) {
size = CircleBufferRead(&channel->buffer, data, CircleBufferSize(&channel->buffer));
if (size <= 0) {
return size;
}
data = (uint8_t*) data + size;
length -= size;
}
if (!_fillBuffer(context, channelId, BUFFER_BASE_SIZE)) {
return size;
}
size += CircleBufferRead(&channel->buffer, data, length);
return size;
}
static ssize_t mVideoLoggerWriteChannel(struct mVideoLogChannel* channel, const void* data, size_t length) {
struct mVideoLogContext* context = channel->p;
unsigned channelId = channel - context->channels;
if (channelId >= mVL_MAX_CHANNELS) {
return 0;
}
if (channelId != context->activeChannel) {
_flushBuffer(context);
context->activeChannel = channelId;
}
if (CircleBufferCapacity(&channel->buffer) - CircleBufferSize(&channel->buffer) < length) {
_flushBuffer(context);
if (CircleBufferCapacity(&channel->buffer) < length) {
CircleBufferDeinit(&channel->buffer);
CircleBufferInit(&channel->buffer, toPow2(length << 1));
}
}
ssize_t read = CircleBufferWrite(&channel->buffer, data, length);
if (CircleBufferCapacity(&channel->buffer) == CircleBufferSize(&channel->buffer)) {
_flushBuffer(context);
}
return read;
}
struct mCore* mVideoLogCoreFind(struct VFile* vf) {
if (!vf) {
return NULL;
}
struct mVideoLogHeader header = {{0}};
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) {
if (memcmp(header.magic, mVL_MAGIC, sizeof(header.magic)) != 0) {
return NULL;
}
enum mPlatform platform;
@ -350,80 +632,3 @@ struct mCore* mVideoLogCoreFind(struct VFile* vf) {
}
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
#include <mgba-util/circle-buffer.h>
#define mVL_MAX_CHANNELS 32
enum mVideoLoggerDirtyType {
@ -36,6 +38,7 @@ struct VFile;
struct mVideoLogger {
bool (*writeData)(struct mVideoLogger* logger, const void* data, size_t length);
bool (*readData)(struct mVideoLogger* logger, void* data, size_t length, bool block);
void* dataContext;
bool block;
void (*init)(struct mVideoLogger*);
@ -61,40 +64,6 @@ struct mVideoLogger {
uint16_t* vram;
uint16_t* oam;
uint16_t* palette;
struct VFile* vf;
};
struct mVideoLogChannel {
uint32_t type;
void* initialState;
size_t initialStateSize;
struct VFile* channelData;
};
struct mVideoLogContext {
void* initialState;
size_t initialStateSize;
uint32_t nChannels;
struct mVideoLogChannel channels[mVL_MAX_CHANNELS];
};
struct mVideoLogHeader {
char magic[4];
uint32_t reserved;
uint32_t platform;
uint32_t padding;
uint32_t initialStatePointer;
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;
};
void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly);
@ -116,13 +85,24 @@ void mVideoLoggerRendererFinishFrame(struct mVideoLogger* logger);
bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block);
struct mVideoLogContext;
void mVideoLoggerAttachChannel(struct mVideoLogger* logger, struct mVideoLogContext* context, size_t channelId);
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 mVideoLogContext* mVideoLogContextCreate(struct mCore* core);
void mVideoLogContextSetOutput(struct mVideoLogContext*, struct VFile*);
void mVideoLogContextWriteHeader(struct mVideoLogContext*, struct mCore* core);
bool mVideoLogContextLoad(struct mVideoLogContext*, struct VFile*);
void mVideoLogContextDestroy(struct mCore* core, struct mVideoLogContext*);
void mVideoLogContextRewind(struct mVideoLogContext*, struct mCore*);
void* mVideoLogContextInitialState(struct mVideoLogContext*, size_t* size);
int mVideoLoggerAddChannel(struct mVideoLogContext*);
struct mCore* mVideoLogCoreFind(struct VFile*);
bool mVideoLogContextLoad(struct VFile*, struct mVideoLogContext*);
CXX_GUARD_END

View File

@ -34,6 +34,7 @@ const static struct mCoreChannelInfo _GBAudioChannels[] = {
{ 3, "ch3", "Channel 3", "Noise" },
};
struct mVideoLogContext;
struct GBCore {
struct mCore d;
struct GBVideoSoftwareRenderer renderer;
@ -644,20 +645,10 @@ static void _GBCoreStartVideoLog(struct mCore* core, struct mVideoLogContext* co
struct GB* gb = core->board;
gbcore->logContext = context;
context->initialStateSize = core->stateSize(core);
context->initialState = anonymousMemoryMap(context->initialStateSize);
core->saveState(core, context->initialState);
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;
int channelId = mVideoLoggerAddChannel(context);
gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, false);
gbcore->proxyRenderer.logger->vf = vf;
mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, context, channelId);
gbcore->proxyRenderer.logger->block = false;
GBVideoProxyRendererCreate(&gbcore->proxyRenderer, &gbcore->renderer.d);
@ -670,9 +661,6 @@ static void _GBCoreEndVideoLog(struct mCore* core) {
GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
free(gbcore->proxyRenderer.logger);
gbcore->proxyRenderer.logger = NULL;
mappedMemoryFree(gbcore->logContext->initialState, gbcore->logContext->initialStateSize);
gbcore->logContext->channels[0].channelData->close(gbcore->logContext->channels[0].channelData);
}
struct mCore* GBCoreCreate(void) {
@ -762,8 +750,7 @@ static void _GBVLPStartFrameCallback(void *context) {
if (!mVideoLoggerRendererRun(gbcore->proxyRenderer.logger, true)) {
GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
gbcore->proxyRenderer.logger->vf->seek(gbcore->proxyRenderer.logger->vf, 0, SEEK_SET);
core->loadState(core, gbcore->logContext->initialState);
mVideoLogContextRewind(gbcore->logContext, core);
GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
// Make sure CPU loop never spins
@ -791,7 +778,7 @@ static bool _GBVLPInit(struct mCore* core) {
static void _GBVLPDeinit(struct mCore* core) {
struct GBCore* gbcore = (struct GBCore*) core;
if (gbcore->logContext) {
mVideoLoggerDestroy(core, gbcore->logContext);
mVideoLogContextDestroy(core, gbcore->logContext);
}
_GBCoreDeinit(core);
}
@ -805,10 +792,9 @@ static void _GBVLPReset(struct mCore* core) {
struct GBVideoRenderer* renderer = &gbcore->renderer.d;
GBVideoAssociateRenderer(&gb->video, renderer);
}
gbcore->proxyRenderer.logger->vf->seek(gbcore->proxyRenderer.logger->vf, 0, SEEK_SET);
LR35902Reset(core->cpu);
core->loadState(core, gbcore->logContext->initialState);
mVideoLogContextRewind(gbcore->logContext, core);
GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
// Make sure CPU loop never spins
@ -819,13 +805,13 @@ static void _GBVLPReset(struct mCore* core) {
static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) {
struct GBCore* gbcore = (struct GBCore*) core;
gbcore->logContext = mVideoLoggerCreate(NULL);
if (!mVideoLogContextLoad(vf, gbcore->logContext)) {
mVideoLoggerDestroy(core, gbcore->logContext);
gbcore->logContext = mVideoLogContextCreate(NULL);
if (!mVideoLogContextLoad(gbcore->logContext, vf)) {
mVideoLogContextDestroy(core, gbcore->logContext);
gbcore->logContext = NULL;
return false;
}
gbcore->proxyRenderer.logger->vf = gbcore->logContext->channels[0].channelData;
mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, gbcore->logContext, 0);
return true;
}

View File

@ -41,6 +41,7 @@ const static struct mCoreChannelInfo _GBAAudioChannels[] = {
{ 5, "chB", "FIFO Channel B", NULL },
};
struct mVideoLogContext;
struct GBACore {
struct mCore d;
struct GBAVideoSoftwareRenderer renderer;
@ -660,23 +661,14 @@ static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* c
struct GBA* gba = core->board;
gbacore->logContext = context;
context->initialStateSize = core->stateSize(core);
context->initialState = anonymousMemoryMap(context->initialStateSize);
core->saveState(core, context->initialState);
struct GBASerializedState* state = context->initialState;
struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL);
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;
int channelId = mVideoLoggerAddChannel(context);
gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, false);
gbacore->proxyRenderer.logger->vf = vf;
mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, context, channelId);
gbacore->proxyRenderer.logger->block = false;
GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, &gbacore->renderer.d);
@ -689,9 +681,6 @@ static void _GBACoreEndVideoLog(struct mCore* core) {
GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
free(gbacore->proxyRenderer.logger);
gbacore->proxyRenderer.logger = NULL;
mappedMemoryFree(gbacore->logContext->initialState, gbacore->logContext->initialStateSize);
gbacore->logContext->channels[0].channelData->close(gbacore->logContext->channels[0].channelData);
}
struct mCore* GBACoreCreate(void) {
@ -781,8 +770,7 @@ static void _GBAVLPStartFrameCallback(void *context) {
if (!mVideoLoggerRendererRun(gbacore->proxyRenderer.logger, true)) {
GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
gbacore->proxyRenderer.logger->vf->seek(gbacore->proxyRenderer.logger->vf, 0, SEEK_SET);
core->loadState(core, gbacore->logContext->initialState);
mVideoLogContextRewind(gbacore->logContext, core);
GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
// Make sure CPU loop never spins
@ -810,7 +798,7 @@ static bool _GBAVLPInit(struct mCore* core) {
static void _GBAVLPDeinit(struct mCore* core) {
struct GBACore* gbacore = (struct GBACore*) core;
if (gbacore->logContext) {
mVideoLoggerDestroy(core, gbacore->logContext);
mVideoLogContextDestroy(core, gbacore->logContext);
}
_GBACoreDeinit(core);
}
@ -824,10 +812,9 @@ static void _GBAVLPReset(struct mCore* core) {
struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
GBAVideoAssociateRenderer(&gba->video, renderer);
}
gbacore->proxyRenderer.logger->vf->seek(gbacore->proxyRenderer.logger->vf, 0, SEEK_SET);
ARMReset(core->cpu);
core->loadState(core, gbacore->logContext->initialState);
mVideoLogContextRewind(gbacore->logContext, core);
GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
// Make sure CPU loop never spins
@ -838,13 +825,13 @@ static void _GBAVLPReset(struct mCore* core) {
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 = mVideoLogContextCreate(NULL);
if (!mVideoLogContextLoad(gbacore->logContext, vf)) {
mVideoLogContextDestroy(core, gbacore->logContext);
gbacore->logContext = NULL;
return false;
}
gbacore->proxyRenderer.logger->vf = gbacore->logContext->channels[0].channelData;
mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, gbacore->logContext, 0);
return true;
}

View File

@ -73,6 +73,7 @@ GameController::GameController(QObject* parent)
, m_preload(false)
, m_override(nullptr)
, m_vl(nullptr)
, m_vlVf(nullptr)
{
#ifdef M_CORE_GBA
m_lux.p = this;
@ -1208,8 +1209,10 @@ void GameController::startVideoLog(const QString& path) {
}
Interrupter interrupter(this);
m_vlPath = path;
m_vl = mVideoLoggerCreate(m_threadContext.core);
m_vl = mVideoLogContextCreate(m_threadContext.core);
m_vlVf = VFileDevice::open(path, O_WRONLY | O_CREAT | O_TRUNC);
mVideoLogContextSetOutput(m_vl, m_vlVf);
mVideoLogContextWriteHeader(m_vl, m_threadContext.core);
}
void GameController::endVideoLog() {
@ -1218,12 +1221,11 @@ void GameController::endVideoLog() {
}
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);
mVideoLogContextDestroy(m_threadContext.core, m_vl);
if (m_vlVf) {
m_vlVf->close(m_vlVf);
m_vlVf = nullptr;
}
mVideoLoggerDestroy(m_threadContext.core, m_vl);
m_vl = nullptr;
}

View File

@ -247,7 +247,7 @@ private:
mAVStream* m_stream;
mVideoLogContext* m_vl;
QString m_vlPath;
VFile* m_vlVf;
struct GameControllerLux : GBALuminanceSource {
GameController* p;

View File

@ -125,6 +125,34 @@ int CircleBufferWrite16(struct CircleBuffer* buffer, int16_t value) {
return 2;
}
size_t CircleBufferWrite(struct CircleBuffer* buffer, const void* input, size_t length) {
int8_t* data = buffer->writePtr;
if (buffer->size + sizeof(int16_t) > buffer->capacity) {
return 0;
}
size_t remaining = buffer->capacity - ((int8_t*) data - (int8_t*) buffer->data);
if (length <= remaining) {
memcpy(data, input, length);
if (length == remaining) {
buffer->writePtr = buffer->data;
} else {
buffer->writePtr = (int8_t*) data + length;
}
} else {
memcpy(data, input, remaining);
memcpy(buffer->data, (const int8_t*) input + remaining, length - remaining);
buffer->writePtr = (int8_t*) buffer->data + length - remaining;
}
buffer->size += length;
#ifndef NDEBUG
if (!_checkIntegrity(buffer)) {
abort();
}
#endif
return length;
}
int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value) {
int8_t* data = buffer->readPtr;
if (buffer->size < sizeof(int8_t)) {