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. */
37 #define INESPRIV // Take this out when old save state support is removed in a future version.
47 static void (*SPreSave)(void) = 0;
48 static void (*SPostSave)(void) = 0;
50 #define SFMDATA_SIZE (64)
51 static SFORMAT SFMDATA[SFMDATA_SIZE];
53 static int stateversion;
55 extern SFORMAT FCEUPPU_STATEINFO[]; // 3
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;
87 { &fhcnt, 4|RLSB,"FHCN"},
90 { &PSG[0x15], 1, "P15"},
91 { &PSG[0x17], 1, "P17"},
92 { decvolume, 3, "DECV"},
94 { &nreg, 2|RLSB, "NREG"},
95 { &trimode, 1, "TRIM"},
96 { &tricoop, 1, "TRIC"},
97 { sweepon, 2, "SWEE"},
98 { &curfreq[0], 4|RLSB,"CRF1"},
99 { &curfreq[1], 4|RLSB,"CRF2"},
100 { SweepCount, 2,"SWCT"},
101 { DecCountTo1, 3,"DCT1"},
102 { &PCMBitIndex, 1,"PBIN"},
103 { &PCMAddressIndex, 4|RLSB, "PAIN"},
104 { &PCMSizeIndex, 4|RLSB, "PSIN"},
108 extern SFORMAT FCEUSND_STATEINFO[]; // TODO: unify?
109 #define get_snd_sf() (use098code ? FCEUSND_STATEINFO : SFSND)
112 static int SubWrite(FILE *st, SFORMAT *sf)
118 if(sf->s==~0) /* Link to another struct. */
122 if(!(tmp=SubWrite(st,(SFORMAT *)sf->v)))
129 acc+=8; /* Description + size */
132 if(st) /* Are we writing or calculating the size of this block? */
134 fwrite(sf->desc,1,4,st);
135 write32le(sf->s&(~RLSB),st);
139 FlipByteOrder(sf->v,sf->s&(~RLSB));
142 fwrite((uint8 *)sf->v,1,sf->s&(~RLSB),st);
143 /* Now restore the original byte order. */
146 FlipByteOrder(sf->v,sf->s&(~RLSB));
155 static int WriteStateChunk(FILE *st, int type, SFORMAT *sf)
161 bsize=SubWrite(0,sf);
171 static SFORMAT *CheckS(SFORMAT *sf, uint32 tsize, char *desc)
175 if(sf->s==~0) /* Link to another SFORMAT structure. */
178 if((tmp= CheckS((SFORMAT *)sf->v, tsize, desc) ))
183 if(!memcmp(desc,sf->desc,4))
185 if(tsize!=(sf->s&(~RLSB)))
187 printf("ReadStateChunk: sect \"%c%c%c%c\" has wrong size\n", desc[0], desc[1], desc[2], desc[3]);
197 static int ReadStateChunk(FILE *st, SFORMAT *sf, int size)
200 // return fseek(st,size,SEEK_CUR) == 0;
206 while(ftell(st)<temp+size)
210 if(fread(toa,1,4,st)<=0)
215 if((tmp=CheckS(sf,tsize,toa)))
217 fread((uint8 *)tmp->v,1,tmp->s&(~RLSB),st);
221 FlipByteOrder(tmp->v,tmp->s&(~RLSB));
226 fseek(st,tsize,SEEK_CUR);
227 printf("ReadStateChunk: sect \"%c%c%c%c\" not handled\n", toa[0], toa[1], toa[2], toa[3]);
234 static int ReadStateChunks(FILE *st)
244 if(!read32(&size,st)) break;
246 // printf("ReadStateChunks: chunk %i\n", t);
249 case 1:if(!ReadStateChunk(st,SFCPU,size)) ret=0;
254 case 2:if(!ReadStateChunk(st,SFCPUC,size)) ret=0;
257 X.mooPI=X.P; // Quick and dirty hack.
260 case 3:if(!ReadStateChunk(st,FCEUPPU_STATEINFO,size)) ret=0;break;
261 case 4:if(!ReadStateChunk(st,FCEUCTRL_STATEINFO,size)) ret=0;break;
262 case 5:if(!ReadStateChunk(st,get_snd_sf(),size)) ret=0;break;
263 case 0x10:if(!ReadStateChunk(st,SFMDATA,size)) ret=0;break;
264 default:printf("ReadStateChunks: unknown chunk: %i\n", t);
265 if(fseek(st,size,SEEK_CUR)<0) goto endo;break;
274 extern int geniestage;
281 RefreshAddrT=RefreshAddr;
285 FCEU_DispMessage("Cannot save FCS in GG screen.");
289 fname = FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0);
290 st=fopen(fname,"wb");
295 static uint32 totalsize;
296 static uint8 header[16]="FCS";
297 memset(header+4,0,13);
298 header[3]=VERSION_NUMERIC;
299 fwrite(header,1,16,st);
305 totalsize=WriteStateChunk(st,1,SFCPU);
306 totalsize+=WriteStateChunk(st,2,SFCPUC);
307 totalsize+=WriteStateChunk(st,3,FCEUPPU_STATEINFO);
308 totalsize+=WriteStateChunk(st,4,FCEUCTRL_STATEINFO);
309 totalsize+=WriteStateChunk(st,5,get_snd_sf());
312 if(SPreSave) SPreSave();
313 totalsize+=WriteStateChunk(st,0x10,SFMDATA);
314 if(SPostSave) SPostSave();
316 fseek(st,4,SEEK_SET);
317 write32(totalsize,st);
318 SaveStateStatus[CurrentState]=1;
323 FCEU_DispMessage("State %d saved.",CurrentState);
326 FCEU_DispMessage("State %d save error.",CurrentState);
329 static int LoadStateOld(FILE *st);
330 int FCEUSS_LoadFP(FILE *st, int make_backup)
337 fread(&header,1,16,st);
338 if(memcmp(header,"FCS",3))
340 fseek(st,0,SEEK_SET);
341 if(!LoadStateOld(st))
345 stateversion=header[3];
347 FixOldSaveStateSFreq();
348 x=ReadStateChunks(st);
349 if(GameStateRestore) GameStateRestore(header[3]);
353 FCEUSND_LoadState(header[3]);
355 RefreshAddr=RefreshAddrT;
357 SaveStateStatus[CurrentState]=1;
358 FCEU_DispMessage("State %d loaded.",CurrentState);
359 SaveStateStatus[CurrentState]=1;
363 SaveStateStatus[CurrentState]=1;
364 FCEU_DispMessage("Error(s) reading state %d!",CurrentState);
370 FCEU_DispMessage("State %d load error.",CurrentState);
371 SaveStateStatus[CurrentState]=0;
384 FCEU_DispMessage("Cannot load FCS in GG screen.");
388 fname = FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0);
389 st=fopen(fname,"rb");
394 FCEUSS_LoadFP(st, 0);
399 FCEU_DispMessage("State %d load error (no file).",CurrentState);
400 SaveStateStatus[CurrentState]=0;
404 char SaveStateStatus[10];
405 #if 0 // leaks memory
406 void CheckStates(void)
411 if(SaveStateStatus[0]==(char)-1)
412 for(ssel=0;ssel<10;ssel++)
414 st=fopen(FCEU_MakeFName(FCEUMKF_STATE,ssel,0),"rb");
417 SaveStateStatus[ssel]=1;
421 SaveStateStatus[ssel]=0;
426 void SaveStateRefresh(void)
428 SaveStateStatus[0]=-1;
431 void ResetExState(void (*PreSave)(void), void (*PostSave)(void))
434 for(x=0;x<SFEXINDEX;x++)
437 free(SFMDATA[x].desc);
440 SPostSave = PostSave;
445 void AddExState(void *v, uint32 s, int type, char *desc)
449 SFMDATA[SFEXINDEX].desc=(char *)FCEU_malloc(5);
450 strcpy(SFMDATA[SFEXINDEX].desc,desc);
453 SFMDATA[SFEXINDEX].desc=0;
454 SFMDATA[SFEXINDEX].v=v;
455 SFMDATA[SFEXINDEX].s=s;
456 if(type) SFMDATA[SFEXINDEX].s|=RLSB;
457 if(SFEXINDEX<SFMDATA_SIZE-1)
465 FCEU_PrintError("Error in AddExState: SFEXINDEX overflow.\nSomebody made SFMDATA_SIZE too small.");
468 SFMDATA[SFEXINDEX].v=0; // End marker.
472 /* Old state loading code follows */
475 unsigned int intostate;
477 static void afread(void *ptr, size_t _size, size_t _nelem)
479 memcpy(ptr,StateBuffer+intostate,_size*_nelem);
480 intostate+=_size*_nelem;
484 static void areadlower8of16(int8 *d)
487 *d=StateBuffer[intostate++];
489 d[1]=StateBuffer[intostate++];
494 static void areadupper8of16(int8 *d)
497 d[1]=StateBuffer[intostate++];
499 *d=StateBuffer[intostate++];
504 static void aread16(int8 *d)
507 *d=StateBuffer[intostate++];
508 d[1]=StateBuffer[intostate++];
510 d[1]=StateBuffer[intostate++];
511 *d=StateBuffer[intostate++];
516 static void aread32(int8 *d)
519 *d=StateBuffer[intostate++];
520 d[1]=StateBuffer[intostate++];
521 d[2]=StateBuffer[intostate++];
522 d[3]=StateBuffer[intostate++];
524 d[3]=StateBuffer[intostate++];
525 d[2]=StateBuffer[intostate++];
526 d[1]=StateBuffer[intostate++];
527 *d=StateBuffer[intostate++];
531 static int LoadStateOld(FILE *st)
538 printf("LoadStateOld\n");
540 StateBuffer=FCEU_malloc(59999);
541 if(StateBuffer==NULL)
543 if(!fread(StateBuffer,59999,1,st))
563 afread(&version,1,1);
568 aread32((int8 *)&X.count);
573 aread32((int8 *)&nada);
580 areadupper8of16((int8 *)&CHRBankList[x]);
581 afread(PRGBankList,4,1);
583 areadlower8of16((int8 *)&CHRBankList[x]);
584 afread(CHRRAM,1,0x2000);
585 afread(NTARAM,1,0x400);
586 afread(ExtraNTARAM,1,0x400);
587 afread(NTARAM+0x400,1,0x400);
588 afread(ExtraNTARAM+0x400,1,0x400);
592 afread(PALRAM,1,0x20);
593 for(x=0;x<256-32;x++)
595 for(x=0x00;x<0x20;x++)
598 afread(SPRAM,1,0x100);
601 aread16((int8 *)&scanline);
602 aread16((int8 *)&RefreshAddr);
603 afread(&VRAMBuffer,1,1);
606 aread32((int8 *)&IRQCount);
607 aread32((int8 *)&IRQLatch);
608 afread(&Mirroring,1,1);
611 afread(MapperExRAM,1,193);
613 PSG[0x17]=MapperExRAM[115];
626 afread(&XOffset,1,1);
632 PPUCHRRAM|=(nada?1:0)<<x;
635 afread(mapbyte1,1,8);
636 afread(mapbyte2,1,8);
637 afread(mapbyte3,1,8);
638 afread(mapbyte4,1,8);
640 aread16((int8 *)&nada);
646 aread16((int8 *)&nada);
647 PPUNTARAM|=((nada&0x800)?0:1)<<x;
649 afread(MapperExRAM,1,32768);
650 afread(&vtoggle,1,1);
651 aread16((int8 *)&TempAddrT);
652 aread16((int8 *)&RefreshAddrT);
654 if(GameStateRestore) GameStateRestore(version);
656 FixOldSaveStateSFreq();