mirror of https://github.com/mgba-emu/mgba.git
Code for embedding savestates in PNG chunk, loading not yet implemented
This commit is contained in:
parent
deb278dc22
commit
89bb9c7eac
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue