diff --git a/src/drivers/libretro/fceu/fceu-endian.c b/src/drivers/libretro/fceu/fceu-endian.c index feb5e1f..cb52ec3 100644 --- a/src/drivers/libretro/fceu/fceu-endian.c +++ b/src/drivers/libretro/fceu/fceu-endian.c @@ -77,6 +77,29 @@ int read32le(uint32 *Bufo, FILE *fp) return 1; } +int write32le_mem(uint32 b, memstream_t *mem) +{ + uint8 s[4]; + s[0]=b; + s[1]=b>>8; + s[2]=b>>16; + s[3]=b>>24; + return((memstream_write(mem, s, 4)<4)?0:4); +} + +int read32le_mem(uint32 *Bufo, memstream_t *mem) +{ + uint32 buf; + if(memstream_read(mem, &buf, 4)<4) + return 0; +#ifdef LSB_FIRST + *(uint32*)Bufo=buf; +#else + *(uint32*)Bufo=((buf&0xFF)<<24)|((buf&0xFF00)<<8)|((buf&0xFF0000)>>8)|((buf&0xFF000000)>>24); +#endif + return 1; +} + int read16le(char *d, FILE *fp) { #ifdef LSB_FIRST diff --git a/src/drivers/libretro/fceu/fceu-endian.h b/src/drivers/libretro/fceu/fceu-endian.h index 00d2a3d..333e679 100644 --- a/src/drivers/libretro/fceu/fceu-endian.h +++ b/src/drivers/libretro/fceu/fceu-endian.h @@ -2,6 +2,10 @@ #define _FCEU_ENDIAN_H #include "fceu-memory.h" +#include "memstream.h" + +int write32le_mem(uint32 b, memstream_t *mem); +int read32le_mem(uint32 *Bufo, memstream_t *mem); int write16le(uint16 b, FILE *fp); int write32le(uint32 b, FILE *fp); diff --git a/src/drivers/libretro/fceu/state.c b/src/drivers/libretro/fceu/state.c index 805c177..057af7a 100644 --- a/src/drivers/libretro/fceu/state.c +++ b/src/drivers/libretro/fceu/state.c @@ -81,6 +81,49 @@ SFORMAT SFCPUC[] = { { 0 } }; +static int SubWrite_Mem(memstream_t *mem, SFORMAT *sf) +{ + uint32 acc=0; + + while(sf->v) + { + if(sf->s==~0) /* Link to another struct. */ + { + uint32 tmp; + + if(!(tmp=SubWrite_Mem(mem, (SFORMAT *)sf->v))) + return(0); + acc+=tmp; + sf++; + continue; + } + + acc+=8; /* Description + size */ + acc+=sf->s&(~RLSB); + + if(mem) /* Are we writing or calculating the size of this block? */ + { + memstream_write(mem, sf->desc, 4); + write32le_mem(sf->s&(~RLSB), mem); + +#ifndef LSB_FIRST + if(sf->s&RLSB) + FlipByteOrder(sf->v,sf->s&(~RLSB)); +#endif + + memstream_write(mem, (uint8 *)sf->v, sf->s&(~RLSB)); + /* Now restore the original byte order. */ +#ifndef LSB_FIRST + if(sf->s&RLSB) + FlipByteOrder(sf->v,sf->s&(~RLSB)); +#endif + } + sf++; + } + + return acc; +} + static int SubWrite(MEM_TYPE *st, SFORMAT *sf) { uint32 acc = 0; @@ -134,6 +177,20 @@ static int WriteStateChunk(MEM_TYPE *st, int type, SFORMAT *sf) return(bsize + 5); } +static int WriteStateChunk_Mem(memstream_t *mem, int type, SFORMAT *sf) +{ + int bsize; + + memstream_putc(mem, type); + + bsize = SubWrite(0,sf); + write32le_mem(bsize, mem); + + if (!SubWrite_Mem(mem, sf)) + return 0; + return bsize + 5; +} + static SFORMAT *CheckS(SFORMAT *sf, uint32 tsize, char *desc) { while (sf->v) @@ -157,6 +214,36 @@ static SFORMAT *CheckS(SFORMAT *sf, uint32 tsize, char *desc) return(0); } +static int ReadStateChunk_Mem(memstream_t *mem, SFORMAT *sf, int size) +{ + SFORMAT *tmp; + int temp; + temp=memstream_pos(mem); + + while(memstream_pos(mem)v, tmp->s&(~RLSB)); + +#ifndef LSB_FIRST + if(tmp->s&RLSB) + FlipByteOrder(tmp->v,tmp->s&(~RLSB)); +#endif + } + else + memstream_seek(mem,tsize,SEEK_CUR); + } + return 1; +} + static int ReadStateChunk(MEM_TYPE *st, SFORMAT *sf, int size) { SFORMAT *tmp; @@ -186,6 +273,59 @@ static int ReadStateChunk(MEM_TYPE *st, SFORMAT *sf, int size) return 1; } +static int ReadStateChunks_Mem(memstream_t *st, int32 totalsize) +{ + int t; + uint32 size; + int ret = 1; + + while (totalsize > 0) + { + t = memstream_getc(st); + if (t == EOF) + break; + if (!read32le_mem(&size,st)) + break; + totalsize -= size + 5; + + switch(t) + { + case 1: + if (!ReadStateChunk_Mem(st, SFCPU, size)) + ret = 0; + break; + case 2: + if (!ReadStateChunk_Mem(st, SFCPUC, size)) + ret = 0; + else + X.mooPI = X.P; // Quick and dirty hack. + break; + case 3: + if (!ReadStateChunk_Mem(st, FCEUPPU_STATEINFO, size)) + ret = 0; + break; + case 4: + if (!ReadStateChunk_Mem(st, FCEUCTRL_STATEINFO, size)) + ret = 0; + break; + case 5: + if (!ReadStateChunk_Mem(st, FCEUSND_STATEINFO, size)) + ret = 0; + break; + case 0x10: + if (!ReadStateChunk_Mem(st, SFMDATA, size)) + ret = 0; + break; + default: + if (memstream_seek(st, size, SEEK_CUR) < 0) + goto endo; + break; + } + } +endo: + return ret; +} + static int ReadStateChunks(MEM_TYPE *st, int32 totalsize) { int t; @@ -274,6 +414,39 @@ int FCEUSS_SaveFP(MEM_TYPE *st) return(1); } +void FCEUSS_Save_Mem(void) +{ + memstream_t *mem = memstream_open(1); + + static uint32 totalsize; + static uint8 header[16] = "FCS"; + + memset(header + 4, 0, 12); + header[3] = 0xFF; + FCEU_en32lsb(header + 8, FCEU_VERSION_NUMERIC); + memstream_write(mem, header, 16); + + FCEUPPU_SaveState(); + totalsize = WriteStateChunk_Mem(mem, 1, SFCPU); + totalsize += WriteStateChunk_Mem(mem, 2, SFCPUC); + totalsize += WriteStateChunk_Mem(mem, 3, FCEUPPU_STATEINFO); + totalsize += WriteStateChunk_Mem(mem, 4, FCEUCTRL_STATEINFO); + totalsize += WriteStateChunk_Mem(mem, 5, FCEUSND_STATEINFO); + + if (SPreSave) + SPreSave(); + + totalsize += WriteStateChunk_Mem(mem, 0x10, SFMDATA); + + if (SPreSave) + SPostSave(); + + memstream_seek(mem, 4, SEEK_SET); + write32le_mem(totalsize, mem); + + memstream_close(mem); +} + void FCEUSS_Save(char *fname) { MEM_TYPE *st = NULL; @@ -322,6 +495,40 @@ int FCEUSS_LoadFP(MEM_TYPE *st) return(x); } +void FCEUSS_Load_Mem(void) +{ + memstream_t *mem = memstream_open(0); + + uint8 header[16]; + int stateversion; + + memstream_read(mem, header, 16); + + if (memcmp(header,"FCS",3) != 0) + return; + + if (header[3] == 0xFF) + stateversion = FCEU_de32lsb(header + 8); + else + stateversion = header[3] * 100; + + int x = ReadStateChunks_Mem(mem, *(uint32*)(header + 4)); + + if (stateversion < 9500) + X.IRQlow=0; + + if (GameStateRestore) + GameStateRestore(stateversion); + + if (x) + { + FCEUPPU_LoadState(stateversion); + FCEUSND_LoadState(stateversion); + } + + memstream_close(mem); +} + int FCEUSS_Load(char *fname) { MEM_TYPE *st; diff --git a/src/drivers/libretro/libretro.c b/src/drivers/libretro/libretro.c index 2b65a2e..100cabc 100644 --- a/src/drivers/libretro/libretro.c +++ b/src/drivers/libretro/libretro.c @@ -768,7 +768,7 @@ size_t retro_serialize_size(void) uint8_t *buffer = (uint8_t*)malloc(1000000); memstream_set_buffer(buffer, 1000000); - FCEUSS_Save(""); + FCEUSS_Save_Mem(); serialize_size = memstream_get_last_size(); free(buffer); } @@ -782,7 +782,7 @@ bool retro_serialize(void *data, size_t size) return false; memstream_set_buffer((uint8_t*)data, size); - FCEUSS_Save(""); + FCEUSS_Save_Mem(); return true; } @@ -792,7 +792,7 @@ bool retro_unserialize(const void * data, size_t size) return false; memstream_set_buffer((uint8_t*)data, size); - FCEUSS_Load(""); + FCEUSS_Load_Mem(); return true; } diff --git a/src/state.h b/src/state.h index 5d9182d..c32c7ef 100644 --- a/src/state.h +++ b/src/state.h @@ -28,6 +28,9 @@ int FCEUSS_Load(char *); int FCEUSS_SaveFP(MEM_TYPE *); int FCEUSS_LoadFP(MEM_TYPE *); +void FCEUSS_Load_Mem(void); +void FCEUSS_Save_Mem(void); + extern int CurrentState; void FCEUSS_CheckStates(void);