diff --git a/include/mgba-util/circle-buffer.h b/include/mgba-util/circle-buffer.h index 28d436e03..dee1f1f2a 100644 --- a/include/mgba-util/circle-buffer.h +++ b/include/mgba-util/circle-buffer.h @@ -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); diff --git a/src/feature/thread-proxy.c b/src/feature/thread-proxy.c index 91db10f4b..51e807565 100644 --- a/src/feature/thread-proxy.c +++ b/src/feature/thread-proxy.c @@ -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) { diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index c3187a3fc..57a028c3c 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -8,6 +8,7 @@ #include #include #include +#include #ifdef M_CORE_GBA #include @@ -16,6 +17,8 @@ #include #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; - } - } else { - header.initialStatePointer = 0; + uint32_t flags = 0; + if (context->initialState) { + flags |= mVL_FLAG_HAS_INITIAL_STATE; } + 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) { + 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)); + } +} + +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; +} + +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; + } + + LOAD_32LE(context->nChannels, 0, &header.nChannels); + if (context->nChannels > mVL_MAX_CHANNELS) { + return false; + } + + 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 < 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) { + 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; +} - struct mVideoLogChannelHeader chHeader = {0}; - STORE_32LE(context->channels[i].type, 0, &chHeader.type); - STORE_32LE(channel->size(channel), 0, &chHeader.channelSize); +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)); - 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; - } + 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; } - STORE_32LE(pointer, 0, &header.channelPointers[i]); - ssize_t written = vf->write(vf, &chHeader, sizeof(chHeader)); - if (written != sizeof(chHeader)) { - continue; + thisRead = vf->read(vf, buffer, thisRead); + if (thisRead <= 0) { + return; } - pointer += written; - written = vf->write(vf, block, channel->size(channel)); - if (written != channel->size(channel)) { + 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; -} diff --git a/src/feature/video-logger.h b/src/feature/video-logger.h index 41fb8ff47..f194433a2 100644 --- a/src/feature/video-logger.h +++ b/src/feature/video-logger.h @@ -10,6 +10,8 @@ CXX_GUARD_START +#include + #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 diff --git a/src/gb/core.c b/src/gb/core.c index cd77c6f33..756c9e19d 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -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; } diff --git a/src/gba/core.c b/src/gba/core.c index 522c8a41b..ea09e4d85 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -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; } diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 3667400a7..3c86f96a5 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -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; } diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index 66b4b4012..86f02579d 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -247,7 +247,7 @@ private: mAVStream* m_stream; mVideoLogContext* m_vl; - QString m_vlPath; + VFile* m_vlVf; struct GameControllerLux : GBALuminanceSource { GameController* p; diff --git a/src/util/circle-buffer.c b/src/util/circle-buffer.c index deb426473..c60326e3c 100644 --- a/src/util/circle-buffer.c +++ b/src/util/circle-buffer.c @@ -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)) {