Code for embedding savestates in PNG chunk, loading not yet implemented

This commit is contained in:
Jeffrey Pfau 2014-07-26 00:04:24 -07:00
parent deb278dc22
commit 89bb9c7eac
3 changed files with 55 additions and 18 deletions

View File

@ -3,11 +3,14 @@
#include "gba-audio.h"
#include "gba-io.h"
#include "gba-thread.h"
#include "gba-video.h"
#include "util/memory.h"
#include "util/png-io.h"
#include "util/vfs.h"
#include <fcntl.h>
#include <zlib.h>
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
@ -68,31 +71,65 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) {
GBAAudioDeserialize(&gba->audio, state);
}
static struct VFile* _getStateVf(struct GBA* gba, int slot, bool write) {
static struct VFile* _getStateVf(struct GBA* gba, struct VDir* dir, int slot, bool write) {
char path[PATH_MAX];
path[PATH_MAX - 1] = '\0';
struct VFile* vf;
if (!dir) {
snprintf(path, PATH_MAX - 1, "%s.ss%d", gba->activeFile, slot);
struct VFile* vf = VFileOpen(path, write ? (O_CREAT | O_RDWR) : O_RDONLY);
if (vf) {
vf->truncate(vf, sizeof(struct GBASerializedState));
vf = VFileOpen(path, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
} else {
snprintf(path, PATH_MAX - 1, "savestate.ss%d", slot);
vf = dir->openFile(dir, path, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
}
return vf;
}
bool GBASaveState(struct GBA* gba, int slot) {
struct VFile* vf = _getStateVf(gba, slot, true);
static bool _savePNGState(struct GBA* gba, struct VFile* vf) {
unsigned stride;
void* pixels = 0;
gba->video.renderer->getPixels(gba->video.renderer, &stride, &pixels);
if (!pixels) {
return false;
}
struct GBASerializedState* state = GBAAllocateState();
png_structp png = PNGWriteOpen(vf);
png_infop info = PNGWriteHeader(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
uLongf len = compressBound(sizeof(*state));
void* buffer = malloc(len);
if (state && png && info && buffer) {
GBASerialize(gba, state);
compress(buffer, &len, (const Bytef*) state, sizeof(*state));
PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, stride, pixels);
PNGWriteCustomChunk(png, "gbAs", len, buffer);
}
PNGWriteClose(png, info);
free(buffer);
GBADeallocateState(state);
return state && png && info && buffer;
}
bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot) {
struct VFile* vf = _getStateVf(gba, dir, slot, true);
if (!vf) {
return false;
}
bool success = true;
if (!screenshot) {
vf->truncate(vf, sizeof(struct GBASerializedState));
struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE);
GBASerialize(gba, state);
vf->unmap(vf, state, sizeof(struct GBASerializedState));
} else {
_savePNGState(gba, vf);
}
vf->close(vf);
return true;
return success;
}
bool GBALoadState(struct GBA* gba, int slot) {
struct VFile* vf = _getStateVf(gba, slot, false);
bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot) {
struct VFile* vf = _getStateVf(gba, dir, slot, false);
if (!vf) {
return false;
}

View File

@ -258,13 +258,13 @@ struct GBASerializedState {
uint8_t wram[SIZE_WORKING_RAM];
};
struct VFile;
struct VDir;
void GBASerialize(struct GBA* gba, struct GBASerializedState* state);
void GBADeserialize(struct GBA* gba, struct GBASerializedState* state);
bool GBASaveState(struct GBA* gba, int slot);
bool GBALoadState(struct GBA* gba, int slot);
bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot);
bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot);
struct GBASerializedState* GBAAllocateState(void);
void GBADeallocateState(struct GBASerializedState* state);

View File

@ -163,7 +163,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
case SDLK_F9:
case SDLK_F10:
GBAThreadInterrupt(context);
GBASaveState(context->gba, event->keysym.sym - SDLK_F1);
GBASaveState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1, false);
GBAThreadContinue(context);
break;
default:
@ -182,7 +182,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
case SDLK_F9:
case SDLK_F10:
GBAThreadInterrupt(context);
GBALoadState(context->gba, event->keysym.sym - SDLK_F1);
GBALoadState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1);
GBAThreadContinue(context);
break;
default: