mirror of https://github.com/mgba-emu/mgba.git
PNG savestate loading
This commit is contained in:
parent
89bb9c7eac
commit
15ece309b7
|
@ -10,6 +10,7 @@
|
|||
#include "util/vfs.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <png.h>
|
||||
#include <zlib.h>
|
||||
|
||||
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
|
||||
|
@ -110,6 +111,33 @@ static bool _savePNGState(struct GBA* gba, struct VFile* vf) {
|
|||
return state && png && info && buffer;
|
||||
}
|
||||
|
||||
static int _loadPNGChunkHandler(png_structp png, png_unknown_chunkp chunk) {
|
||||
if (strcmp((const char*) chunk->name, "gbAs") != 0) {
|
||||
return 0;
|
||||
}
|
||||
struct GBASerializedState state;
|
||||
uLongf len = sizeof(state);
|
||||
uncompress((Bytef*) &state, &len, chunk->data, chunk->size);
|
||||
GBADeserialize(png_get_user_chunk_ptr(png), &state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool _loadPNGState(struct GBA* gba, struct VFile* vf) {
|
||||
png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
|
||||
png_infop info = png_create_info_struct(png);
|
||||
png_infop end = png_create_info_struct(png);
|
||||
if (!png || !info || !end) {
|
||||
PNGReadClose(png, info, end);
|
||||
return false;
|
||||
}
|
||||
PNGInstallChunkHandler(png, gba, _loadPNGChunkHandler, "gbAs");
|
||||
PNGReadHeader(png, info);
|
||||
PNGIgnorePixels(png, info);
|
||||
PNGReadFooter(png, end);
|
||||
PNGReadClose(png, info, end);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot) {
|
||||
struct VFile* vf = _getStateVf(gba, dir, slot, true);
|
||||
if (!vf) {
|
||||
|
@ -133,9 +161,13 @@ bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot) {
|
|||
if (!vf) {
|
||||
return false;
|
||||
}
|
||||
struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ);
|
||||
GBADeserialize(gba, state);
|
||||
vf->unmap(vf, state, sizeof(struct GBASerializedState));
|
||||
if (!isPNG(vf)) {
|
||||
struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ);
|
||||
GBADeserialize(gba, state);
|
||||
vf->unmap(vf, state, sizeof(struct GBASerializedState));
|
||||
} else {
|
||||
_loadPNGState(gba, vf);
|
||||
}
|
||||
vf->close(vf);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents
|
|||
case SDLK_F9:
|
||||
case SDLK_F10:
|
||||
GBAThreadInterrupt(context);
|
||||
GBASaveState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1, false);
|
||||
GBASaveState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1, true);
|
||||
GBAThreadContinue(context);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -10,6 +10,14 @@ static void _pngWrite(png_structp png, png_bytep buffer, png_size_t size) {
|
|||
}
|
||||
}
|
||||
|
||||
static void _pngRead(png_structp png, png_bytep buffer, png_size_t size) {
|
||||
struct VFile* vf = png_get_io_ptr(png);
|
||||
size_t read = vf->read(vf, buffer, size);
|
||||
if (read != size) {
|
||||
png_error(png, "Could not read PNG");
|
||||
}
|
||||
}
|
||||
|
||||
png_structp PNGWriteOpen(struct VFile* source) {
|
||||
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
|
||||
if (!png) {
|
||||
|
@ -71,6 +79,70 @@ bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* d
|
|||
}
|
||||
|
||||
void PNGWriteClose(png_structp png, png_infop info) {
|
||||
png_write_end(png, info);
|
||||
if (!setjmp(png_jmpbuf(png))) {
|
||||
png_write_end(png, info);
|
||||
}
|
||||
png_destroy_write_struct(&png, &info);
|
||||
}
|
||||
|
||||
bool isPNG(struct VFile* source) {
|
||||
png_byte header[PNG_HEADER_BYTES];
|
||||
source->read(source, header, PNG_HEADER_BYTES);
|
||||
return !png_sig_cmp(header, 0, PNG_HEADER_BYTES);
|
||||
}
|
||||
|
||||
png_structp PNGReadOpen(struct VFile* source, unsigned offset) {
|
||||
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
|
||||
if (!png) {
|
||||
return 0;
|
||||
}
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
png_destroy_read_struct(&png, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
png_set_read_fn(png, source, _pngRead);
|
||||
png_set_sig_bytes(png, offset);
|
||||
return png;
|
||||
}
|
||||
|
||||
bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName) {
|
||||
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_const_bytep) chunkName, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PNGReadHeader(png_structp png, png_infop info) {
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
return false;
|
||||
}
|
||||
png_read_info(png, info);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PNGIgnorePixels(png_structp png, png_infop info) {
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned height = png_get_image_height(png, info);
|
||||
unsigned i;
|
||||
for (i = 0; i < height; ++i) {
|
||||
png_read_row(png, 0, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PNGReadFooter(png_structp png, png_infop end) {
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
return false;
|
||||
}
|
||||
png_read_end(png, end);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PNGReadClose(png_structp png, png_infop info, png_infop end) {
|
||||
png_destroy_read_struct(&png, &info, &end);
|
||||
}
|
||||
|
|
|
@ -7,10 +7,24 @@
|
|||
|
||||
struct VFile;
|
||||
|
||||
enum {
|
||||
PNG_HEADER_BYTES = 8
|
||||
};
|
||||
|
||||
png_structp PNGWriteOpen(struct VFile* source);
|
||||
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height);
|
||||
bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, void* pixels);
|
||||
bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data);
|
||||
void PNGWriteClose(png_structp png, png_infop info);
|
||||
|
||||
typedef int (*ChunkHandler)(png_structp, png_unknown_chunkp);
|
||||
|
||||
bool isPNG(struct VFile* source);
|
||||
png_structp PNGReadOpen(struct VFile* source, unsigned offset);
|
||||
bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName);
|
||||
bool PNGReadHeader(png_structp png, png_infop info);
|
||||
bool PNGIgnorePixels(png_structp png, png_infop info);
|
||||
bool PNGReadFooter(png_structp png, png_infop end);
|
||||
void PNGReadClose(png_structp png, png_infop info, png_infop end);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue