release r2, update credits
[fceu.git] / state.c
CommitLineData
c62d2810 1/* FCE Ultra - NES/Famicom Emulator
2 *
3 * Copyright notice for this file:
4 * Copyright (C) 2002 Ben Parnell
5 *
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.
10 *
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.
15 *
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
19 */
20
21/* TODO: Add (better) file io error checking */
22/* TODO: Change save state file format. */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
92e249b1 27#ifdef GP2X
28#include <unistd.h>
29#endif
c62d2810 30
31#include "types.h"
32#include "x6502.h"
33#include "version.h"
34#include "fce.h"
35#include "sound.h"
36#define INESPRIV // Take this out when old save state support is removed in a future version.
37#include "ines.h"
38#include "svga.h"
39#include "endian.h"
40#include "fds.h"
41#include "general.h"
42#include "state.h"
43#include "memory.h"
5232c20c 44#include "ppu.h"
c62d2810 45
d97315ac 46static void (*SPreSave)(void) = 0;
47static void (*SPostSave)(void) = 0;
48
49#define SFMDATA_SIZE (64)
50static SFORMAT SFMDATA[SFMDATA_SIZE];
c62d2810 51static int SFEXINDEX;
52static int stateversion;
53
890e37ba 54extern SFORMAT FCEUPPU_STATEINFO[]; // 3
5bd16b94 55extern SFORMAT FCEUSND_STATEINFO[];
890e37ba 56extern SFORMAT FCEUCTRL_STATEINFO[]; // 4
c62d2810 57
890e37ba 58SFORMAT SFCPU[]={ // 1
c62d2810 59 { &X.PC, 2|RLSB, "PC\0"},
60 { &X.A, 1, "A\0\0"},
61 { &X.P, 1, "P\0\0"},
62 { &X.X, 1, "X\0\0"},
63 { &X.Y, 1, "Y\0\0"},
64 { &X.S, 1, "S\0\0"},
890e37ba 65 { RAM, 0x800, "RAM"},
66 { 0, }
c62d2810 67};
68
890e37ba 69SFORMAT SFCPUC[]={ // 2
c62d2810 70 { &X.jammed, 1, "JAMM"},
71 { &X.IRQlow, 1, "IRQL"},
72 { &X.tcount, 4|RLSB, "ICoa"},
73 { &X.count, 4|RLSB, "ICou"},
74 { &timestamp, 4|RLSB, "TIME"},
890e37ba 75 { &timestampbase, 8|RLSB, "TMEB"},
76 // from 0.98.15
77 { &timestampbase, sizeof(timestampbase) | RLSB, "TSBS"}, // size seems to match?
78 { &X.mooPI, 1, "MooP"}, // alternative to the "quick and dirty hack"
79 // TODO: IQLB?
80 { 0, }
c62d2810 81};
82
890e37ba 83extern uint16 TempAddrT,RefreshAddrT;
c62d2810 84
c62d2810 85
0bb3fe12 86static int SubWrite(FILE *st, SFORMAT *sf)
c62d2810 87{
0bb3fe12 88 uint32 acc=0;
c62d2810 89
0bb3fe12 90 while(sf->v)
91 {
92 if(sf->s==~0) /* Link to another struct. */
93 {
94 uint32 tmp;
95
96 if(!(tmp=SubWrite(st,(SFORMAT *)sf->v)))
97 return(0);
98 acc+=tmp;
99 sf++;
100 continue;
101 }
102
103 acc+=8; /* Description + size */
104 acc+=sf->s&(~RLSB);
105
106 if(st) /* Are we writing or calculating the size of this block? */
107 {
108 fwrite(sf->desc,1,4,st);
109 write32le(sf->s&(~RLSB),st);
110
111 #ifndef LSB_FIRST
112 if(sf->s&RLSB)
113 FlipByteOrder(sf->v,sf->s&(~RLSB));
114 #endif
115
116 fwrite((uint8 *)sf->v,1,sf->s&(~RLSB),st);
117 /* Now restore the original byte order. */
118 #ifndef LSB_FIRST
119 if(sf->s&RLSB)
120 FlipByteOrder(sf->v,sf->s&(~RLSB));
121 #endif
122 }
123 sf++;
124 }
125
126 return(acc);
127}
128
129static int WriteStateChunk(FILE *st, int type, SFORMAT *sf)
130{
131 int bsize;
890e37ba 132
c62d2810 133 fputc(type,st);
22f08d95 134
0bb3fe12 135 bsize=SubWrite(0,sf);
136 write32le(bsize,st);
137
138 if(!SubWrite(st,sf))
139 {
140 return(0);
141 }
142 return (bsize+5);
143}
144
145static SFORMAT *CheckS(SFORMAT *sf, uint32 tsize, char *desc)
146{
147 while(sf->v)
c62d2810 148 {
0bb3fe12 149 if(sf->s==~0) /* Link to another SFORMAT structure. */
c62d2810 150 {
0bb3fe12 151 SFORMAT *tmp;
152 if((tmp= CheckS((SFORMAT *)sf->v, tsize, desc) ))
153 return(tmp);
154 sf++;
155 continue;
156 }
157 if(!memcmp(desc,sf->desc,4))
c62d2810 158 {
0bb3fe12 159 if(tsize!=(sf->s&(~RLSB)))
c62d2810 160 {
0bb3fe12 161 printf("ReadStateChunk: sect \"%c%c%c%c\" has wrong size\n", desc[0], desc[1], desc[2], desc[3]);
162 return(0);
c62d2810 163 }
0bb3fe12 164 return(sf);
c62d2810 165 }
0bb3fe12 166 sf++;
c62d2810 167 }
0bb3fe12 168 return(0);
c62d2810 169}
170
0bb3fe12 171static int ReadStateChunk(FILE *st, SFORMAT *sf, int size)
c62d2810 172{
0bb3fe12 173 //if(scan_chunks)
174 // return fseek(st,size,SEEK_CUR) == 0;
c62d2810 175
0bb3fe12 176 SFORMAT *tmp;
177 int temp;
178 temp=ftell(st);
890e37ba 179
0bb3fe12 180 while(ftell(st)<temp+size)
c62d2810 181 {
0bb3fe12 182 uint32 tsize;
183 char toa[4];
184 if(fread(toa,1,4,st)<=0)
c62d2810 185 return 0;
c62d2810 186
0bb3fe12 187 read32le(&tsize,st);
c62d2810 188
0bb3fe12 189 if((tmp=CheckS(sf,tsize,toa)))
c62d2810 190 {
0bb3fe12 191 fread((uint8 *)tmp->v,1,tmp->s&(~RLSB),st);
c62d2810 192
0bb3fe12 193 #ifndef LSB_FIRST
194 if(tmp->s&RLSB)
195 FlipByteOrder(tmp->v,tmp->s&(~RLSB));
c62d2810 196 #endif
197 }
0bb3fe12 198 else
199 {
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]);
202 }
203 } // while(...)
c62d2810 204 return 1;
205}
206
0bb3fe12 207
890e37ba 208static int ReadStateChunks(FILE *st)
c62d2810 209{
210 int t;
211 uint32 size;
212 int ret=1;
213
214for(;;)
215 {
216 t=fgetc(st);
217 if(t==EOF) break;
218 if(!read32(&size,st)) break;
890e37ba 219
220 // printf("ReadStateChunks: chunk %i\n", t);
c62d2810 221 switch(t)
222 {
890e37ba 223 case 1:if(!ReadStateChunk(st,SFCPU,size)) ret=0;
92e249b1 224#ifdef ASM_6502
225 asmcpu_unpack();
226#endif
227 break;
890e37ba 228 case 2:if(!ReadStateChunk(st,SFCPUC,size)) ret=0;
c62d2810 229 else
230 {
231 X.mooPI=X.P; // Quick and dirty hack.
232 }
233 break;
890e37ba 234 case 3:if(!ReadStateChunk(st,FCEUPPU_STATEINFO,size)) ret=0;break;
235 case 4:if(!ReadStateChunk(st,FCEUCTRL_STATEINFO,size)) ret=0;break;
5bd16b94 236 case 5:if(!ReadStateChunk(st,FCEUSND_STATEINFO,size)) ret=0;break;
890e37ba 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;
c62d2810 240 }
241 }
242 endo:
5bd16b94 243 if(ret)
244 FCEUSND_LoadState(stateversion);
c62d2810 245 return ret;
246}
247
248
249int CurrentState=0;
250extern int geniestage;
251void SaveState(void)
252{
253 FILE *st=NULL;
971a1d07 254 char *fname;
c62d2810 255
256 TempAddrT=TempAddr;
257 RefreshAddrT=RefreshAddr;
258
259 if(geniestage==1)
260 {
261 FCEU_DispMessage("Cannot save FCS in GG screen.");
262 return;
263 }
264
971a1d07 265 fname = FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0);
266 st=fopen(fname,"wb");
267 free(fname);
c62d2810 268
269 if(st!=NULL)
270 {
ed531dd6 271 uint32 totalsize;
272 uint8 header[16]="FCS";
273 memset(header+4,0,sizeof(header)-4);
c62d2810 274 header[3]=VERSION_NUMERIC;
275 fwrite(header,1,16,st);
276
92e249b1 277#ifdef ASM_6502
278 asmcpu_pack();
279#endif
5bd16b94 280 FCEUSND_SaveState();
890e37ba 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);
5bd16b94 285 totalsize+=WriteStateChunk(st,5,FCEUSND_STATEINFO);
d97315ac 286
287
288 if(SPreSave) SPreSave();
890e37ba 289 totalsize+=WriteStateChunk(st,0x10,SFMDATA);
d97315ac 290 if(SPostSave) SPostSave();
22f08d95 291
c62d2810 292 fseek(st,4,SEEK_SET);
293 write32(totalsize,st);
294 SaveStateStatus[CurrentState]=1;
295 fclose(st);
92e249b1 296#ifdef GP2X
297 sync();
298#endif
c62d2810 299 FCEU_DispMessage("State %d saved.",CurrentState);
300 }
301 else
302 FCEU_DispMessage("State %d save error.",CurrentState);
303}
304
305static int LoadStateOld(FILE *st);
5a2aa426 306int FCEUSS_LoadFP(FILE *st, int make_backup)
c62d2810 307{
308 int x;
c62d2810 309 if(st!=NULL)
310 {
311 uint8 header[16];
312
313 fread(&header,1,16,st);
314 if(memcmp(header,"FCS",3))
315 {
22f08d95 316 fseek(st,0,SEEK_SET);
c62d2810 317 if(!LoadStateOld(st))
318 goto lerror;
319 goto okload;
320 }
321 stateversion=header[3];
322 if(stateversion<53)
323 FixOldSaveStateSFreq();
324 x=ReadStateChunks(st);
325 if(GameStateRestore) GameStateRestore(header[3]);
326 if(x)
327 {
328 okload:
329 TempAddr=TempAddrT;
330 RefreshAddr=RefreshAddrT;
331
332 SaveStateStatus[CurrentState]=1;
333 FCEU_DispMessage("State %d loaded.",CurrentState);
334 SaveStateStatus[CurrentState]=1;
335 }
336 else
337 {
338 SaveStateStatus[CurrentState]=1;
339 FCEU_DispMessage("Error(s) reading state %d!",CurrentState);
340 }
341 }
342 else
343 {
344 lerror:
345 FCEU_DispMessage("State %d load error.",CurrentState);
346 SaveStateStatus[CurrentState]=0;
5a2aa426 347 return 0;
348 }
349 return 1;
350}
351
352void LoadState(void)
353{
354 FILE *st=NULL;
971a1d07 355 char *fname;
5a2aa426 356
357 if(geniestage==1)
358 {
359 FCEU_DispMessage("Cannot load FCS in GG screen.");
360 return;
361 }
362
971a1d07 363 fname = FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0);
364 st=fopen(fname,"rb");
365 free(fname);
366
5a2aa426 367 if (st)
368 {
369 FCEUSS_LoadFP(st, 0);
370 fclose(st);
c62d2810 371 }
971a1d07 372 else
373 {
374 FCEU_DispMessage("State %d load error (no file).",CurrentState);
375 SaveStateStatus[CurrentState]=0;
376 }
c62d2810 377}
378
379char SaveStateStatus[10];
971a1d07 380#if 0 // leaks memory
c62d2810 381void CheckStates(void)
382{
383 FILE *st=NULL;
384 int ssel;
385
22f08d95 386 if(SaveStateStatus[0]==(char)-1)
c62d2810 387 for(ssel=0;ssel<10;ssel++)
388 {
389 st=fopen(FCEU_MakeFName(FCEUMKF_STATE,ssel,0),"rb");
390 if(st)
391 {
392 SaveStateStatus[ssel]=1;
393 fclose(st);
394 }
395 else
396 SaveStateStatus[ssel]=0;
397 }
398}
971a1d07 399#endif
c62d2810 400
401void SaveStateRefresh(void)
402{
403 SaveStateStatus[0]=-1;
404}
405
d97315ac 406void ResetExState(void (*PreSave)(void), void (*PostSave)(void))
c62d2810 407{
408 int x;
409 for(x=0;x<SFEXINDEX;x++)
d97315ac 410 {
411 if(SFMDATA[x].desc)
412 free(SFMDATA[x].desc);
413 }
414 SPreSave = PreSave;
415 SPostSave = PostSave;
c62d2810 416 SFEXINDEX=0;
417}
418
d97315ac 419
c62d2810 420void AddExState(void *v, uint32 s, int type, char *desc)
421{
d97315ac 422 if(desc)
c62d2810 423 {
d97315ac 424 SFMDATA[SFEXINDEX].desc=(char *)FCEU_malloc(5);
c62d2810 425 strcpy(SFMDATA[SFEXINDEX].desc,desc);
c62d2810 426 }
d97315ac 427 else
0bb3fe12 428 SFMDATA[SFEXINDEX].desc=0;
d97315ac 429 SFMDATA[SFEXINDEX].v=v;
430 SFMDATA[SFEXINDEX].s=s;
431 if(type) SFMDATA[SFEXINDEX].s|=RLSB;
432 if(SFEXINDEX<SFMDATA_SIZE-1)
433 SFEXINDEX++;
434 else
435 {
436 static int once=1;
437 if(once)
438 {
439 once=0;
440 FCEU_PrintError("Error in AddExState: SFEXINDEX overflow.\nSomebody made SFMDATA_SIZE too small.");
441 }
442 }
443 SFMDATA[SFEXINDEX].v=0; // End marker.
c62d2810 444}
445
d97315ac 446
c62d2810 447/* Old state loading code follows */
448
449uint8 *StateBuffer;
450unsigned int intostate;
451
452static void afread(void *ptr, size_t _size, size_t _nelem)
453{
454 memcpy(ptr,StateBuffer+intostate,_size*_nelem);
455 intostate+=_size*_nelem;
456}
457
458
459static void areadlower8of16(int8 *d)
460{
461#ifdef LSB_FIRST
462 *d=StateBuffer[intostate++];
463#else
464 d[1]=StateBuffer[intostate++];
465#endif
466}
467
468
469static void areadupper8of16(int8 *d)
470{
471#ifdef LSB_FIRST
472 d[1]=StateBuffer[intostate++];
473#else
474 *d=StateBuffer[intostate++];
475#endif
476}
477
478
479static void aread16(int8 *d)
480{
481#ifdef LSB_FIRST
482 *d=StateBuffer[intostate++];
483 d[1]=StateBuffer[intostate++];
484#else
485 d[1]=StateBuffer[intostate++];
486 *d=StateBuffer[intostate++];
487#endif
488}
489
490
491static void aread32(int8 *d)
492{
493#ifdef LSB_FIRST
494 *d=StateBuffer[intostate++];
495 d[1]=StateBuffer[intostate++];
496 d[2]=StateBuffer[intostate++];
497 d[3]=StateBuffer[intostate++];
498#else
499 d[3]=StateBuffer[intostate++];
500 d[2]=StateBuffer[intostate++];
501 d[1]=StateBuffer[intostate++];
502 *d=StateBuffer[intostate++];
503#endif
504}
505
506static int LoadStateOld(FILE *st)
507{
508 int x;
509 int32 nada;
510 uint8 version;
511 nada=0;
22f08d95 512
890e37ba 513 printf("LoadStateOld\n");
514
c62d2810 515 StateBuffer=FCEU_malloc(59999);
516 if(StateBuffer==NULL)
517 return 0;
518 if(!fread(StateBuffer,59999,1,st))
519 {
520 fclose(st);
521 free(StateBuffer);
522 return 0;
523 }
524
525 intostate=0;
526
527 {
528 uint8 a[2];
529 afread(&a[0],1,1);
530 afread(&a[1],1,1);
531 X.PC=a[0]|(a[1]<<8);
532 }
533 afread(&X.A,1,1);
534 afread(&X.P,1,1);
535 afread(&X.X,1,1);
536 afread(&X.Y,1,1);
537 afread(&X.S,1,1);
538 afread(&version,1,1);
539 afread(&nada,1,1);
540 afread(&nada,1,1);
541 afread(&nada,1,1);
542 afread(&nada,1,1);
543 aread32((int8 *)&X.count);
544 afread(&nada,1,1);
545 afread(&nada,1,1);
546 afread(&nada,1,1);
547 afread(&nada,1,1);
548 aread32((int8 *)&nada);
549 afread(&nada,1,1);
550 afread(&nada,1,1);
551 afread(&nada,1,1);
552 afread(&nada,1,1);
22f08d95 553
c62d2810 554 for(x=0;x<8;x++)
555 areadupper8of16((int8 *)&CHRBankList[x]);
556 afread(PRGBankList,4,1);
557 for(x=0;x<8;x++)
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);
564
565 for(x=0;x<0xF00;x++)
566 afread(&nada,1,1);
567 afread(PALRAM,1,0x20);
568 for(x=0;x<256-32;x++)
569 afread(&nada,1,1);
570 for(x=0x00;x<0x20;x++)
571 PALRAM[x]&=0x3f;
572 afread(PPU,1,4);
573 afread(SPRAM,1,0x100);
574 afread(WRAM,1,8192);
575 afread(RAM,1,0x800);
576 aread16((int8 *)&scanline);
577 aread16((int8 *)&RefreshAddr);
578 afread(&VRAMBuffer,1,1);
22f08d95 579
c62d2810 580 afread(&IRQa,1,1);
581 aread32((int8 *)&IRQCount);
582 aread32((int8 *)&IRQLatch);
583 afread(&Mirroring,1,1);
584 afread(PSG,1,0x17);
585 PSG[0x11]&=0x7F;
586 afread(MapperExRAM,1,193);
587 if(version>=31)
588 PSG[0x17]=MapperExRAM[115];
589 else
590 PSG[0x17]|=0x40;
591 PSG[0x15]&=0xF;
592 sqnon=PSG[0x15];
593
594 X.IRQlow=0;
595 afread(&nada,1,1);
596 afread(&nada,1,1);
597 afread(&nada,1,1);
598 afread(&nada,1,1);
599 afread(&nada,1,1);
600 afread(&nada,1,1);
601 afread(&XOffset,1,1);
602 PPUCHRRAM=0;
603 for(x=0;x<8;x++)
604 {
605 nada=0;
606 afread(&nada,1,1);
22f08d95 607 PPUCHRRAM|=(nada?1:0)<<x;
c62d2810 608 }
22f08d95 609
c62d2810 610 afread(mapbyte1,1,8);
611 afread(mapbyte2,1,8);
612 afread(mapbyte3,1,8);
613 afread(mapbyte4,1,8);
614 for(x=0;x<4;x++)
615 aread16((int8 *)&nada);
22f08d95 616
c62d2810 617 PPUNTARAM=0;
618 for(x=0;x<4;x++)
619 {
620 nada=0;
621 aread16((int8 *)&nada);
622 PPUNTARAM|=((nada&0x800)?0:1)<<x;
623 }
624 afread(MapperExRAM,1,32768);
625 afread(&vtoggle,1,1);
626 aread16((int8 *)&TempAddrT);
627 aread16((int8 *)&RefreshAddrT);
22f08d95 628
c62d2810 629 if(GameStateRestore) GameStateRestore(version);
630 free(StateBuffer);
631 FixOldSaveStateSFreq();
632 X.mooPI=X.P;
633 return 1;
634}
635