all repos — mgba @ 5c007289e40d7203c8bc5053de1f7a60090709c5

mGBA Game Boy Advance Emulator

GBA: Add extra data section to the end of savestates
Jeffrey Pfau jeffrey@endrift.com
Mon, 28 Dec 2015 03:26:17 -0500
commit

5c007289e40d7203c8bc5053de1f7a60090709c5

parent

70b9a1bfe0b3551f94d3311ddaec0efa503fc617

M src/arm/macros.hsrc/arm/macros.h

@@ -8,8 +8,10 @@ #define MACROS_H

#include "util/common.h" +#define LOAD_64 LOAD_64LE #define LOAD_32 LOAD_32LE #define LOAD_16 LOAD_16LE +#define STORE_64 STORE_64LE #define STORE_32 STORE_32LE #define STORE_16 STORE_16LE
M src/gba/gui/gui-runner.csrc/gba/gui/gui-runner.c

@@ -311,14 +311,14 @@ break;

case RUNNER_SAVE_STATE: vf = GBAGetState(runner->context.gba, 0, ((int) item->data) >> 16, true); if (vf) { - GBASaveStateNamed(runner->context.gba, vf, true); + GBASaveStateNamed(runner->context.gba, vf, SAVESTATE_SCREENSHOT); vf->close(vf); } break; case RUNNER_LOAD_STATE: vf = GBAGetState(runner->context.gba, 0, ((int) item->data) >> 16, false); if (vf) { - GBALoadStateNamed(runner->context.gba, vf); + GBALoadStateNamed(runner->context.gba, vf, SAVESTATE_SCREENSHOT); vf->close(vf); } break;
M src/gba/rr/rr.csrc/gba/rr/rr.c

@@ -27,7 +27,7 @@ }

if (gba->rr->initFrom & INIT_FROM_SAVESTATE) { struct VFile* vf = gba->rr->openSavestate(gba->rr, O_TRUNC | O_CREAT | O_RDWR); - GBASaveStateNamed(gba, vf, false); + GBASaveStateNamed(gba, vf, 0); vf->close(vf); } else { ARMReset(gba->cpu);

@@ -51,7 +51,7 @@ }

if (gba->rr->initFrom & INIT_FROM_SAVESTATE) { struct VFile* vf = gba->rr->openSavestate(gba->rr, O_RDONLY); - GBALoadStateNamed(gba, vf); + GBALoadStateNamed(gba, vf, SAVESTATE_SCREENSHOT); vf->close(vf); } else { ARMReset(gba->cpu);
M src/gba/savedata.csrc/gba/savedata.c

@@ -123,6 +123,33 @@ }

return true; } +bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in) { + if (savedata->data) { + switch (savedata->type) { + case SAVEDATA_SRAM: + return in->read(in, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM; + case SAVEDATA_FLASH512: + return in->read(in, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512; + case SAVEDATA_FLASH1M: + return in->read(in, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M; + case SAVEDATA_EEPROM: + return in->read(in, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM; + case SAVEDATA_AUTODETECT: + case SAVEDATA_FORCE_NONE: + return true; + } + } else if (savedata->vf) { + off_t read = 0; + uint8_t buffer[2048]; + do { + in->read(in, buffer, read); + read = savedata->vf->write(savedata->vf, buffer, sizeof(buffer)); + } while (read == sizeof(buffer)); + return read >= 0; + } + return true; +} + void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type, bool realisticTiming) { if (savedata->type != SAVEDATA_AUTODETECT) { struct VFile* vf = savedata->vf;

@@ -441,7 +468,7 @@ GBALog(0, GBA_LOG_INFO, "Savedata synced");

} } -void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state, bool includeData) { +void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state) { state->savedata.type = savedata->type; state->savedata.command = savedata->command; GBASerializedSavedataFlags flags = 0;

@@ -453,11 +480,9 @@ STORE_32(savedata->readAddress, 0, &state->savedata.readAddress);

STORE_32(savedata->writeAddress, 0, &state->savedata.writeAddress); STORE_16(savedata->settling, 0, &state->savedata.settlingSector); STORE_16(savedata->dust, 0, &state->savedata.settlingDust); - - UNUSED(includeData); // TODO } -void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state, bool includeData) { +void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state) { if (state->savedata.type == SAVEDATA_FORCE_NONE) { return; }

@@ -476,8 +501,6 @@

if (savedata->type == SAVEDATA_FLASH1M) { _flashSwitchBank(savedata, GBASerializedSavedataFlagsGetFlashBank(flags)); } - - UNUSED(includeData); // TODO } void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
M src/gba/savedata.hsrc/gba/savedata.h

@@ -94,6 +94,7 @@

void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf); void GBASavedataUnmask(struct GBASavedata* savedata); bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out); +bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in); void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type, bool realisticTiming); void GBASavedataInitFlash(struct GBASavedata* savedata, bool realisticTiming);

@@ -109,7 +110,7 @@

void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount); struct GBASerializedState; -void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state, bool includeData); -void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state, bool includeData); +void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state); +void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state); #endif
M src/gba/serialize.csrc/gba/serialize.c

@@ -28,6 +28,17 @@ struct GBAExtdata {

struct GBAExtdataItem data[EXTDATA_MAX]; }; +struct GBABundledState { + struct GBASerializedState* state; + struct GBAExtdata* extdata; +}; + +struct GBAExtdataHeader { + uint32_t tag; + int32_t size; + int64_t offset; +}; + void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { STORE_32(GBA_SAVESTATE_MAGIC, 0, &state->versionMagic); STORE_32(gba->biosChecksum, 0, &state->biosChecksum);

@@ -65,7 +76,7 @@ GBAMemorySerialize(&gba->memory, state);

GBAIOSerialize(gba, state); GBAVideoSerialize(&gba->video, state); GBAAudioSerialize(&gba->audio, state); - GBASavedataSerialize(&gba->memory.savedata, state, false); + GBASavedataSerialize(&gba->memory.savedata, state); state->associatedStreamId = 0; if (gba->rr) {

@@ -173,7 +184,7 @@ GBAMemoryDeserialize(&gba->memory, state);

GBAIODeserialize(gba, state); GBAVideoDeserialize(&gba->video, state); GBAAudioDeserialize(&gba->audio, state); - GBASavedataDeserialize(&gba->memory.savedata, state, false); + GBASavedataDeserialize(&gba->memory.savedata, state); if (gba->rr) { gba->rr->stateLoaded(gba->rr, state);

@@ -188,7 +199,7 @@ return VDirOptionalOpenFile(dir, gba->activeFile, "savestate", suffix, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);

} #ifdef USE_PNG -static bool _savePNGState(struct GBA* gba, struct VFile* vf) { +static bool _savePNGState(struct GBA* gba, struct VFile* vf, struct GBAExtdata* extdata) { unsigned stride; const void* pixels = 0; gba->video.renderer->getPixels(gba->video.renderer, &stride, &pixels);

@@ -219,19 +230,72 @@ return false;

} PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, stride, pixels); PNGWriteCustomChunk(png, "gbAs", len, buffer); + if (extdata) { + uint32_t i; + for (i = 1; i < EXTDATA_MAX; ++i) { + if (!extdata->data[i].data) { + continue; + } + uLongf len = compressBound(extdata->data[i].size) + sizeof(uint32_t) * 2; + uint32_t* data = malloc(len); + if (!data) { + continue; + } + STORE_32(i, 0, data); + STORE_32(extdata->data[i].size, sizeof(uint32_t), data); + compress((Bytef*) (data + 2), &len, extdata->data[i].data, extdata->data[i].size); + PNGWriteCustomChunk(png, "gbAx", len + sizeof(uint32_t) * 2, data); + free(data); + } + } PNGWriteClose(png, info); free(buffer); return true; } static int _loadPNGChunkHandler(png_structp png, png_unknown_chunkp chunk) { - if (strcmp((const char*) chunk->name, "gbAs") != 0) { + struct GBABundledState* bundle = png_get_user_chunk_ptr(png); + if (!bundle) { return 0; } - struct GBASerializedState* state = png_get_user_chunk_ptr(png); - uLongf len = sizeof(*state); - uncompress((Bytef*) state, &len, chunk->data, chunk->size); - return 1; + if (!strcmp((const char*) chunk->name, "gbAs")) { + struct GBASerializedState* state = bundle->state; + if (!state) { + return 0; + } + uLongf len = sizeof(*state); + uncompress((Bytef*) state, &len, chunk->data, chunk->size); + return 1; + } + if (!strcmp((const char*) chunk->name, "gbAx")) { + struct GBAExtdata* extdata = bundle->extdata; + if (!extdata) { + return 0; + } + struct GBAExtdataItem item; + if (chunk->size < sizeof(uint32_t) * 2) { + return 0; + } + uint32_t tag; + LOAD_32(tag, 0, chunk->data); + LOAD_32(item.size, sizeof(uint32_t), chunk->data); + uLongf len = item.size; + if (item.size < 0) { + return 0; + } + item.data = malloc(item.size); + item.clean = free; + if (!item.data) { + return 0; + } + const uint8_t* data = chunk->data; + data += sizeof(uint32_t) * 2; + uncompress((Bytef*) item.data, &len, data, chunk->size); + item.size = len; + GBAExtdataPut(extdata, tag, &item); + return 1; + } + return 0; } static struct GBASerializedState* _loadPNGState(struct VFile* vf, struct GBAExtdata* extdata) {

@@ -249,8 +313,12 @@ return false;

} struct GBASerializedState* state = GBAAllocateState(); + struct GBABundledState bundle = { + .state = state, + .extdata = extdata + }; - PNGInstallChunkHandler(png, state, _loadPNGChunkHandler, "gbAs"); + PNGInstallChunkHandler(png, &bundle, _loadPNGChunkHandler, "gbAs gbAx"); bool success = PNGReadHeader(png, info); success = success && PNGReadPixels(png, info, pixels, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, VIDEO_HORIZONTAL_PIXELS); success = success && PNGReadFooter(png, end);

@@ -272,13 +340,12 @@ return state;

} #endif -#ifndef _3DS -bool GBASaveState(struct GBAThread* threadContext, struct VDir* dir, int slot, bool screenshot) { +bool GBASaveState(struct GBAThread* threadContext, struct VDir* dir, int slot, int flags) { struct VFile* vf = GBAGetState(threadContext->gba, dir, slot, true); if (!vf) { return false; } - bool success = GBASaveStateNamed(threadContext->gba, vf, screenshot); + bool success = GBASaveStateNamed(threadContext->gba, vf, flags); vf->close(vf); if (success) { #if SAVESTATE_DEBUG

@@ -288,7 +355,7 @@ struct GBA* backup = anonymousMemoryMap(sizeof(*backup));

memcpy(backup, threadContext->gba, sizeof(*backup)); memset(threadContext->gba->memory.io, 0, sizeof(threadContext->gba->memory.io)); memset(threadContext->gba->timers, 0, sizeof(threadContext->gba->timers)); - GBALoadStateNamed(threadContext->gba, vf); + GBALoadStateNamed(threadContext->gba, vf, flags); if (memcmp(backup, threadContext->gba, sizeof(*backup))) { char suffix[16] = { '\0' }; struct VFile* vf2;

@@ -317,13 +384,13 @@

return success; } -bool GBALoadState(struct GBAThread* threadContext, struct VDir* dir, int slot) { +bool GBALoadState(struct GBAThread* threadContext, struct VDir* dir, int slot, int flags) { struct VFile* vf = GBAGetState(threadContext->gba, dir, slot, false); if (!vf) { return false; } threadContext->rewindBufferSize = 0; - bool success = GBALoadStateNamed(threadContext->gba, vf); + bool success = GBALoadStateNamed(threadContext->gba, vf, flags); vf->close(vf); if (success) { GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i loaded", slot);

@@ -332,28 +399,52 @@ GBALog(threadContext->gba, GBA_LOG_STATUS, "State %i failed to load", slot);

} return success; } -#endif -bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, bool screenshot) { +bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, int flags) { + struct GBAExtdata extdata; + GBAExtdataInit(&extdata); + if (flags & SAVESTATE_SAVEDATA) { + // TODO: A better way to do this would be nice + void* sram = malloc(SIZE_CART_FLASH1M); + struct VFile* svf = VFileFromMemory(sram, SIZE_CART_FLASH1M); + if (GBASavedataClone(&gba->memory.savedata, svf)) { + struct GBAExtdataItem item = { + .size = svf->seek(svf, 0, SEEK_CUR), + .data = sram, + .clean = free + }; + GBAExtdataPut(&extdata, EXTDATA_SAVEDATA, &item); + } else { + free(sram); + } + svf->close(svf); + } #ifdef USE_PNG - if (!screenshot) { + if (!(flags & SAVESTATE_SCREENSHOT)) { #else - UNUSED(screenshot); + UNUSED(flags); #endif vf->truncate(vf, sizeof(struct GBASerializedState)); struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE); if (!state) { + GBAExtdataDeinit(&extdata); return false; } GBASerialize(gba, state); vf->unmap(vf, state, sizeof(struct GBASerializedState)); + vf->seek(vf, sizeof(struct GBASerializedState), SEEK_SET); + GBAExtdataSerialize(&extdata, vf); + GBAExtdataDeinit(&extdata); return true; #ifdef USE_PNG } else { - return _savePNGState(gba, vf); + bool success = _savePNGState(gba, vf, &extdata); + GBAExtdataDeinit(&extdata); + return success; } #endif + GBAExtdataDeinit(&extdata); return false; }

@@ -370,11 +461,14 @@ struct GBASerializedState* state = GBAAllocateState();

if (vf->read(vf, state, sizeof(*state)) != sizeof(*state)) { GBADeallocateState(state); return 0; + } + if (extdata) { + GBAExtdataDeserialize(extdata, vf); } return state; } -bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf) { +bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf, int flags) { struct GBAExtdata extdata; GBAExtdataInit(&extdata); struct GBASerializedState* state = GBAExtractState(vf, &extdata);

@@ -384,10 +478,21 @@ }

bool success = GBADeserialize(gba, state); GBADeallocateState(state); - struct GBAExtdataItem screenshot; - if (GBAExtdataGet(&extdata, EXTDATA_SCREENSHOT, &screenshot)) { - gba->video.renderer->putPixels(gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, screenshot.data); - GBASyncForceFrame(gba->sync); + struct GBAExtdataItem item; + if (flags & SAVESTATE_SCREENSHOT && GBAExtdataGet(&extdata, EXTDATA_SCREENSHOT, &item)) { + if (item.size >= VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4) { + gba->video.renderer->putPixels(gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, item.data); + GBASyncForceFrame(gba->sync); + } else { + GBALog(gba, GBA_LOG_WARN, "Savestate includes invalid screenshot"); + } + } + if (flags & SAVESTATE_SAVEDATA && GBAExtdataGet(&extdata, EXTDATA_SAVEDATA, &item)) { + struct VFile* svf = VFileFromMemory(item.data, item.size); + if (svf) { + GBASavedataLoad(&gba->memory.savedata, svf); + svf->close(svf); + } } GBAExtdataDeinit(&extdata); return success;

@@ -424,6 +529,89 @@ return false;

} *item = extdata->data[tag]; + return true; +} + +bool GBAExtdataSerialize(struct GBAExtdata* extdata, struct VFile* vf) { + ssize_t position = vf->seek(vf, 0, SEEK_CUR); + ssize_t size = 2; + size_t i = 0; + for (i = 1; i < EXTDATA_MAX; ++i) { + if (extdata->data[i].data) { + size += sizeof(uint64_t) * 2; + } + } + if (size == 2) { + return true; + } + struct GBAExtdataHeader* header = malloc(size); + position += size; + + size_t j; + for (i = 1, j = 0; i < EXTDATA_MAX; ++i) { + if (extdata->data[i].data) { + STORE_32(i, offsetof(struct GBAExtdataHeader, tag), &header[j]); + STORE_32(extdata->data[i].size, offsetof(struct GBAExtdataHeader, size), &header[j]); + STORE_64(position, offsetof(struct GBAExtdataHeader, offset), &header[j]); + position += extdata->data[i].size; + ++j; + } + } + header[j].tag = 0; + header[j].size = 0; + header[j].offset = 0; + + if (vf->write(vf, header, size) != size) { + free(header); + return false; + } + free(header); + + for (i = 1; i < EXTDATA_MAX; ++i) { + if (extdata->data[i].data) { + if (vf->write(vf, extdata->data[i].data, extdata->data[i].size) != extdata->data[i].size) { + return false; + } + } + } + return true; +} + +bool GBAExtdataDeserialize(struct GBAExtdata* extdata, struct VFile* vf) { + while (true) { + struct GBAExtdataHeader buffer, header; + if (vf->read(vf, &buffer, sizeof(buffer)) != sizeof(buffer)) { + return false; + } + LOAD_32(header.tag, 0, &buffer.tag); + LOAD_32(header.size, 0, &buffer.size); + LOAD_64(header.offset, 0, &buffer.offset); + + if (header.tag == EXTDATA_NONE) { + break; + } + if (header.tag >= EXTDATA_MAX) { + continue; + } + ssize_t position = vf->seek(vf, 0, SEEK_CUR); + if (vf->seek(vf, header.offset, SEEK_SET) < 0) { + return false; + } + struct GBAExtdataItem item = { + .data = malloc(header.size), + .size = header.size, + .clean = free + }; + if (!item.data) { + continue; + } + if (vf->read(vf, item.data, header.size) != header.size) { + free(item.data); + continue; + } + GBAExtdataPut(extdata, header.tag, &item); + vf->seek(vf, position, SEEK_SET); + }; return true; }
M src/gba/serialize.hsrc/gba/serialize.h

@@ -333,12 +333,16 @@

enum GBAExtdataTag { EXTDATA_NONE = 0, EXTDATA_SCREENSHOT = 1, + EXTDATA_SAVEDATA = 2, EXTDATA_MAX }; +#define SAVESTATE_SCREENSHOT 1 +#define SAVESTATE_SAVEDATA 2 + struct GBAExtdata; struct GBAExtdataItem { - uint64_t size; + int32_t size; void* data; void (*clean)(void*); };

@@ -349,17 +353,19 @@

void GBASerialize(struct GBA* gba, struct GBASerializedState* state); bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state); -bool GBASaveState(struct GBAThread* thread, struct VDir* dir, int slot, bool screenshot); -bool GBALoadState(struct GBAThread* thread, struct VDir* dir, int slot); +bool GBASaveState(struct GBAThread* thread, struct VDir* dir, int slot, int flags); +bool GBALoadState(struct GBAThread* thread, struct VDir* dir, int slot, int flags); struct VFile* GBAGetState(struct GBA* gba, struct VDir* dir, int slot, bool write); -bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, bool screenshot); -bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf); +bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, int flags); +bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf, int flags); bool GBAExtdataInit(struct GBAExtdata*); void GBAExtdataDeinit(struct GBAExtdata*); void GBAExtdataPut(struct GBAExtdata*, enum GBAExtdataTag, struct GBAExtdataItem*); bool GBAExtdataGet(struct GBAExtdata*, enum GBAExtdataTag, struct GBAExtdataItem*); +bool GBAExtdataSerialize(struct GBAExtdata* extpdata, struct VFile* vf); +bool GBAExtdataDeserialize(struct GBAExtdata* extdata, struct VFile* vf); struct GBASerializedState* GBAExtractState(struct VFile* vf, struct GBAExtdata* extdata); struct GBASerializedState* GBAAllocateState(void);
M src/gba/supervisor/cli.csrc/gba/supervisor/cli.c

@@ -106,7 +106,7 @@ }

struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system; - GBALoadState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue); + GBALoadState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue, SAVESTATE_SCREENSHOT); } static void _rewind(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {

@@ -133,6 +133,6 @@ }

struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system; - GBASaveState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue, true); + GBASaveState(gbaDebugger->context, gbaDebugger->context->stateDir, dv->intValue, SAVESTATE_SCREENSHOT); } #endif
M src/platform/qt/GameController.cppsrc/platform/qt/GameController.cpp

@@ -108,7 +108,7 @@ context->gba->video.renderer->disableBG[3] = !controller->m_videoLayers[3];

context->gba->video.renderer->disableOBJ = !controller->m_videoLayers[4]; controller->m_fpsTarget = context->fpsTarget; - if (GBALoadState(context, context->stateDir, 0)) { + if (GBALoadState(context, context->stateDir, 0, SAVESTATE_SCREENSHOT)) { VFile* vf = GBAGetState(context->gba, context->stateDir, 0, true); if (vf) { vf->truncate(vf, 0);

@@ -701,7 +701,7 @@ if (!controller->m_backupLoadState) {

controller->m_backupLoadState = new GBASerializedState; } GBASerialize(context->gba, controller->m_backupLoadState); - if (GBALoadState(context, context->stateDir, controller->m_stateSlot)) { + if (GBALoadState(context, context->stateDir, controller->m_stateSlot, SAVESTATE_SCREENSHOT)) { controller->frameAvailable(controller->m_drawContext); controller->stateLoaded(context); }

@@ -720,7 +720,7 @@ controller->m_backupSaveState.resize(vf->size(vf));

vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size()); vf->close(vf); } - GBASaveState(context, context->stateDir, controller->m_stateSlot, true); + GBASaveState(context, context->stateDir, controller->m_stateSlot, SAVESTATE_SCREENSHOT | EXTDATA_SAVEDATA); }); }
M src/platform/sdl/sdl-events.csrc/platform/sdl/sdl-events.c

@@ -423,7 +423,7 @@ case SDLK_F7:

case SDLK_F8: case SDLK_F9: GBAThreadInterrupt(context); - GBASaveState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1, true); + GBASaveState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1, SAVESTATE_SCREENSHOT); GBAThreadContinue(context); break; default:

@@ -441,7 +441,7 @@ case SDLK_F7:

case SDLK_F8: case SDLK_F9: GBAThreadInterrupt(context); - GBALoadState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1); + GBALoadState(context, context->stateDir, event->keysym.sym - SDLK_F1 + 1, SAVESTATE_SCREENSHOT); GBAThreadContinue(context); break; default:
M src/platform/test/fuzz-main.csrc/platform/test/fuzz-main.c

@@ -112,7 +112,7 @@ free(fuzzOpts.ssOverlay);

} if (savestate) { if (!savestateOverlay) { - GBALoadStateNamed(context.gba, savestate); + GBALoadStateNamed(context.gba, savestate, 0); } else { struct GBASerializedState* state = GBAAllocateState(); savestate->read(savestate, state, sizeof(*state));
M src/platform/test/perf-main.csrc/platform/test/perf-main.c

@@ -238,7 +238,7 @@ }

} static void _loadSavestate(struct GBAThread* context) { - GBALoadStateNamed(context->gba, _savestate); + GBALoadStateNamed(context->gba, _savestate, 0); _savestate->close(_savestate); _savestate = 0; }
M src/util/common.hsrc/util/common.h

@@ -73,18 +73,25 @@ uint32_t _addr = (ADDR); \

void* _ptr = (ARR); \ __asm__("sthbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr)); \ } + +#define LOAD_64LE(DEST, ADDR, ARR) DEST = __builtin_bswap64(((uint64_t*) ARR)[(ADDR) >> 3]) +#define STORE_64LE(SRC, ADDR, ARR) ((uint64_t*) ARR)[(ADDR) >> 3] = __builtin_bswap64(SRC) #elif defined __BIG_ENDIAN__ #if defined(__llvm__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) +#define LOAD_64LE(DEST, ADDR, ARR) DEST = __builtin_bswap64(((uint64_t*) ARR)[(ADDR) >> 3]) #define LOAD_32LE(DEST, ADDR, ARR) DEST = __builtin_bswap32(((uint32_t*) ARR)[(ADDR) >> 2]) #define LOAD_16LE(DEST, ADDR, ARR) DEST = __builtin_bswap16(((uint16_t*) ARR)[(ADDR) >> 1]) +#define STORE_64LE(SRC, ADDR, ARR) ((uint64_t*) ARR)[(ADDR) >> 3] = __builtin_bswap64(SRC) #define STORE_32LE(SRC, ADDR, ARR) ((uint32_t*) ARR)[(ADDR) >> 2] = __builtin_bswap32(SRC) #define STORE_16LE(SRC, ADDR, ARR) ((uint16_t*) ARR)[(ADDR) >> 1] = __builtin_bswap16(SRC) #else #error Big endian build not supported on this platform. #endif #else +#define LOAD_64LE(DEST, ADDR, ARR) DEST = ((uint64_t*) ARR)[(ADDR) >> 3] #define LOAD_32LE(DEST, ADDR, ARR) DEST = ((uint32_t*) ARR)[(ADDR) >> 2] #define LOAD_16LE(DEST, ADDR, ARR) DEST = ((uint16_t*) ARR)[(ADDR) >> 1] +#define STORE_64LE(SRC, ADDR, ARR) ((uint64_t*) ARR)[(ADDR) >> 3] = SRC #define STORE_32LE(SRC, ADDR, ARR) ((uint32_t*) ARR)[(ADDR) >> 2] = SRC #define STORE_16LE(SRC, ADDR, ARR) ((uint16_t*) ARR)[(ADDR) >> 1] = SRC #endif
M src/util/png-io.csrc/util/png-io.c

@@ -126,7 +126,16 @@ if (setjmp(png_jmpbuf(png))) {

return false; } png_set_read_user_chunk_fn(png, context, handler); - png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_bytep) chunkName, 1); + int len = strlen(chunkName); + int chunks = 0; + char* chunkList = strdup(chunkName); + int i; + for (i = 4; i <= len; i += 5) { + chunkList[i] = '\0'; + ++chunks; + } + png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_bytep) chunkList, chunks); + free(chunkList); return true; }