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