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-audio.h"
#include "gba-io.h" #include "gba-io.h"
#include "gba-thread.h" #include "gba-thread.h"
#include "gba-video.h"
#include "util/memory.h" #include "util/memory.h"
#include "util/png-io.h"
#include "util/vfs.h" #include "util/vfs.h"
#include <fcntl.h> #include <fcntl.h>
#include <zlib.h>
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
@ -68,31 +71,65 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) {
GBAAudioDeserialize(&gba->audio, 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]; char path[PATH_MAX];
path[PATH_MAX - 1] = '\0'; path[PATH_MAX - 1] = '\0';
struct VFile* vf;
if (!dir) {
snprintf(path, PATH_MAX - 1, "%s.ss%d", gba->activeFile, slot); snprintf(path, PATH_MAX - 1, "%s.ss%d", gba->activeFile, slot);
struct VFile* vf = VFileOpen(path, write ? (O_CREAT | O_RDWR) : O_RDONLY); vf = VFileOpen(path, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
if (vf) { } else {
vf->truncate(vf, sizeof(struct GBASerializedState)); 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; return vf;
} }
bool GBASaveState(struct GBA* gba, int slot) { static bool _savePNGState(struct GBA* gba, struct VFile* vf) {
struct VFile* vf = _getStateVf(gba, slot, true); 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) { if (!vf) {
return false; return false;
} }
bool success = true;
if (!screenshot) {
vf->truncate(vf, sizeof(struct GBASerializedState));
struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE); struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE);
GBASerialize(gba, state); GBASerialize(gba, state);
vf->unmap(vf, state, sizeof(struct GBASerializedState)); vf->unmap(vf, state, sizeof(struct GBASerializedState));
} else {
_savePNGState(gba, vf);
}
vf->close(vf); vf->close(vf);
return true; return success;
} }
bool GBALoadState(struct GBA* gba, int slot) { bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot) {
struct VFile* vf = _getStateVf(gba, slot, false); struct VFile* vf = _getStateVf(gba, dir, slot, false);
if (!vf) { if (!vf) {
return false; return false;
} }

View File

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

View File

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