#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#ifdef GP2X
+#include <unistd.h>
+#endif
#include "types.h"
#include "x6502.h"
#include "memory.h"
#include "ppu.h"
-static SFORMAT SFMDATA[64];
+static void (*SPreSave)(void) = 0;
+static void (*SPostSave)(void) = 0;
+
+#define SFMDATA_SIZE (64)
+static SFORMAT SFMDATA[SFMDATA_SIZE];
static int SFEXINDEX;
static int stateversion;
-#define RLSB 0x80000000
+extern SFORMAT FCEUPPU_STATEINFO[]; // 3
+extern SFORMAT FCEUCTRL_STATEINFO[]; // 4
-#define SFCPUELEMENTS 7
-
-SFORMAT SFCPU[SFCPUELEMENTS]={
+SFORMAT SFCPU[]={ // 1
{ &X.PC, 2|RLSB, "PC\0"},
{ &X.A, 1, "A\0\0"},
{ &X.P, 1, "P\0\0"},
{ &X.X, 1, "X\0\0"},
{ &X.Y, 1, "Y\0\0"},
{ &X.S, 1, "S\0\0"},
- { RAM, 0x800, "RAM"}
+ { RAM, 0x800, "RAM"},
+ { 0, }
};
-#define SFCPUCELEMENTS 6
-SFORMAT SFCPUC[SFCPUCELEMENTS]={
+SFORMAT SFCPUC[]={ // 2
{ &X.jammed, 1, "JAMM"},
{ &X.IRQlow, 1, "IRQL"},
{ &X.tcount, 4|RLSB, "ICoa"},
{ &X.count, 4|RLSB, "ICou"},
{ ×tamp, 4|RLSB, "TIME"},
- { ×tampbase, 8|RLSB, "TMEB"}
+ { ×tampbase, 8|RLSB, "TMEB"},
+ // from 0.98.15
+ { ×tampbase, sizeof(timestampbase) | RLSB, "TSBS"}, // size seems to match?
+ { &X.mooPI, 1, "MooP"}, // alternative to the "quick and dirty hack"
+ // TODO: IQLB?
+ { 0, }
};
-static uint16 TempAddrT,RefreshAddrT;
-
-#define SFPPUELEMENTS 10
-SFORMAT SFPPU[SFPPUELEMENTS]={
- { NTARAM, 0x800, "NTAR"},
- { PALRAM, 0x20, "PRAM"},
- { SPRAM, 0x100, "SPRA"},
- { PPU, 0x4, "PPUR"},
- { &XOffset, 1, "XOFF"},
- { &vtoggle, 1, "VTOG"},
- { &RefreshAddrT, 2|RLSB, "RADD"},
- { &TempAddrT, 2|RLSB, "TADD"},
- { &VRAMBuffer, 1, "VBUF"},
- { &PPUGenLatch, 1, "PGEN"},
-};
+extern uint16 TempAddrT,RefreshAddrT;
-// Is this chunk necessary? I'll fix it later.
-//#define SFCTLRELEMENTS 2
-//SFORMAT SFCTLR[SFCTLRELEMENTS]={
-// { &joy_readbit, 1, "J1RB"},
-// { &joy2_readbit, 1, "J2RB"}
-//};
-#define SFSNDELEMENTS 18
-SFORMAT SFSND[SFSNDELEMENTS]={
+SFORMAT SFSND[]={
{ &fhcnt, 4|RLSB,"FHCN"},
{ &fcnt, 1, "FCNT"},
{ PSG, 14, "PSG"},
{ DecCountTo1, 3,"DCT1"},
{ &PCMBitIndex, 1,"PBIN"},
{ &PCMAddressIndex, 4|RLSB, "PAIN"},
- { &PCMSizeIndex, 4|RLSB, "PSIN"}
+ { &PCMSizeIndex, 4|RLSB, "PSIN"},
+ { 0, }
};
-int WriteStateChunk(FILE *st, int type, SFORMAT *sf, int count)
+
+static int SubWrite(FILE *st, SFORMAT *sf)
{
- int bsize;
- int x;
+ uint32 acc=0;
- fputc(type,st);
-
- for(x=bsize=0;x<count;x++)
- bsize+=sf[x].s&(~RLSB);
- bsize+=count<<3;
- write32(bsize,st);
- for(x=0;x<count;x++)
+ while(sf->v)
{
- fwrite(sf[x].desc,1,4,st);
- write32(sf[x].s&(~RLSB),st);
- #ifdef LSB_FIRST
- fwrite((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st);
- #else
+ if(sf->s==~0) /* Link to another struct. */
{
- int z;
- if(sf[x].s&RLSB)
- {
- for(z=(sf[x].s&(~RLSB))-1;z>=0;z--)
- {
- fputc(*(uint8*)sf[x].v,st);
- }
+ uint32 tmp;
+
+ if(!(tmp=SubWrite(st,(SFORMAT *)sf->v)))
+ return(0);
+ acc+=tmp;
+ sf++;
+ continue;
}
- else
- fwrite((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st);
+
+ acc+=8; /* Description + size */
+ acc+=sf->s&(~RLSB);
+
+ if(st) /* Are we writing or calculating the size of this block? */
+ {
+ fwrite(sf->desc,1,4,st);
+ write32le(sf->s&(~RLSB),st);
+
+ #ifndef LSB_FIRST
+ if(sf->s&RLSB)
+ FlipByteOrder(sf->v,sf->s&(~RLSB));
+ #endif
+
+ fwrite((uint8 *)sf->v,1,sf->s&(~RLSB),st);
+ /* Now restore the original byte order. */
+ #ifndef LSB_FIRST
+ if(sf->s&RLSB)
+ FlipByteOrder(sf->v,sf->s&(~RLSB));
+ #endif
}
- #endif
+ sf++;
}
- return (bsize+5);
+
+ return(acc);
}
-int ReadStateChunk(FILE *st, SFORMAT *sf, int count, int size)
+static int WriteStateChunk(FILE *st, int type, SFORMAT *sf)
{
- uint8 tmpyo[16];
int bsize;
- int x;
- for(x=bsize=0;x<count;x++)
- bsize+=sf[x].s&(~RLSB);
- if(stateversion>=53)
- bsize+=count<<3;
- else
+ fputc(type,st);
+
+ bsize=SubWrite(0,sf);
+ write32le(bsize,st);
+
+ if(!SubWrite(st,sf))
{
- if(bsize!=size)
+ return(0);
+ }
+ return (bsize+5);
+}
+
+static SFORMAT *CheckS(SFORMAT *sf, uint32 tsize, char *desc)
+{
+ while(sf->v)
+ {
+ if(sf->s==~0) /* Link to another SFORMAT structure. */
{
- fseek(st,size,SEEK_CUR);
- return 0;
+ SFORMAT *tmp;
+ if((tmp= CheckS((SFORMAT *)sf->v, tsize, desc) ))
+ return(tmp);
+ sf++;
+ continue;
+ }
+ if(!memcmp(desc,sf->desc,4))
+ {
+ if(tsize!=(sf->s&(~RLSB)))
+ {
+ printf("ReadStateChunk: sect \"%c%c%c%c\" has wrong size\n", desc[0], desc[1], desc[2], desc[3]);
+ return(0);
+ }
+ return(sf);
}
+ sf++;
}
+ return(0);
+}
- if(stateversion<56)
- memcpy(tmpyo,mapbyte3,16);
+static int ReadStateChunk(FILE *st, SFORMAT *sf, int size)
+{
+ //if(scan_chunks)
+ // return fseek(st,size,SEEK_CUR) == 0;
- if(stateversion>=53)
- {
- int temp;
- temp=ftell(st);
+ SFORMAT *tmp;
+ int temp;
+ temp=ftell(st);
- while(ftell(st)<temp+size)
- {
- int tsize;
- char toa[4];
+ while(ftell(st)<temp+size)
+ {
+ uint32 tsize;
+ char toa[4];
+ if(fread(toa,1,4,st)<=0)
+ return 0;
- if(fread(toa,1,4,st)<=0)
- return 0;
- read32(&tsize,st);
+ read32le(&tsize,st);
- for(x=0;x<count;x++)
- {
- if(!memcmp(toa,sf[x].desc,4))
- {
- if(tsize!=(sf[x].s&(~RLSB)))
- goto nkayo;
- #ifndef LSB_FIRST
- if(sf[x].s&RLSB)
- {
- int z;
- for(z=(sf[x].s&(~RLSB))-1;z>=0;z--)
- *(uint8*)sf[x].v=fgetc(st);
- }
- else
- #endif
- {
- fread((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st);
- }
- goto bloo;
- }
- }
- nkayo:
- fseek(st,tsize,SEEK_CUR);
- bloo:;
- } // while(...)
- } // >=53
- else
- {
- for(x=0;x<count;x++)
+ if((tmp=CheckS(sf,tsize,toa)))
{
- #ifdef LSB_FIRST
- fread((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st);
- #else
- int z;
- if(sf[x].s&RLSB)
- for(z=(sf[x].s&(~RLSB))-1;z>=0;z--)
- {
- *(uint8*)sf[x].v=fgetc(st);
- }
- else
- fread((uint8 *)sf[x].v,1,sf[x].s&(~RLSB),st);
+ fread((uint8 *)tmp->v,1,tmp->s&(~RLSB),st);
+
+ #ifndef LSB_FIRST
+ if(tmp->s&RLSB)
+ FlipByteOrder(tmp->v,tmp->s&(~RLSB));
#endif
}
- }
- if(stateversion<56)
- {
- for(x=0;x<16;x++)
- #ifdef LSB_FIRST
- mapbyte1[x]=mapbyte1[x<<1];
- #else
- mapbyte1[x]=mapbyte1[(x<<1)+1];
- #endif
- memcpy(mapbyte3,tmpyo,16);
- }
+ else
+ {
+ fseek(st,tsize,SEEK_CUR);
+ printf("ReadStateChunk: sect \"%c%c%c%c\" not handled\n", toa[0], toa[1], toa[2], toa[3]);
+ }
+ } // while(...)
return 1;
}
-int ReadStateChunks(FILE *st)
+
+static int ReadStateChunks(FILE *st)
{
int t;
uint32 size;
t=fgetc(st);
if(t==EOF) break;
if(!read32(&size,st)) break;
+
+ // printf("ReadStateChunks: chunk %i\n", t);
switch(t)
{
- case 1:if(!ReadStateChunk(st,SFCPU,SFCPUELEMENTS,size)) ret=0;break;
- case 2:if(!ReadStateChunk(st,SFCPUC,SFCPUCELEMENTS,size)) ret=0;
+ case 1:if(!ReadStateChunk(st,SFCPU,size)) ret=0;
+#ifdef ASM_6502
+ asmcpu_unpack();
+#endif
+ break;
+ case 2:if(!ReadStateChunk(st,SFCPUC,size)) ret=0;
else
{
X.mooPI=X.P; // Quick and dirty hack.
}
break;
- case 3:if(!ReadStateChunk(st,SFPPU,SFPPUELEMENTS,size)) ret=0;break;
-// case 4:if(!ReadStateChunk(st,SFCTLR,SFCTLRELEMENTS,size)) ret=0;break;
- case 5:if(!ReadStateChunk(st,SFSND,SFSNDELEMENTS,size)) ret=0;break;
- case 0x10:if(!ReadStateChunk(st,SFMDATA,SFEXINDEX,size)) ret=0;break;
- default: if(fseek(st,size,SEEK_CUR)<0) goto endo;break;
+ case 3:if(!ReadStateChunk(st,FCEUPPU_STATEINFO,size)) ret=0;break;
+ case 4:if(!ReadStateChunk(st,FCEUCTRL_STATEINFO,size)) ret=0;break;
+ case 5:if(!ReadStateChunk(st,SFSND,size)) ret=0;break;
+ case 0x10:if(!ReadStateChunk(st,SFMDATA,size)) ret=0;break;
+ default:printf("ReadStateChunks: unknown chunk: %i\n", t);
+ if(fseek(st,size,SEEK_CUR)<0) goto endo;break;
}
}
endo:
void SaveState(void)
{
FILE *st=NULL;
+ char *fname;
TempAddrT=TempAddr;
RefreshAddrT=RefreshAddr;
return;
}
- st=fopen(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0),"wb");
+ fname = FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0);
+ st=fopen(fname,"wb");
+ free(fname);
if(st!=NULL)
{
header[3]=VERSION_NUMERIC;
fwrite(header,1,16,st);
- totalsize=WriteStateChunk(st,1,SFCPU,SFCPUELEMENTS);
- totalsize+=WriteStateChunk(st,2,SFCPUC,SFCPUCELEMENTS);
- totalsize+=WriteStateChunk(st,3,SFPPU,SFPPUELEMENTS);
- // totalsize+=WriteStateChunk(st,4,SFCTLR,SFCTLRELEMENTS);
- totalsize+=WriteStateChunk(st,5,SFSND,SFSNDELEMENTS);
- totalsize+=WriteStateChunk(st,0x10,SFMDATA,SFEXINDEX);
-
+#ifdef ASM_6502
+ asmcpu_pack();
+#endif
+ totalsize=WriteStateChunk(st,1,SFCPU);
+ totalsize+=WriteStateChunk(st,2,SFCPUC);
+ totalsize+=WriteStateChunk(st,3,FCEUPPU_STATEINFO);
+ totalsize+=WriteStateChunk(st,4,FCEUCTRL_STATEINFO);
+ totalsize+=WriteStateChunk(st,5,SFSND);
+
+
+ if(SPreSave) SPreSave();
+ totalsize+=WriteStateChunk(st,0x10,SFMDATA);
+ if(SPostSave) SPostSave();
+
fseek(st,4,SEEK_SET);
write32(totalsize,st);
SaveStateStatus[CurrentState]=1;
fclose(st);
+#ifdef GP2X
+ sync();
+#endif
FCEU_DispMessage("State %d saved.",CurrentState);
}
else
}
static int LoadStateOld(FILE *st);
-void LoadState(void)
+int FCEUSS_LoadFP(FILE *st, int make_backup)
{
int x;
- FILE *st=NULL;
-
- if(geniestage==1)
- {
- FCEU_DispMessage("Cannot load FCS in GG screen.");
- return;
- }
-
- st=fopen(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0),"rb");
-
if(st!=NULL)
{
uint8 header[16];
fread(&header,1,16,st);
if(memcmp(header,"FCS",3))
{
- fseek(st,0,SEEK_SET);
+ fseek(st,0,SEEK_SET);
if(!LoadStateOld(st))
goto lerror;
goto okload;
lerror:
FCEU_DispMessage("State %d load error.",CurrentState);
SaveStateStatus[CurrentState]=0;
- return;
+ return 0;
+ }
+ return 1;
+}
+
+void LoadState(void)
+{
+ FILE *st=NULL;
+ char *fname;
+
+ if(geniestage==1)
+ {
+ FCEU_DispMessage("Cannot load FCS in GG screen.");
+ return;
+ }
+
+ fname = FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0);
+ st=fopen(fname,"rb");
+ free(fname);
+
+ if (st)
+ {
+ FCEUSS_LoadFP(st, 0);
+ fclose(st);
+ }
+ else
+ {
+ FCEU_DispMessage("State %d load error (no file).",CurrentState);
+ SaveStateStatus[CurrentState]=0;
}
- fclose(st);
}
char SaveStateStatus[10];
+#if 0 // leaks memory
void CheckStates(void)
{
FILE *st=NULL;
int ssel;
- if(SaveStateStatus[0]==-1)
+ if(SaveStateStatus[0]==(char)-1)
for(ssel=0;ssel<10;ssel++)
{
st=fopen(FCEU_MakeFName(FCEUMKF_STATE,ssel,0),"rb");
SaveStateStatus[ssel]=0;
}
}
+#endif
void SaveStateRefresh(void)
{
SaveStateStatus[0]=-1;
}
-void ResetExState(void)
+void ResetExState(void (*PreSave)(void), void (*PostSave)(void))
{
int x;
for(x=0;x<SFEXINDEX;x++)
- free(SFMDATA[x].desc);
+ {
+ if(SFMDATA[x].desc)
+ free(SFMDATA[x].desc);
+ }
+ SPreSave = PreSave;
+ SPostSave = PostSave;
SFEXINDEX=0;
}
+
void AddExState(void *v, uint32 s, int type, char *desc)
{
- SFMDATA[SFEXINDEX].desc=FCEU_malloc(5);
- if(SFMDATA[SFEXINDEX].desc)
+ if(desc)
{
+ SFMDATA[SFEXINDEX].desc=(char *)FCEU_malloc(5);
strcpy(SFMDATA[SFEXINDEX].desc,desc);
- SFMDATA[SFEXINDEX].v=v;
- SFMDATA[SFEXINDEX].s=s;
- if(type) SFMDATA[SFEXINDEX].s|=RLSB;
- if(SFEXINDEX<63) SFEXINDEX++;
}
+ else
+ SFMDATA[SFEXINDEX].desc=0;
+ SFMDATA[SFEXINDEX].v=v;
+ SFMDATA[SFEXINDEX].s=s;
+ if(type) SFMDATA[SFEXINDEX].s|=RLSB;
+ if(SFEXINDEX<SFMDATA_SIZE-1)
+ SFEXINDEX++;
+ else
+ {
+ static int once=1;
+ if(once)
+ {
+ once=0;
+ FCEU_PrintError("Error in AddExState: SFEXINDEX overflow.\nSomebody made SFMDATA_SIZE too small.");
+ }
+ }
+ SFMDATA[SFEXINDEX].v=0; // End marker.
}
+
/* Old state loading code follows */
uint8 *StateBuffer;
int32 nada;
uint8 version;
nada=0;
-
+
+ printf("LoadStateOld\n");
+
StateBuffer=FCEU_malloc(59999);
if(StateBuffer==NULL)
return 0;
afread(&nada,1,1);
afread(&nada,1,1);
afread(&nada,1,1);
-
+
for(x=0;x<8;x++)
areadupper8of16((int8 *)&CHRBankList[x]);
afread(PRGBankList,4,1);
aread16((int8 *)&scanline);
aread16((int8 *)&RefreshAddr);
afread(&VRAMBuffer,1,1);
-
+
afread(&IRQa,1,1);
aread32((int8 *)&IRQCount);
aread32((int8 *)&IRQLatch);
{
nada=0;
afread(&nada,1,1);
- PPUCHRRAM|=(nada?1:0)<<x;
+ PPUCHRRAM|=(nada?1:0)<<x;
}
-
+
afread(mapbyte1,1,8);
afread(mapbyte2,1,8);
afread(mapbyte3,1,8);
afread(mapbyte4,1,8);
for(x=0;x<4;x++)
aread16((int8 *)&nada);
-
+
PPUNTARAM=0;
for(x=0;x<4;x++)
{
afread(&vtoggle,1,1);
aread16((int8 *)&TempAddrT);
aread16((int8 *)&RefreshAddrT);
-
+
if(GameStateRestore) GameStateRestore(version);
free(StateBuffer);
FixOldSaveStateSFreq();