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