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