diff --git a/include/mgba/feature/video-logger.h b/include/mgba/feature/video-logger.h index cf50dd58e..373ea8069 100644 --- a/include/mgba/feature/video-logger.h +++ b/include/mgba/feature/video-logger.h @@ -35,6 +35,8 @@ enum mVideoLoggerEvent { LOGGER_EVENT_DEINIT, LOGGER_EVENT_RESET, LOGGER_EVENT_GET_PIXELS, + LOGGER_EVENT_LOAD_STATE, + LOGGER_EVENT_SAVE_STATE, }; enum mVideoLoggerInjectionPoint { @@ -85,6 +87,10 @@ struct mVideoLogger { const void* pixelBuffer; size_t pixelStride; + + void* stateBuffer; + size_t stateSize; + bool stateStatus; }; void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly); diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index b31fd0f5a..9bd21821a 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -285,6 +285,11 @@ DECL_BIT(GBASerializedMiscFlags, IrqPending, 2); DECL_BIT(GBASerializedMiscFlags, Blocked, 3); DECL_BITS(GBASerializedMiscFlags, KeyIRQKeys, 4, 11); +enum { + GBA_SUBSYSTEM_VIDEO_RENDERER = 0, + GBA_SUBSYSTEM_MAX, +}; + struct GBASerializedState { uint32_t versionMagic; uint32_t biosChecksum; diff --git a/include/mgba/internal/gba/video.h b/include/mgba/internal/gba/video.h index b29b3d607..924772660 100644 --- a/include/mgba/internal/gba/video.h +++ b/include/mgba/internal/gba/video.h @@ -182,6 +182,10 @@ struct GBAVideoRenderer { void (*reset)(struct GBAVideoRenderer* renderer); void (*deinit)(struct GBAVideoRenderer* renderer); + uint32_t (*rendererId)(const struct GBAVideoRenderer* renderer); + bool (*loadState)(struct GBAVideoRenderer* renderer, const void* state, size_t size); + void (*saveState)(struct GBAVideoRenderer* renderer, void** state, size_t* size); + uint16_t (*writeVideoRegister)(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); void (*writeVRAM)(struct GBAVideoRenderer* renderer, uint32_t address); void (*writePalette)(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); diff --git a/src/gba/core.c b/src/gba/core.c index 904d85c20..e9cf2b81c 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -833,14 +834,44 @@ static bool _GBACoreSaveState(struct mCore* core, void* state) { } static bool _GBACoreLoadExtraState(struct mCore* core, const struct mStateExtdata* extdata) { - UNUSED(core); - UNUSED(extdata); - return true; + struct GBA* gba = core->board; + struct mStateExtdataItem item; + bool ok = true; + if (mStateExtdataGet(extdata, EXTDATA_SUBSYSTEM_START + GBA_SUBSYSTEM_VIDEO_RENDERER, &item)) { + if ((uint32_t) item.size > sizeof(uint32_t)) { + uint32_t type; + LOAD_32(type, 0, item.data); + if (type == gba->video.renderer->rendererId(gba->video.renderer)) { + ok = gba->video.renderer->loadState(gba->video.renderer, + (void*) ((uintptr_t) item.data + sizeof(uint32_t)), + item.size - sizeof(type)); + } + } else if (item.data) { + ok = false; + } + } + return ok; } static bool _GBACoreSaveExtraState(struct mCore* core, struct mStateExtdata* extdata) { - UNUSED(core); - UNUSED(extdata); + struct GBA* gba = core->board; + void* buffer = NULL; + size_t size = 0; + gba->video.renderer->saveState(gba->video.renderer, &buffer, &size); + if (size > 0 && buffer) { + struct mStateExtdataItem item; + item.size = size + sizeof(uint32_t); + item.data = malloc(item.size); + item.clean = free; + uint32_t type = gba->video.renderer->rendererId(gba->video.renderer); + STORE_32(type, 0, item.data); + memcpy((void*) ((uintptr_t) item.data + sizeof(uint32_t)), buffer, size); + mStateExtdataPut(extdata, EXTDATA_SUBSYSTEM_START + GBA_SUBSYSTEM_VIDEO_RENDERER, &item); + } + if (buffer) { + free(buffer); + } + return true; } diff --git a/src/gba/extra/proxy.c b/src/gba/extra/proxy.c index 7842bef1f..0847e1f4a 100644 --- a/src/gba/extra/proxy.c +++ b/src/gba/extra/proxy.c @@ -13,6 +13,9 @@ static void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer); static void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer); static void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer); +static uint32_t GBAVideoProxyRendererId(const struct GBAVideoRenderer* renderer); +static bool GBAVideoProxyRendererLoadState(struct GBAVideoRenderer* renderer, const void* state, size_t size); +static void GBAVideoProxyRendererSaveState(struct GBAVideoRenderer* renderer, void** state, size_t* size); static uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); static void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address); static void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); @@ -27,9 +30,13 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address); void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend) { + memset(renderer, 0, sizeof(*renderer)); renderer->d.init = GBAVideoProxyRendererInit; renderer->d.reset = GBAVideoProxyRendererReset; renderer->d.deinit = GBAVideoProxyRendererDeinit; + renderer->d.rendererId = GBAVideoProxyRendererId; + renderer->d.loadState = GBAVideoProxyRendererLoadState; + renderer->d.saveState = GBAVideoProxyRendererSaveState; renderer->d.writeVideoRegister = GBAVideoProxyRendererWriteVideoRegister; renderer->d.writeVRAM = GBAVideoProxyRendererWriteVRAM; renderer->d.writeOAM = GBAVideoProxyRendererWriteOAM; @@ -172,6 +179,11 @@ void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) { mVideoLoggerRendererDeinit(proxyRenderer->logger); } +uint32_t GBAVideoProxyRendererId(const struct GBAVideoRenderer* renderer) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + return proxyRenderer->backend->rendererId(proxyRenderer->backend); +} + static void _handleEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event) { struct GBAVideoProxyRenderer* proxyRenderer = logger->context; switch (event) { @@ -189,6 +201,12 @@ static void _handleEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent eve case LOGGER_EVENT_GET_PIXELS: proxyRenderer->backend->getPixels(proxyRenderer->backend, &logger->pixelStride, &logger->pixelBuffer); break; + case LOGGER_EVENT_LOAD_STATE: + logger->stateStatus = proxyRenderer->backend->loadState(proxyRenderer->backend, logger->stateBuffer, logger->stateSize); + break; + case LOGGER_EVENT_SAVE_STATE: + proxyRenderer->backend->saveState(proxyRenderer->backend, &logger->stateBuffer, &logger->stateSize); + break; } } @@ -279,6 +297,35 @@ uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* render return value; } +bool GBAVideoProxyRendererLoadState(struct GBAVideoRenderer* renderer, const void* state, size_t size) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->wait(proxyRenderer->logger); + proxyRenderer->logger->stateBuffer = (void*) state; + proxyRenderer->logger->stateSize = size; + proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_LOAD_STATE); + proxyRenderer->logger->stateBuffer = NULL; + proxyRenderer->logger->stateSize = 0; + return proxyRenderer->logger->stateStatus; + } else { + return proxyRenderer->backend->loadState(proxyRenderer->backend, state, size); + } +} + +void GBAVideoProxyRendererSaveState(struct GBAVideoRenderer* renderer, void** state, size_t* size) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->wait(proxyRenderer->logger); + proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_SAVE_STATE); + *state = proxyRenderer->logger->stateBuffer; + *size = proxyRenderer->logger->stateSize; + proxyRenderer->logger->stateBuffer = NULL; + proxyRenderer->logger->stateSize = 0; + } else { + proxyRenderer->backend->saveState(proxyRenderer->backend, state, size); + } +} + void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address); diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index afc9c54ac..24fb092c7 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -13,9 +13,14 @@ #include #include +#define OPENGL_MAGIC 0x6E726C67 + static void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer); static void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer); static void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer); +static uint32_t GBAVideoGLRendererId(const struct GBAVideoRenderer* renderer); +static bool GBAVideoGLRendererLoadState(struct GBAVideoRenderer* renderer, const void* state, size_t size); +static void GBAVideoGLRendererSaveState(struct GBAVideoRenderer* renderer, void** state, size_t* size); static void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address); static void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam); static void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); @@ -656,9 +661,13 @@ static const GLint _vertices[] = { }; void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer) { + memset(renderer, 0, sizeof(*renderer)); renderer->d.init = GBAVideoGLRendererInit; renderer->d.reset = GBAVideoGLRendererReset; renderer->d.deinit = GBAVideoGLRendererDeinit; + renderer->d.rendererId = GBAVideoGLRendererId; + renderer->d.loadState = GBAVideoGLRendererLoadState; + renderer->d.saveState = GBAVideoGLRendererSaveState; renderer->d.writeVideoRegister = GBAVideoGLRendererWriteVideoRegister; renderer->d.writeVRAM = GBAVideoGLRendererWriteVRAM; renderer->d.writeOAM = GBAVideoGLRendererWriteOAM; @@ -953,6 +962,26 @@ void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) { } } +static uint32_t GBAVideoGLRendererId(const struct GBAVideoRenderer* renderer) { + UNUSED(renderer); + return OPENGL_MAGIC; +} + +static bool GBAVideoGLRendererLoadState(struct GBAVideoRenderer* renderer, const void* state, size_t size) { + UNUSED(renderer); + UNUSED(state); + UNUSED(size); + // TODO + return false; +} + +static void GBAVideoGLRendererSaveState(struct GBAVideoRenderer* renderer, void** state, size_t* size) { + UNUSED(renderer); + *state = NULL; + *size = 0; + // TODO +} + void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer; if (renderer->cache) { diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 7eba88ba3..41f092937 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -14,10 +14,14 @@ #define DIRTY_SCANLINE(R, Y) R->scanlineDirty[Y >> 5] |= (1U << (Y & 0x1F)) #define CLEAN_SCANLINE(R, Y) R->scanlineDirty[Y >> 5] &= ~(1U << (Y & 0x1F)) +#define SOFTWARE_MAGIC 0x6E727773 static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer); static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer); static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer); +static uint32_t GBAVideoSoftwareRendererId(const struct GBAVideoRenderer* renderer); +static bool GBAVideoSoftwareRendererLoadState(struct GBAVideoRenderer* renderer, const void* state, size_t size); +static void GBAVideoSoftwareRendererSaveState(struct GBAVideoRenderer* renderer, void** state, size_t* size); static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address); static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam); static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); @@ -47,9 +51,13 @@ static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, stru static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win); void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) { + memset(renderer, 0, sizeof(*renderer)); renderer->d.init = GBAVideoSoftwareRendererInit; renderer->d.reset = GBAVideoSoftwareRendererReset; renderer->d.deinit = GBAVideoSoftwareRendererDeinit; + renderer->d.rendererId = GBAVideoSoftwareRendererId; + renderer->d.loadState = GBAVideoSoftwareRendererLoadState; + renderer->d.saveState = GBAVideoSoftwareRendererSaveState; renderer->d.writeVideoRegister = GBAVideoSoftwareRendererWriteVideoRegister; renderer->d.writeVRAM = GBAVideoSoftwareRendererWriteVRAM; renderer->d.writeOAM = GBAVideoSoftwareRendererWriteOAM; @@ -79,7 +87,7 @@ void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) { renderer->d.highlightColor = M_COLOR_WHITE; renderer->d.highlightAmount = 0; - renderer->temporaryBuffer = 0; + renderer->temporaryBuffer = NULL; } static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) { @@ -155,6 +163,26 @@ static void GBAVideoSoftwareRendererDeinit(struct GBAVideoRenderer* renderer) { UNUSED(softwareRenderer); } +static uint32_t GBAVideoSoftwareRendererId(const struct GBAVideoRenderer* renderer) { + UNUSED(renderer); + return SOFTWARE_MAGIC; +} + +static bool GBAVideoSoftwareRendererLoadState(struct GBAVideoRenderer* renderer, const void* state, size_t size) { + UNUSED(renderer); + UNUSED(state); + UNUSED(size); + // TODO + return false; +} + +static void GBAVideoSoftwareRendererSaveState(struct GBAVideoRenderer* renderer, void** state, size_t* size) { + UNUSED(renderer); + *state = NULL; + *size = 0; + // TODO +} + static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; if (renderer->cache) {