diff --git a/CHANGES b/CHANGES index aaee74ee6..60b6794a5 100644 --- a/CHANGES +++ b/CHANGES @@ -51,6 +51,7 @@ Misc: - GBA RR: Starting from savestate now embeds the savegame - Libretro: Add install target for libretro core - 3DS: Update to new ctrulib API + - GBA RR: Add preliminary SRAM support for VBM loading 0.3.2: (2015-12-16) Bugfixes: diff --git a/src/gba/rr/vbm.c b/src/gba/rr/vbm.c index 5d452487b..71deb345b 100644 --- a/src/gba/rr/vbm.c +++ b/src/gba/rr/vbm.c @@ -9,6 +9,10 @@ #include "gba/serialize.h" #include "util/vfs.h" +#ifdef USE_ZLIB +#include +#endif + static const char VBM_MAGIC[] = "VBM\x1A"; static void GBAVBMContextDestroy(struct GBARRContext*); @@ -125,9 +129,72 @@ void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* } struct VFile* GBAVBMOpenSavedata(struct GBARRContext* rr, int flags) { - UNUSED(rr); UNUSED(flags); +#ifndef USE_ZLIB + UNUSED(rr); return 0; +#else + struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr; + off_t pos = vbm->vbmFile->seek(vbm->vbmFile, 0, SEEK_CUR); + uint32_t saveType, flashSize, sramOffset; + vbm->vbmFile->seek(vbm->vbmFile, 0x18, SEEK_SET); + vbm->vbmFile->read(vbm->vbmFile, &saveType, sizeof(saveType)); + vbm->vbmFile->read(vbm->vbmFile, &flashSize, sizeof(flashSize)); + vbm->vbmFile->seek(vbm->vbmFile, 0x38, SEEK_SET); + vbm->vbmFile->read(vbm->vbmFile, &sramOffset, sizeof(sramOffset)); + if (!sramOffset) { + vbm->vbmFile->seek(vbm->vbmFile, pos, SEEK_SET); + return 0; + } + vbm->vbmFile->seek(vbm->vbmFile, sramOffset, SEEK_SET); + struct VFile* save = VFileMemChunk(0, 0); + size_t size; + switch (saveType) { + case 1: + size = SIZE_CART_SRAM; + break; + case 2: + size = flashSize; + if (size > SIZE_CART_FLASH1M) { + size = SIZE_CART_FLASH1M; + } + break; + case 3: + size = SIZE_CART_EEPROM; + break; + default: + size = SIZE_CART_FLASH1M; + break; + } + uLong zlen = vbm->inputOffset - sramOffset; + char buffer[8761]; + char* zbuffer = malloc(zlen); + vbm->vbmFile->read(vbm->vbmFile, zbuffer, zlen); + z_stream zstr; + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + zstr.avail_in = zlen; + zstr.next_in = (Bytef*) zbuffer; + zstr.avail_out = 0; + inflateInit2(&zstr, 31); + // Skip header, we know where the save file is + zstr.avail_out = sizeof(buffer); + zstr.next_out = (Bytef*) &buffer; + int err = inflate(&zstr, 0); + while (err != Z_STREAM_END && !zstr.avail_out) { + zstr.avail_out = sizeof(buffer); + zstr.next_out = (Bytef*) &buffer; + int err = inflate(&zstr, 0); + if (err < 0) { + break; + } + save->write(save, buffer, sizeof(buffer) - zstr.avail_out); + } + inflateEnd(&zstr); + vbm->vbmFile->seek(vbm->vbmFile, pos, SEEK_SET); + return save; +#endif } struct VFile* GBAVBMOpenSavestate(struct GBARRContext* rr, int flags) { @@ -163,14 +230,18 @@ bool GBAVBMSetStream(struct GBAVBMContext* vbm, struct VFile* vf) { uint8_t flags; vf->read(vf, &flags, sizeof(flags)); + if (flags & 2) { +#if USE_ZLIB + vbm->d.initFrom = INIT_FROM_SAVEGAME; +#else + // zlib is needed to parse the savegame + return false; +#endif + } if (flags & 1) { // Incompatible savestate format return false; } - if (flags & 2) { - // TODO: Implement SRAM loading - return false; - } vf->seek(vf, 1, SEEK_CUR); vf->read(vf, &flags, sizeof(flags));