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