save event times to savestates
[picodrive.git] / pico / state.c
1 /*\r
2  * PicoDrive\r
3  * (C) notaz, 2010\r
4  *\r
5  * This work is licensed under the terms of MAME license.\r
6  * See COPYING file in the top-level directory.\r
7  */\r
8 \r
9 #include "pico_int.h"\r
10 #include <zlib/zlib.h>\r
11 \r
12 #include "../cpu/sh2/sh2.h"\r
13 #include "sound/ym2612.h"\r
14 \r
15 // sn76496\r
16 extern int *sn76496_regs;\r
17 \r
18 typedef size_t (arearw)(void *p, size_t _size, size_t _n, void *file);\r
19 typedef size_t (areaeof)(void *file);\r
20 typedef int    (areaseek)(void *file, long offset, int whence);\r
21 typedef int    (areaclose)(void *file);\r
22 \r
23 static arearw    *areaRead;\r
24 static arearw    *areaWrite;\r
25 static areaeof   *areaEof;\r
26 static areaseek  *areaSeek;\r
27 static areaclose *areaClose;\r
28 \r
29 carthw_state_chunk *carthw_chunks;\r
30 void (*PicoStateProgressCB)(const char *str);\r
31 void (*PicoLoadStateHook)(void);\r
32 \r
33 \r
34 /* I/O functions */\r
35 static size_t gzRead2(void *p, size_t _size, size_t _n, void *file)\r
36 {\r
37   return gzread(file, p, _size * _n);\r
38 }\r
39 \r
40 static size_t gzWrite2(void *p, size_t _size, size_t _n, void *file)\r
41 {\r
42   return gzwrite(file, p, _size * _n);\r
43 }\r
44 \r
45 static void set_cbs(int gz)\r
46 {\r
47   if (gz) {\r
48     areaRead  = gzRead2;\r
49     areaWrite = gzWrite2;\r
50     areaEof   = (areaeof *) gzeof;\r
51     areaSeek  = (areaseek *) gzseek;\r
52     areaClose = (areaclose *) gzclose;\r
53   } else {\r
54     areaRead  = (arearw *) fread;\r
55     areaWrite = (arearw *) fwrite;\r
56     areaEof   = (areaeof *) feof;\r
57     areaSeek  = (areaseek *) fseek;\r
58     areaClose = (areaclose *) fclose;\r
59   }\r
60 }\r
61 \r
62 static void *open_save_file(const char *fname, int is_save)\r
63 {\r
64   int len = strlen(fname);\r
65   void *afile = NULL;\r
66 \r
67   if (len > 3 && strcmp(fname + len - 3, ".gz") == 0)\r
68   {\r
69     if ( (afile = gzopen(fname, is_save ? "wb" : "rb")) ) {\r
70       set_cbs(1);\r
71       if (is_save)\r
72         gzsetparams(afile, 9, Z_DEFAULT_STRATEGY);\r
73     }\r
74   }\r
75   else\r
76   {\r
77     if ( (afile = fopen(fname, is_save ? "wb" : "rb")) ) {\r
78       set_cbs(0);\r
79     }\r
80   }\r
81 \r
82   return afile;\r
83 }\r
84 \r
85 // legacy savestate loading\r
86 #define SCANP(f, x) areaRead(&Pico.x, sizeof(Pico.x), 1, f)\r
87 \r
88 static int state_load_legacy(void *file)\r
89 {\r
90   unsigned char head[32];\r
91   unsigned char cpu[0x60];\r
92   unsigned char cpu_z80[Z80_STATE_SIZE];\r
93   void *ym2612_regs;\r
94   int ok;\r
95 \r
96   memset(&cpu,0,sizeof(cpu));\r
97   memset(&cpu_z80,0,sizeof(cpu_z80));\r
98 \r
99   memset(head, 0, sizeof(head));\r
100   areaRead(head, sizeof(head), 1, file);\r
101   if (strcmp((char *)head, "Pico") != 0)\r
102     return -1;\r
103 \r
104   elprintf(EL_STATUS, "legacy savestate");\r
105 \r
106   // Scan all the memory areas:\r
107   SCANP(file, ram);\r
108   SCANP(file, vram);\r
109   SCANP(file, zram);\r
110   SCANP(file, cram);\r
111   SCANP(file, vsram);\r
112 \r
113   // Pack, scan and unpack the cpu data:\r
114   areaRead(cpu, sizeof(cpu), 1, file);\r
115   SekUnpackCpu(cpu, 0);\r
116 \r
117   SCANP(file, m);\r
118   SCANP(file, video);\r
119 \r
120   ok = areaRead(cpu_z80, sizeof(cpu_z80), 1, file) == sizeof(cpu_z80);\r
121   // do not unpack if we fail to load z80 state\r
122   if (!ok) z80_reset();\r
123   else     z80_unpack(cpu_z80);\r
124 \r
125   ym2612_regs = YM2612GetRegs();\r
126   areaRead(sn76496_regs, 28*4, 1, file);\r
127   areaRead(ym2612_regs, 0x200+4, 1, file);\r
128   ym2612_unpack_state();\r
129 \r
130   return 0;\r
131 }\r
132 \r
133 // ---------------------------------------------------------------------------\r
134 \r
135 typedef enum {\r
136   CHUNK_M68K = 1,\r
137   CHUNK_RAM,\r
138   CHUNK_VRAM,\r
139   CHUNK_ZRAM,\r
140   CHUNK_CRAM,    // 5\r
141   CHUNK_VSRAM,\r
142   CHUNK_MISC,\r
143   CHUNK_VIDEO,\r
144   CHUNK_Z80,\r
145   CHUNK_PSG,     // 10\r
146   CHUNK_FM,\r
147   // CD stuff\r
148   CHUNK_S68K,\r
149   CHUNK_PRG_RAM,\r
150   CHUNK_WORD_RAM,\r
151   CHUNK_PCM_RAM, // 15\r
152   CHUNK_BRAM,\r
153   CHUNK_GA_REGS,\r
154   CHUNK_PCM,\r
155   CHUNK_CDC,\r
156   CHUNK_CDD,     // 20\r
157   CHUNK_SCD,\r
158   CHUNK_RC,\r
159   CHUNK_MISC_CD,\r
160   //\r
161   CHUNK_IOPORTS, // versions < 1.70 did not save that..\r
162   CHUNK_SMS,     // 25\r
163   // 32x\r
164   CHUNK_MSH2,\r
165   CHUNK_MSH2_DATA,\r
166   CHUNK_MSH2_PERI,\r
167   CHUNK_SSH2,\r
168   CHUNK_SSH2_DATA, // 30\r
169   CHUNK_SSH2_PERI,\r
170   CHUNK_32XSYS,\r
171   CHUNK_M68K_BIOS,\r
172   CHUNK_MSH2_BIOS,\r
173   CHUNK_SSH2_BIOS, // 35\r
174   CHUNK_SDRAM,\r
175   CHUNK_DRAM,\r
176   CHUNK_32XPAL,\r
177   CHUNK_32X_EVT,\r
178   //\r
179   CHUNK_DEFAULT_COUNT,\r
180   CHUNK_CARTHW_ = CHUNK_CARTHW,  // defined in PicoInt\r
181 } chunk_name_e;\r
182 \r
183 static const char * const chunk_names[] = {\r
184   "INVALID!",\r
185   "M68K state",\r
186   "RAM",\r
187   "VRAM",\r
188   "ZRAM",\r
189   "CRAM",      // 5\r
190   "VSRAM",\r
191   "emu state",\r
192   "VIDEO",\r
193   "Z80 state",\r
194   "PSG",       // 10\r
195   "FM",\r
196   // CD stuff\r
197   "S68K state",\r
198   "PRG_RAM",\r
199   "WORD_RAM",\r
200   "PCM_RAM",   // 15\r
201   "BRAM",\r
202   "GATE ARRAY regs",\r
203   "PCM state",\r
204   "CDC",\r
205   "CDD",       // 20\r
206   "SCD",\r
207   "GFX chip",\r
208   "MCD state",\r
209   //\r
210   "IO",\r
211   "SMS state", // 25\r
212   // 32x\r
213   "MSH2",\r
214   "MSH2 data",\r
215   "MSH2 peri",\r
216   "SSH2",\r
217   "SSH2 data", // 30\r
218   "SSH2 peri",\r
219   "32X system regs",\r
220   "M68K BIOS",\r
221   "MSH2 BIOS",\r
222   "SSH2 BIOS", // 35\r
223   "SDRAM",\r
224   "DRAM",\r
225   "PAL",\r
226   "events",\r
227 };\r
228 \r
229 static int write_chunk(chunk_name_e name, int len, void *data, void *file)\r
230 {\r
231   size_t bwritten = 0;\r
232   bwritten += areaWrite(&name, 1, 1, file);\r
233   bwritten += areaWrite(&len, 1, 4, file);\r
234   bwritten += areaWrite(data, 1, len, file);\r
235 \r
236   return (bwritten == len + 4 + 1);\r
237 }\r
238 \r
239 #define CHECKED_WRITE(name,len,data) { \\r
240   if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) { \\r
241     strncpy(sbuff + 9, chunk_names[name], sizeof(sbuff) - 9); \\r
242     PicoStateProgressCB(sbuff); \\r
243   } \\r
244   if (!write_chunk(name, len, data, file)) return 1; \\r
245 }\r
246 \r
247 #define CHECKED_WRITE_BUFF(name,buff) { \\r
248   if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) { \\r
249     strncpy(sbuff + 9, chunk_names[name], sizeof(sbuff) - 9); \\r
250     PicoStateProgressCB(sbuff); \\r
251   } \\r
252   if (!write_chunk(name, sizeof(buff), &buff, file)) return 1; \\r
253 }\r
254 \r
255 static int state_save(void *file)\r
256 {\r
257   char sbuff[32] = "Saving.. ";\r
258   unsigned char buff[0x60], buff_z80[Z80_STATE_SIZE];\r
259   void *ym2612_regs = YM2612GetRegs();\r
260   int ver = 0x0170; // not really used..\r
261 \r
262   areaWrite("PicoSEXT", 1, 8, file);\r
263   areaWrite(&ver, 1, 4, file);\r
264 \r
265   if (!(PicoAHW & PAHW_SMS)) {\r
266     memset(buff, 0, sizeof(buff));\r
267     SekPackCpu(buff, 0);\r
268     CHECKED_WRITE_BUFF(CHUNK_M68K,  buff);\r
269     CHECKED_WRITE_BUFF(CHUNK_RAM,   Pico.ram);\r
270     CHECKED_WRITE_BUFF(CHUNK_VSRAM, Pico.vsram);\r
271     CHECKED_WRITE_BUFF(CHUNK_IOPORTS, Pico.ioports);\r
272     ym2612_pack_state();\r
273     CHECKED_WRITE(CHUNK_FM, 0x200+4, ym2612_regs);\r
274   }\r
275   else {\r
276     CHECKED_WRITE_BUFF(CHUNK_SMS, Pico.ms);\r
277   }\r
278 \r
279   CHECKED_WRITE_BUFF(CHUNK_VRAM,  Pico.vram);\r
280   CHECKED_WRITE_BUFF(CHUNK_ZRAM,  Pico.zram);\r
281   CHECKED_WRITE_BUFF(CHUNK_CRAM,  Pico.cram);\r
282   CHECKED_WRITE_BUFF(CHUNK_MISC,  Pico.m);\r
283   CHECKED_WRITE_BUFF(CHUNK_VIDEO, Pico.video);\r
284 \r
285   z80_pack(buff_z80);\r
286   CHECKED_WRITE_BUFF(CHUNK_Z80, buff_z80);\r
287   CHECKED_WRITE(CHUNK_PSG, 28*4, sn76496_regs);\r
288 \r
289   if (PicoAHW & PAHW_MCD)\r
290   {\r
291     memset(buff, 0, sizeof(buff));\r
292     SekPackCpu(buff, 1);\r
293     if (Pico_mcd->s68k_regs[3] & 4) // 1M mode?\r
294       wram_1M_to_2M(Pico_mcd->word_ram2M);\r
295     Pico_mcd->m.hint_vector = *(unsigned short *)(Pico_mcd->bios + 0x72);\r
296 \r
297     CHECKED_WRITE_BUFF(CHUNK_S68K,     buff);\r
298     CHECKED_WRITE_BUFF(CHUNK_PRG_RAM,  Pico_mcd->prg_ram);\r
299     CHECKED_WRITE_BUFF(CHUNK_WORD_RAM, Pico_mcd->word_ram2M); // in 2M format\r
300     CHECKED_WRITE_BUFF(CHUNK_PCM_RAM,  Pico_mcd->pcm_ram);\r
301     CHECKED_WRITE_BUFF(CHUNK_BRAM,     Pico_mcd->bram);\r
302     CHECKED_WRITE_BUFF(CHUNK_GA_REGS,  Pico_mcd->s68k_regs); // GA regs, not CPU regs\r
303     CHECKED_WRITE_BUFF(CHUNK_PCM,      Pico_mcd->pcm);\r
304     CHECKED_WRITE_BUFF(CHUNK_CDD,      Pico_mcd->cdd);\r
305     CHECKED_WRITE_BUFF(CHUNK_CDC,      Pico_mcd->cdc);\r
306     CHECKED_WRITE_BUFF(CHUNK_SCD,      Pico_mcd->scd);\r
307     CHECKED_WRITE_BUFF(CHUNK_RC,       Pico_mcd->rot_comp);\r
308     CHECKED_WRITE_BUFF(CHUNK_MISC_CD,  Pico_mcd->m);\r
309 \r
310     if (Pico_mcd->s68k_regs[3] & 4) // convert back\r
311       wram_2M_to_1M(Pico_mcd->word_ram2M);\r
312   }\r
313 \r
314 #ifndef NO_32X\r
315   if (PicoAHW & PAHW_32X)\r
316   {\r
317     unsigned char cpubuff[SH2_STATE_SIZE];\r
318 \r
319     memset(cpubuff, 0, sizeof(cpubuff));\r
320 \r
321     sh2_pack(&sh2s[0], cpubuff);\r
322     CHECKED_WRITE_BUFF(CHUNK_MSH2,      cpubuff);\r
323     CHECKED_WRITE_BUFF(CHUNK_MSH2_DATA, Pico32xMem->data_array[0]);\r
324     CHECKED_WRITE_BUFF(CHUNK_MSH2_PERI, Pico32xMem->sh2_peri_regs[0]);\r
325 \r
326     sh2_pack(&sh2s[1], cpubuff);\r
327     CHECKED_WRITE_BUFF(CHUNK_SSH2,      cpubuff);\r
328     CHECKED_WRITE_BUFF(CHUNK_SSH2_DATA, Pico32xMem->data_array[1]);\r
329     CHECKED_WRITE_BUFF(CHUNK_SSH2_PERI, Pico32xMem->sh2_peri_regs[1]);\r
330 \r
331     CHECKED_WRITE_BUFF(CHUNK_32XSYS,    Pico32x);\r
332     CHECKED_WRITE_BUFF(CHUNK_M68K_BIOS, Pico32xMem->m68k_rom);\r
333     CHECKED_WRITE_BUFF(CHUNK_MSH2_BIOS, Pico32xMem->sh2_rom_m);\r
334     CHECKED_WRITE_BUFF(CHUNK_SSH2_BIOS, Pico32xMem->sh2_rom_s);\r
335     CHECKED_WRITE_BUFF(CHUNK_SDRAM,     Pico32xMem->sdram);\r
336     CHECKED_WRITE_BUFF(CHUNK_DRAM,      Pico32xMem->dram);\r
337     CHECKED_WRITE_BUFF(CHUNK_32XPAL,    Pico32xMem->pal);\r
338 \r
339     memset(buff, 0, 0x40);\r
340     memcpy(buff, event_times, sizeof(event_times));\r
341     CHECKED_WRITE(CHUNK_32X_EVT, 0x40, buff);\r
342   }\r
343 #endif\r
344 \r
345   if (carthw_chunks != NULL)\r
346   {\r
347     carthw_state_chunk *chwc;\r
348     if (PicoStateProgressCB)\r
349       PicoStateProgressCB("Saving.. cart hw state");\r
350     for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++)\r
351       CHECKED_WRITE(chwc->chunk, chwc->size, chwc->ptr);\r
352   }\r
353 \r
354   return 0;\r
355 }\r
356 \r
357 static int g_read_offs = 0;\r
358 \r
359 #define R_ERROR_RETURN(error) \\r
360 { \\r
361   elprintf(EL_STATUS, "load_state @ %x: " error, g_read_offs); \\r
362   return 1; \\r
363 }\r
364 \r
365 // when is eof really set?\r
366 #define CHECKED_READ(len,data) { \\r
367   if (areaRead(data, 1, len, file) != len) { \\r
368     if (len == 1 && areaEof(file)) goto readend; \\r
369     R_ERROR_RETURN("areaRead: premature EOF\n"); \\r
370     return 1; \\r
371   } \\r
372   g_read_offs += len; \\r
373 }\r
374 \r
375 #define CHECKED_READ2(len2,data) { \\r
376   if (len2 != len) { \\r
377     elprintf(EL_STATUS, "unexpected len %i, wanted %i (%s)", len, len2, #len2); \\r
378     if (len > len2) R_ERROR_RETURN("failed."); \\r
379     /* else read anyway and hope for the best.. */ \\r
380   } \\r
381   CHECKED_READ(len, data); \\r
382 }\r
383 \r
384 #define CHECKED_READ_BUFF(buff) CHECKED_READ2(sizeof(buff), &buff);\r
385 \r
386 static int state_load(void *file)\r
387 {\r
388   unsigned char buff_m68k[0x60], buff_s68k[0x60];\r
389   unsigned char buff_z80[Z80_STATE_SIZE];\r
390   unsigned char buff_sh2[SH2_STATE_SIZE];\r
391   unsigned char buff[0x40];\r
392   unsigned char chunk;\r
393   void *ym2612_regs;\r
394   char header[8];\r
395   int ver, len;\r
396 \r
397   memset(buff_m68k, 0, sizeof(buff_m68k));\r
398   memset(buff_s68k, 0, sizeof(buff_s68k));\r
399   memset(buff_z80, 0, sizeof(buff_z80));\r
400 \r
401   g_read_offs = 0;\r
402   CHECKED_READ(8, header);\r
403   if (strncmp(header, "PicoSMCD", 8) && strncmp(header, "PicoSEXT", 8))\r
404     R_ERROR_RETURN("bad header");\r
405   CHECKED_READ(4, &ver);\r
406 \r
407   while (!areaEof(file))\r
408   {\r
409     CHECKED_READ(1, &chunk);\r
410     CHECKED_READ(4, &len);\r
411     if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");\r
412     if (CHUNK_S68K <= chunk && chunk <= CHUNK_MISC_CD && !(PicoAHW & PAHW_MCD))\r
413       R_ERROR_RETURN("cd chunk in non CD state?");\r
414     if (CHUNK_MSH2 <= chunk && chunk <= CHUNK_32XPAL && !(PicoAHW & PAHW_32X))\r
415       R_ERROR_RETURN("32x chunk in non 32x state?");\r
416 \r
417     switch (chunk)\r
418     {\r
419       case CHUNK_M68K:\r
420         CHECKED_READ_BUFF(buff_m68k);\r
421         break;\r
422 \r
423       case CHUNK_Z80:\r
424         CHECKED_READ_BUFF(buff_z80);\r
425         break;\r
426 \r
427       case CHUNK_RAM:     CHECKED_READ_BUFF(Pico.ram); break;\r
428       case CHUNK_VRAM:    CHECKED_READ_BUFF(Pico.vram); break;\r
429       case CHUNK_ZRAM:    CHECKED_READ_BUFF(Pico.zram); break;\r
430       case CHUNK_CRAM:    CHECKED_READ_BUFF(Pico.cram); break;\r
431       case CHUNK_VSRAM:   CHECKED_READ_BUFF(Pico.vsram); break;\r
432       case CHUNK_MISC:    CHECKED_READ_BUFF(Pico.m); break;\r
433       case CHUNK_VIDEO:   CHECKED_READ_BUFF(Pico.video); break;\r
434       case CHUNK_IOPORTS: CHECKED_READ_BUFF(Pico.ioports); break;\r
435       case CHUNK_PSG:     CHECKED_READ2(28*4, sn76496_regs); break;\r
436       case CHUNK_FM:\r
437         ym2612_regs = YM2612GetRegs();\r
438         CHECKED_READ2(0x200+4, ym2612_regs);\r
439         ym2612_unpack_state();\r
440         break;\r
441 \r
442       case CHUNK_SMS:\r
443         CHECKED_READ_BUFF(Pico.ms);\r
444         break;\r
445 \r
446       // cd stuff\r
447       case CHUNK_S68K:\r
448         CHECKED_READ_BUFF(buff_s68k);\r
449         break;\r
450 \r
451       case CHUNK_PRG_RAM:  CHECKED_READ_BUFF(Pico_mcd->prg_ram); break;\r
452       case CHUNK_WORD_RAM: CHECKED_READ_BUFF(Pico_mcd->word_ram2M); break;\r
453       case CHUNK_PCM_RAM:  CHECKED_READ_BUFF(Pico_mcd->pcm_ram); break;\r
454       case CHUNK_BRAM:     CHECKED_READ_BUFF(Pico_mcd->bram); break;\r
455       case CHUNK_GA_REGS:  CHECKED_READ_BUFF(Pico_mcd->s68k_regs); break;\r
456       case CHUNK_PCM:      CHECKED_READ_BUFF(Pico_mcd->pcm); break;\r
457       case CHUNK_CDD:      CHECKED_READ_BUFF(Pico_mcd->cdd); break;\r
458       case CHUNK_CDC:      CHECKED_READ_BUFF(Pico_mcd->cdc); break;\r
459       case CHUNK_SCD:      CHECKED_READ_BUFF(Pico_mcd->scd); break;\r
460       case CHUNK_RC:       CHECKED_READ_BUFF(Pico_mcd->rot_comp); break;\r
461       case CHUNK_MISC_CD:  CHECKED_READ_BUFF(Pico_mcd->m); break;\r
462 \r
463       // 32x stuff\r
464 #ifndef NO_32X\r
465       case CHUNK_MSH2:\r
466         CHECKED_READ_BUFF(buff_sh2);\r
467         sh2_unpack(&sh2s[0], buff_sh2);\r
468         break;\r
469 \r
470       case CHUNK_SSH2:\r
471         CHECKED_READ_BUFF(buff_sh2);\r
472         sh2_unpack(&sh2s[1], buff_sh2);\r
473         break;\r
474 \r
475       case CHUNK_MSH2_DATA:   CHECKED_READ_BUFF(Pico32xMem->data_array[0]); break;\r
476       case CHUNK_MSH2_PERI:   CHECKED_READ_BUFF(Pico32xMem->sh2_peri_regs[0]); break;\r
477       case CHUNK_SSH2_DATA:   CHECKED_READ_BUFF(Pico32xMem->data_array[1]); break;\r
478       case CHUNK_SSH2_PERI:   CHECKED_READ_BUFF(Pico32xMem->sh2_peri_regs[1]); break;\r
479       case CHUNK_32XSYS:      CHECKED_READ_BUFF(Pico32x); break;\r
480       case CHUNK_M68K_BIOS:   CHECKED_READ_BUFF(Pico32xMem->m68k_rom); break;\r
481       case CHUNK_MSH2_BIOS:   CHECKED_READ_BUFF(Pico32xMem->sh2_rom_m); break;\r
482       case CHUNK_SSH2_BIOS:   CHECKED_READ_BUFF(Pico32xMem->sh2_rom_s); break;\r
483       case CHUNK_SDRAM:       CHECKED_READ_BUFF(Pico32xMem->sdram); break;\r
484       case CHUNK_DRAM:        CHECKED_READ_BUFF(Pico32xMem->dram); break;\r
485       case CHUNK_32XPAL:      CHECKED_READ_BUFF(Pico32xMem->pal); break;\r
486 \r
487       case CHUNK_32X_EVT:\r
488         CHECKED_READ_BUFF(buff);\r
489         memcpy(event_times, buff, sizeof(event_times));\r
490         break;\r
491 #endif\r
492       default:\r
493         if (carthw_chunks != NULL)\r
494         {\r
495           carthw_state_chunk *chwc;\r
496           for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++) {\r
497             if (chwc->chunk == chunk) {\r
498               CHECKED_READ2(chwc->size, chwc->ptr);\r
499               goto breakswitch;\r
500             }\r
501           }\r
502         }\r
503         elprintf(EL_STATUS, "load_state: skipping unknown chunk %i of size %i", chunk, len);\r
504         areaSeek(file, len, SEEK_CUR);\r
505         break;\r
506     }\r
507 breakswitch:;\r
508   }\r
509 \r
510 readend:\r
511   if (PicoAHW & PAHW_SMS)\r
512     PicoStateLoadedMS();\r
513 \r
514   if (PicoAHW & PAHW_MCD)\r
515   {\r
516     PicoMemStateLoaded();\r
517 \r
518     if (!(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1))\r
519       cdda_start_play();\r
520 \r
521     SekUnpackCpu(buff_s68k, 1);\r
522   }\r
523 \r
524   // must unpack 68k and z80 after banks are set up\r
525   if (!(PicoAHW & PAHW_SMS))\r
526     SekUnpackCpu(buff_m68k, 0);\r
527 \r
528   z80_unpack(buff_z80);\r
529 \r
530   if (PicoAHW & PAHW_32X)\r
531     Pico32xStateLoaded();\r
532 \r
533   return 0;\r
534 }\r
535 \r
536 static int state_load_gfx(void *file)\r
537 {\r
538   int ver, len, found = 0, to_find = 4;\r
539   char buff[8];\r
540 \r
541   if (PicoAHW & PAHW_32X)\r
542     to_find += 2;\r
543 \r
544   g_read_offs = 0;\r
545   CHECKED_READ(8, buff);\r
546   if (strncmp((char *)buff, "PicoSMCD", 8) && strncmp((char *)buff, "PicoSEXT", 8))\r
547     R_ERROR_RETURN("bad header");\r
548   CHECKED_READ(4, &ver);\r
549 \r
550   while (!areaEof(file) && found < to_find)\r
551   {\r
552     CHECKED_READ(1, buff);\r
553     CHECKED_READ(4, &len);\r
554     if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");\r
555     if (buff[0] > CHUNK_FM && buff[0] <= CHUNK_MISC_CD && !(PicoAHW & PAHW_MCD))\r
556       R_ERROR_RETURN("cd chunk in non CD state?");\r
557 \r
558     switch (buff[0])\r
559     {\r
560       case CHUNK_VRAM:  CHECKED_READ_BUFF(Pico.vram);  found++; break;\r
561       case CHUNK_CRAM:  CHECKED_READ_BUFF(Pico.cram);  found++; break;\r
562       case CHUNK_VSRAM: CHECKED_READ_BUFF(Pico.vsram); found++; break;\r
563       case CHUNK_VIDEO: CHECKED_READ_BUFF(Pico.video); found++; break;\r
564 \r
565 #ifndef NO_32X\r
566       case CHUNK_DRAM:\r
567         if (Pico32xMem != NULL)\r
568           CHECKED_READ_BUFF(Pico32xMem->dram);\r
569         break;\r
570 \r
571       case CHUNK_32XPAL:\r
572         if (Pico32xMem != NULL)\r
573           CHECKED_READ_BUFF(Pico32xMem->pal);\r
574         Pico32x.dirty_pal = 1;\r
575         break;\r
576 \r
577       case CHUNK_32XSYS:\r
578         CHECKED_READ_BUFF(Pico32x);\r
579         break;\r
580 #endif\r
581       default:\r
582         areaSeek(file, len, SEEK_CUR);\r
583         break;\r
584     }\r
585   }\r
586 \r
587 readend:\r
588   return 0;\r
589 }\r
590 \r
591 int PicoState(const char *fname, int is_save)\r
592 {\r
593   void *afile = NULL;\r
594   int ret;\r
595 \r
596   afile = open_save_file(fname, is_save);\r
597   if (afile == NULL)\r
598     return -1;\r
599 \r
600   if (is_save)\r
601     ret = state_save(afile);\r
602   else {\r
603     ret = state_load(afile);\r
604     if (ret != 0) {\r
605       areaSeek(afile, 0, SEEK_SET);\r
606       ret = state_load_legacy(afile);\r
607     }\r
608 \r
609     if (PicoLoadStateHook != NULL)\r
610       PicoLoadStateHook();\r
611     Pico.m.dirtyPal = 1;\r
612   }\r
613 \r
614   areaClose(afile);\r
615   return ret;\r
616 }\r
617 \r
618 int PicoStateLoadGfx(const char *fname)\r
619 {\r
620   void *afile;\r
621   int ret;\r
622 \r
623   afile = open_save_file(fname, 0);\r
624   if (afile == NULL)\r
625     return -1;\r
626 \r
627   ret = state_load_gfx(afile);\r
628   if (ret != 0) {\r
629     // assume legacy\r
630     areaSeek(afile, 0x10020, SEEK_SET);  // skip header and RAM\r
631     areaRead(Pico.vram, 1, sizeof(Pico.vram), afile);\r
632     areaSeek(afile, 0x2000, SEEK_CUR);\r
633     areaRead(Pico.cram, 1, sizeof(Pico.cram), afile);\r
634     areaRead(Pico.vsram, 1, sizeof(Pico.vsram), afile);\r
635     areaSeek(afile, 0x221a0, SEEK_SET);\r
636     areaRead(&Pico.video, 1, sizeof(Pico.video), afile);\r
637   }\r
638   areaClose(afile);\r
639   return 0;\r
640 }\r
641 \r
642 // tmp state\r
643 struct PicoTmp\r
644 {\r
645   unsigned short vram[0x8000];\r
646   unsigned short cram[0x40];\r
647   unsigned short vsram[0x40];\r
648 \r
649   //struct PicoMisc m;\r
650   struct PicoVideo video;\r
651 \r
652   struct {\r
653     struct Pico32x p32x;\r
654     unsigned short dram[2][0x20000/2];\r
655     unsigned short pal[0x100];\r
656   } t32x;\r
657 };\r
658 \r
659 // returns data ptr to free() or PicoTmpStateRestore()\r
660 void *PicoTmpStateSave(void)\r
661 {\r
662   // gfx only for now\r
663   struct PicoTmp *t = malloc(sizeof(*t));\r
664   if (t == NULL)\r
665     return NULL;\r
666 \r
667   memcpy(t->vram, Pico.vram, sizeof(Pico.vram));\r
668   memcpy(t->cram, Pico.cram, sizeof(Pico.cram));\r
669   memcpy(t->vsram, Pico.vsram, sizeof(Pico.vsram));\r
670   memcpy(&t->video, &Pico.video, sizeof(Pico.video));\r
671 \r
672 #ifndef NO_32X\r
673   if (PicoAHW & PAHW_32X) {\r
674     memcpy(&t->t32x.p32x, &Pico32x, sizeof(Pico32x));\r
675     memcpy(t->t32x.dram, Pico32xMem->dram, sizeof(Pico32xMem->dram));\r
676     memcpy(t->t32x.pal, Pico32xMem->pal, sizeof(Pico32xMem->pal));\r
677   }\r
678 #endif\r
679 \r
680   return t;\r
681 }\r
682 \r
683 void PicoTmpStateRestore(void *data)\r
684 {\r
685   struct PicoTmp *t = data;\r
686   if (t == NULL)\r
687     return;\r
688 \r
689   memcpy(Pico.vram, t->vram, sizeof(Pico.vram));\r
690   memcpy(Pico.cram, t->cram, sizeof(Pico.cram));\r
691   memcpy(Pico.vsram, t->vsram, sizeof(Pico.vsram));\r
692   memcpy(&Pico.video, &t->video, sizeof(Pico.video));\r
693   Pico.m.dirtyPal = 1;\r
694 \r
695 #ifndef NO_32X\r
696   if (PicoAHW & PAHW_32X) {\r
697     memcpy(&Pico32x, &t->t32x.p32x, sizeof(Pico32x));\r
698     memcpy(Pico32xMem->dram, t->t32x.dram, sizeof(Pico32xMem->dram));\r
699     memcpy(Pico32xMem->pal, t->t32x.pal, sizeof(Pico32xMem->pal));\r
700     Pico32x.dirty_pal = 1;\r
701   }\r
702 #endif\r
703 }\r
704 \r
705 // vim:shiftwidth=2:ts=2:expandtab\r