mirror of https://github.com/mgba-emu/mgba.git
Core: Compressed video logs
This commit is contained in:
parent
c4aa669cf1
commit
f82ef55517
|
@ -17,7 +17,11 @@
|
||||||
#include <mgba/gb/core.h>
|
#include <mgba/gb/core.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define BUFFER_BASE_SIZE 0x8000
|
#ifdef USE_ZLIB
|
||||||
|
#include <zlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BUFFER_BASE_SIZE 0x20000
|
||||||
|
|
||||||
const char mVL_MAGIC[] = "mVL\0";
|
const char mVL_MAGIC[] = "mVL\0";
|
||||||
|
|
||||||
|
@ -53,6 +57,10 @@ struct mVLBlockHeader {
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum mVLBlockFlag {
|
||||||
|
mVL_FLAG_BLOCK_COMPRESSED = 1
|
||||||
|
};
|
||||||
|
|
||||||
struct mVideoLogHeader {
|
struct mVideoLogHeader {
|
||||||
char magic[4];
|
char magic[4];
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
@ -70,6 +78,10 @@ struct mVideoLogChannel {
|
||||||
|
|
||||||
off_t currentPointer;
|
off_t currentPointer;
|
||||||
size_t bufferRemaining;
|
size_t bufferRemaining;
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
bool inflating;
|
||||||
|
z_stream inflateStream;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct CircleBuffer buffer;
|
struct CircleBuffer buffer;
|
||||||
};
|
};
|
||||||
|
@ -429,11 +441,82 @@ bool mVideoLogContextLoad(struct mVideoLogContext* context, struct VFile* vf) {
|
||||||
context->channels[i].bufferRemaining = 0;
|
context->channels[i].bufferRemaining = 0;
|
||||||
context->channels[i].currentPointer = pointer;
|
context->channels[i].currentPointer = pointer;
|
||||||
context->channels[i].p = context;
|
context->channels[i].p = context;
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
context->channels[i].inflating = false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
static void _flushBufferCompressed(struct mVideoLogContext* context) {
|
||||||
|
struct CircleBuffer* buffer = &context->channels[context->activeChannel].buffer;
|
||||||
|
if (!CircleBufferSize(buffer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t writeBuffer[0x400];
|
||||||
|
struct mVLBlockHeader header = { 0 };
|
||||||
|
STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType);
|
||||||
|
|
||||||
|
STORE_32LE(context->activeChannel, 0, &header.channelId);
|
||||||
|
STORE_32LE(mVL_FLAG_BLOCK_COMPRESSED, 0, &header.flags);
|
||||||
|
|
||||||
|
uint8_t compressBuffer[0x800];
|
||||||
|
z_stream zstr;
|
||||||
|
zstr.zalloc = Z_NULL;
|
||||||
|
zstr.zfree = Z_NULL;
|
||||||
|
zstr.opaque = Z_NULL;
|
||||||
|
zstr.avail_in = 0;
|
||||||
|
zstr.avail_out = sizeof(compressBuffer);
|
||||||
|
zstr.next_out = (Bytef*) compressBuffer;
|
||||||
|
if (deflateInit(&zstr, 9) != Z_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VFile* vfm = VFileMemChunk(NULL, 0);
|
||||||
|
|
||||||
|
while (CircleBufferSize(buffer)) {
|
||||||
|
size_t read = CircleBufferRead(buffer, writeBuffer, sizeof(writeBuffer));
|
||||||
|
zstr.avail_in = read;
|
||||||
|
zstr.next_in = (Bytef*) writeBuffer;
|
||||||
|
while (zstr.avail_in) {
|
||||||
|
if (deflate(&zstr, Z_NO_FLUSH) == Z_STREAM_ERROR) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vfm->write(vfm, compressBuffer, sizeof(compressBuffer) - zstr.avail_out);
|
||||||
|
zstr.avail_out = sizeof(compressBuffer);
|
||||||
|
zstr.next_out = (Bytef*) compressBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
zstr.avail_out = sizeof(compressBuffer);
|
||||||
|
zstr.next_out = (Bytef*) compressBuffer;
|
||||||
|
zstr.avail_in = 0;
|
||||||
|
int ret = deflate(&zstr, Z_FINISH);
|
||||||
|
if (ret == Z_STREAM_ERROR) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vfm->write(vfm, compressBuffer, sizeof(compressBuffer) - zstr.avail_out);
|
||||||
|
} while (sizeof(compressBuffer) - zstr.avail_out);
|
||||||
|
|
||||||
|
size_t size = vfm->size(vfm);
|
||||||
|
STORE_32LE(size, 0, &header.length);
|
||||||
|
context->backing->write(context->backing, &header, sizeof(header));
|
||||||
|
void* vfmm = vfm->map(vfm, size, MAP_READ);
|
||||||
|
context->backing->write(context->backing, vfmm, size);
|
||||||
|
vfm->unmap(vfm, vfmm, size);
|
||||||
|
vfm->close(vfm);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void _flushBuffer(struct mVideoLogContext* context) {
|
static void _flushBuffer(struct mVideoLogContext* context) {
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
// TODO: Make optional
|
||||||
|
_flushBufferCompressed(context);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct CircleBuffer* buffer = &context->channels[context->activeChannel].buffer;
|
struct CircleBuffer* buffer = &context->channels[context->activeChannel].buffer;
|
||||||
if (!CircleBufferSize(buffer)) {
|
if (!CircleBufferSize(buffer)) {
|
||||||
return;
|
return;
|
||||||
|
@ -442,9 +525,10 @@ static void _flushBuffer(struct mVideoLogContext* context) {
|
||||||
STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType);
|
STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType);
|
||||||
STORE_32LE(CircleBufferSize(buffer), 0, &header.length);
|
STORE_32LE(CircleBufferSize(buffer), 0, &header.length);
|
||||||
STORE_32LE(context->activeChannel, 0, &header.channelId);
|
STORE_32LE(context->activeChannel, 0, &header.channelId);
|
||||||
|
|
||||||
context->backing->write(context->backing, &header, sizeof(header));
|
context->backing->write(context->backing, &header, sizeof(header));
|
||||||
|
|
||||||
uint8_t writeBuffer[0x1000];
|
uint8_t writeBuffer[0x800];
|
||||||
while (CircleBufferSize(buffer)) {
|
while (CircleBufferSize(buffer)) {
|
||||||
size_t read = CircleBufferRead(buffer, writeBuffer, sizeof(writeBuffer));
|
size_t read = CircleBufferRead(buffer, writeBuffer, sizeof(writeBuffer));
|
||||||
context->backing->write(context->backing, writeBuffer, read);
|
context->backing->write(context->backing, writeBuffer, read);
|
||||||
|
@ -503,8 +587,83 @@ int mVideoLoggerAddChannel(struct mVideoLogContext* context) {
|
||||||
return chid;
|
return chid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
static size_t _readBufferCompressed(struct VFile* vf, struct mVideoLogChannel* channel, size_t length) {
|
||||||
|
uint8_t fbuffer[0x400];
|
||||||
|
uint8_t zbuffer[0x800];
|
||||||
|
size_t read = 0;
|
||||||
|
|
||||||
|
channel->inflateStream.avail_in = 0;
|
||||||
|
while (length) {
|
||||||
|
size_t thisWrite = sizeof(zbuffer);
|
||||||
|
if (thisWrite > length) {
|
||||||
|
thisWrite = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t thisRead = 0;
|
||||||
|
if (channel->inflating && channel->inflateStream.avail_in) {
|
||||||
|
channel->inflateStream.next_out = zbuffer;
|
||||||
|
channel->inflateStream.avail_out = thisWrite;
|
||||||
|
thisRead = channel->inflateStream.avail_in;
|
||||||
|
} else if (channel->bufferRemaining) {
|
||||||
|
thisRead = sizeof(fbuffer);
|
||||||
|
if (thisRead > channel->bufferRemaining) {
|
||||||
|
thisRead = channel->bufferRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
thisRead = vf->read(vf, fbuffer, thisRead);
|
||||||
|
if (thisRead <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel->inflateStream.next_in = fbuffer;
|
||||||
|
channel->inflateStream.avail_in = thisRead;
|
||||||
|
channel->inflateStream.next_out = zbuffer;
|
||||||
|
channel->inflateStream.avail_out = thisWrite;
|
||||||
|
|
||||||
|
if (!channel->inflating) {
|
||||||
|
if (inflateInit(&channel->inflateStream) != Z_OK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
channel->inflating = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel->inflateStream.next_in = Z_NULL;
|
||||||
|
channel->inflateStream.avail_in = 0;
|
||||||
|
channel->inflateStream.next_out = zbuffer;
|
||||||
|
channel->inflateStream.avail_out = thisWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = inflate(&channel->inflateStream, Z_NO_FLUSH);
|
||||||
|
|
||||||
|
if (channel->inflateStream.next_in != Z_NULL) {
|
||||||
|
thisRead -= channel->inflateStream.avail_in;
|
||||||
|
channel->currentPointer += thisRead;
|
||||||
|
channel->bufferRemaining -= thisRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != Z_OK) {
|
||||||
|
inflateEnd(&channel->inflateStream);
|
||||||
|
channel->inflating = false;
|
||||||
|
if (ret != Z_STREAM_END) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thisWrite = CircleBufferWrite(&channel->buffer, zbuffer, thisWrite - channel->inflateStream.avail_out);
|
||||||
|
length -= thisWrite;
|
||||||
|
read += thisWrite;
|
||||||
|
|
||||||
|
if (!channel->inflating) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void _readBuffer(struct VFile* vf, struct mVideoLogChannel* channel, size_t length) {
|
static void _readBuffer(struct VFile* vf, struct mVideoLogChannel* channel, size_t length) {
|
||||||
uint8_t buffer[0x1000];
|
uint8_t buffer[0x800];
|
||||||
while (length) {
|
while (length) {
|
||||||
size_t thisRead = sizeof(buffer);
|
size_t thisRead = sizeof(buffer);
|
||||||
if (thisRead > length) {
|
if (thisRead > length) {
|
||||||
|
@ -531,9 +690,16 @@ static bool _fillBuffer(struct mVideoLogContext* context, size_t channelId, size
|
||||||
while (length) {
|
while (length) {
|
||||||
size_t bufferRemaining = channel->bufferRemaining;
|
size_t bufferRemaining = channel->bufferRemaining;
|
||||||
if (bufferRemaining) {
|
if (bufferRemaining) {
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
if (channel->inflating) {
|
||||||
|
length -= _readBufferCompressed(context->backing, channel, length);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (bufferRemaining > length) {
|
if (bufferRemaining > length) {
|
||||||
bufferRemaining = length;
|
bufferRemaining = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
_readBuffer(context->backing, channel, bufferRemaining);
|
_readBuffer(context->backing, channel, bufferRemaining);
|
||||||
length -= bufferRemaining;
|
length -= bufferRemaining;
|
||||||
continue;
|
continue;
|
||||||
|
@ -553,8 +719,15 @@ static bool _fillBuffer(struct mVideoLogContext* context, size_t channelId, size
|
||||||
if (!header.length) {
|
if (!header.length) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel->bufferRemaining = header.length;
|
channel->bufferRemaining = header.length;
|
||||||
|
|
||||||
|
if (header.flags & mVL_FLAG_BLOCK_COMPRESSED) {
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
length -= _readBufferCompressed(context->backing, channel, length);
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue