1 /* FCE Ultra - NES/Famicom Emulator
3 * Copyright notice for this file:
4 * Copyright (C) 2002 Ben Parnell
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 /* TODO: Add (better) file io error checking */
22 /* TODO: Change save state file format. */
36 #define INESPRIV // Take this out when old save state support is removed in a future version.
46 static void (*SPreSave)(void) = 0;
47 static void (*SPostSave)(void) = 0;
49 #define SFMDATA_SIZE (64)
50 static SFORMAT SFMDATA[SFMDATA_SIZE];
52 static int stateversion;
54 extern SFORMAT FCEUPPU_STATEINFO[]; // 3
55 extern SFORMAT FCEUSND_STATEINFO[];
56 extern SFORMAT FCEUCTRL_STATEINFO[]; // 4
58 SFORMAT SFCPU[]={ // 1
59 { &X.PC, 2|RLSB, "PC\0"},
69 SFORMAT SFCPUC[]={ // 2
70 { &X.jammed, 1, "JAMM"},
71 { &X.IRQlow, 1, "IRQL"},
72 { &X.tcount, 4|RLSB, "ICoa"},
73 { &X.count, 4|RLSB, "ICou"},
74 { ×tamp, 4|RLSB, "TIME"},
75 { ×tampbase, 8|RLSB, "TMEB"},
77 { ×tampbase, sizeof(timestampbase) | RLSB, "TSBS"}, // size seems to match?
78 { &X.mooPI, 1, "MooP"}, // alternative to the "quick and dirty hack"
83 extern uint16 TempAddrT,RefreshAddrT;
86 static int SubWrite(FILE *st, SFORMAT *sf)
92 if(sf->s==~0) /* Link to another struct. */
96 if(!(tmp=SubWrite(st,(SFORMAT *)sf->v)))
103 acc+=8; /* Description + size */
106 if(st) /* Are we writing or calculating the size of this block? */
108 fwrite(sf->desc,1,4,st);
109 write32le(sf->s&(~RLSB),st);
113 FlipByteOrder(sf->v,sf->s&(~RLSB));
116 fwrite((uint8 *)sf->v,1,sf->s&(~RLSB),st);
117 /* Now restore the original byte order. */
120 FlipByteOrder(sf->v,sf->s&(~RLSB));
129 static int WriteStateChunk(FILE *st, int type, SFORMAT *sf)
135 bsize=SubWrite(0,sf);
145 static SFORMAT *CheckS(SFORMAT *sf, uint32 tsize, char *desc)
149 if(sf->s==~0) /* Link to another SFORMAT structure. */
152 if((tmp= CheckS((SFORMAT *)sf->v, tsize, desc) ))
157 if(!memcmp(desc,sf->desc,4))
159 if(tsize!=(sf->s&(~RLSB)))
161 printf("ReadStateChunk: sect \"%c%c%c%c\" has wrong size\n", desc[0], desc[1], desc[2], desc[3]);
171 static int ReadStateChunk(FILE *st, SFORMAT *sf, int size)
174 // return fseek(st,size,SEEK_CUR) == 0;
180 while(ftell(st)<temp+size)
184 if(fread(toa,1,4,st)<=0)
189 if((tmp=CheckS(sf,tsize,toa)))
191 fread((uint8 *)tmp->v,1,tmp->s&(~RLSB),st);
195 FlipByteOrder(tmp->v,tmp->s&(~RLSB));
200 fseek(st,tsize,SEEK_CUR);
201 printf("ReadStateChunk: sect \"%c%c%c%c\" not handled\n", toa[0], toa[1], toa[2], toa[3]);
208 static int ReadStateChunks(FILE *st)
218 if(!read32(&size,st)) break;
220 // printf("ReadStateChunks: chunk %i\n", t);
223 case 1:if(!ReadStateChunk(st,SFCPU,size)) ret=0;
228 case 2:if(!ReadStateChunk(st,SFCPUC,size)) ret=0;
231 X.mooPI=X.P; // Quick and dirty hack.
234 case 3:if(!ReadStateChunk(st,FCEUPPU_STATEINFO,size)) ret=0;break;
235 case 4:if(!ReadStateChunk(st,FCEUCTRL_STATEINFO,size)) ret=0;break;
236 case 5:if(!ReadStateChunk(st,FCEUSND_STATEINFO,size)) ret=0;break;
237 case 0x10:if(!ReadStateChunk(st,SFMDATA,size)) ret=0;break;
238 default:printf("ReadStateChunks: unknown chunk: %i\n", t);
239 if(fseek(st,size,SEEK_CUR)<0) goto endo;break;
244 FCEUSND_LoadState(stateversion);
250 extern int geniestage;
257 RefreshAddrT=RefreshAddr;
261 FCEU_DispMessage("Cannot save FCS in GG screen.");
265 fname = FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0);
266 st=fopen(fname,"wb");
272 uint8 header[16]="FCS";
273 memset(header+4,0,sizeof(header)-4);
274 header[3]=VERSION_NUMERIC;
275 fwrite(header,1,16,st);
281 totalsize=WriteStateChunk(st,1,SFCPU);
282 totalsize+=WriteStateChunk(st,2,SFCPUC);
283 totalsize+=WriteStateChunk(st,3,FCEUPPU_STATEINFO);
284 totalsize+=WriteStateChunk(st,4,FCEUCTRL_STATEINFO);
285 totalsize+=WriteStateChunk(st,5,FCEUSND_STATEINFO);
288 if(SPreSave) SPreSave();
289 totalsize+=WriteStateChunk(st,0x10,SFMDATA);
290 if(SPostSave) SPostSave();
292 fseek(st,4,SEEK_SET);
293 write32(totalsize,st);
294 SaveStateStatus[CurrentState]=1;
299 FCEU_DispMessage("State %d saved.",CurrentState);
302 FCEU_DispMessage("State %d save error.",CurrentState);
305 static int LoadStateOld(FILE *st);
306 int FCEUSS_LoadFP(FILE *st, int make_backup)
313 fread(&header,1,16,st);
314 if(memcmp(header,"FCS",3))
316 fseek(st,0,SEEK_SET);
317 if(!LoadStateOld(st))
321 stateversion=header[3];
323 FixOldSaveStateSFreq();
324 x=ReadStateChunks(st);
325 if(GameStateRestore) GameStateRestore(header[3]);
330 RefreshAddr=RefreshAddrT;
332 SaveStateStatus[CurrentState]=1;
333 FCEU_DispMessage("State %d loaded.",CurrentState);
334 SaveStateStatus[CurrentState]=1;
338 SaveStateStatus[CurrentState]=1;
339 FCEU_DispMessage("Error(s) reading state %d!",CurrentState);
345 FCEU_DispMessage("State %d load error.",CurrentState);
346 SaveStateStatus[CurrentState]=0;
359 FCEU_DispMessage("Cannot load FCS in GG screen.");
363 fname = FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0);
364 st=fopen(fname,"rb");
369 FCEUSS_LoadFP(st, 0);
374 FCEU_DispMessage("State %d load error (no file).",CurrentState);
375 SaveStateStatus[CurrentState]=0;
379 char SaveStateStatus[10];
380 #if 0 // leaks memory
381 void CheckStates(void)
386 if(SaveStateStatus[0]==(char)-1)
387 for(ssel=0;ssel<10;ssel++)
389 st=fopen(FCEU_MakeFName(FCEUMKF_STATE,ssel,0),"rb");
392 SaveStateStatus[ssel]=1;
396 SaveStateStatus[ssel]=0;
401 void SaveStateRefresh(void)
403 SaveStateStatus[0]=-1;
406 void ResetExState(void (*PreSave)(void), void (*PostSave)(void))
409 for(x=0;x<SFEXINDEX;x++)
412 free(SFMDATA[x].desc);
415 SPostSave = PostSave;
420 void AddExState(void *v, uint32 s, int type, char *desc)
424 SFMDATA[SFEXINDEX].desc=(char *)FCEU_malloc(5);
425 strcpy(SFMDATA[SFEXINDEX].desc,desc);
428 SFMDATA[SFEXINDEX].desc=0;
429 SFMDATA[SFEXINDEX].v=v;
430 SFMDATA[SFEXINDEX].s=s;
431 if(type) SFMDATA[SFEXINDEX].s|=RLSB;
432 if(SFEXINDEX<SFMDATA_SIZE-1)
440 FCEU_PrintError("Error in AddExState: SFEXINDEX overflow.\nSomebody made SFMDATA_SIZE too small.");
443 SFMDATA[SFEXINDEX].v=0; // End marker.
447 /* Old state loading code follows */
450 unsigned int intostate;
452 static void afread(void *ptr, size_t _size, size_t _nelem)
454 memcpy(ptr,StateBuffer+intostate,_size*_nelem);
455 intostate+=_size*_nelem;
459 static void areadlower8of16(int8 *d)
462 *d=StateBuffer[intostate++];
464 d[1]=StateBuffer[intostate++];
469 static void areadupper8of16(int8 *d)
472 d[1]=StateBuffer[intostate++];
474 *d=StateBuffer[intostate++];
479 static void aread16(int8 *d)
482 *d=StateBuffer[intostate++];
483 d[1]=StateBuffer[intostate++];
485 d[1]=StateBuffer[intostate++];
486 *d=StateBuffer[intostate++];
491 static void aread32(int8 *d)
494 *d=StateBuffer[intostate++];
495 d[1]=StateBuffer[intostate++];
496 d[2]=StateBuffer[intostate++];
497 d[3]=StateBuffer[intostate++];
499 d[3]=StateBuffer[intostate++];
500 d[2]=StateBuffer[intostate++];
501 d[1]=StateBuffer[intostate++];
502 *d=StateBuffer[intostate++];
506 static int LoadStateOld(FILE *st)
513 printf("LoadStateOld\n");
515 StateBuffer=FCEU_malloc(59999);
516 if(StateBuffer==NULL)
518 if(!fread(StateBuffer,59999,1,st))
538 afread(&version,1,1);
543 aread32((int8 *)&X.count);
548 aread32((int8 *)&nada);
555 areadupper8of16((int8 *)&CHRBankList[x]);
556 afread(PRGBankList,4,1);
558 areadlower8of16((int8 *)&CHRBankList[x]);
559 afread(CHRRAM,1,0x2000);
560 afread(NTARAM,1,0x400);
561 afread(ExtraNTARAM,1,0x400);
562 afread(NTARAM+0x400,1,0x400);
563 afread(ExtraNTARAM+0x400,1,0x400);
567 afread(PALRAM,1,0x20);
568 for(x=0;x<256-32;x++)
570 for(x=0x00;x<0x20;x++)
573 afread(SPRAM,1,0x100);
576 aread16((int8 *)&scanline);
577 aread16((int8 *)&RefreshAddr);
578 afread(&VRAMBuffer,1,1);
581 aread32((int8 *)&IRQCount);
582 aread32((int8 *)&IRQLatch);
583 afread(&Mirroring,1,1);
586 afread(MapperExRAM,1,193);
588 PSG[0x17]=MapperExRAM[115];
601 afread(&XOffset,1,1);
607 PPUCHRRAM|=(nada?1:0)<<x;
610 afread(mapbyte1,1,8);
611 afread(mapbyte2,1,8);
612 afread(mapbyte3,1,8);
613 afread(mapbyte4,1,8);
615 aread16((int8 *)&nada);
621 aread16((int8 *)&nada);
622 PPUNTARAM|=((nada&0x800)?0:1)<<x;
624 afread(MapperExRAM,1,32768);
625 afread(&vtoggle,1,1);
626 aread16((int8 *)&TempAddrT);
627 aread16((int8 *)&RefreshAddrT);
629 if(GameStateRestore) GameStateRestore(version);
631 FixOldSaveStateSFreq();