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