684610d99bb2e0a8c090c0dd2d03599f40967c8a
[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.h>\r
11 \r
12 #include <cpu/sh2/sh2.h>\r
13 #include "sound/ym2612.h"\r
14 #include "sound/ym2413.h"\r
15 #include "sound/sn76496.h"\r
16 #include "cd/megasd.h"\r
17 #include "state.h"\r
18 \r
19 static arearw    *areaRead;\r
20 static arearw    *areaWrite;\r
21 static areaeof   *areaEof;\r
22 static areaseek  *areaSeek;\r
23 static areaclose *areaClose;\r
24 \r
25 carthw_state_chunk *carthw_chunks;\r
26 void (*PicoStateProgressCB)(const char *str);\r
27 void (*PicoLoadStateHook)(void);\r
28 \r
29 \r
30 /* I/O functions */\r
31 static size_t gzRead2(void *p, size_t _size, size_t _n, void *file)\r
32 {\r
33   return gzread(file, p, _size * _n);\r
34 }\r
35 \r
36 static size_t gzWrite2(void *p, size_t _size, size_t _n, void *file)\r
37 {\r
38   return gzwrite(file, p, _size * _n);\r
39 }\r
40 \r
41 static void set_cbs(int gz)\r
42 {\r
43   if (gz) {\r
44     areaRead  = gzRead2;\r
45     areaWrite = gzWrite2;\r
46     areaEof   = (areaeof *) gzeof;\r
47     areaSeek  = (areaseek *) gzseek;\r
48     areaClose = (areaclose *) gzclose;\r
49   } else {\r
50     areaRead  = (arearw *) fread;\r
51     areaWrite = (arearw *) fwrite;\r
52     areaEof   = (areaeof *) feof;\r
53     areaSeek  = (areaseek *) fseek;\r
54     areaClose = (areaclose *) fclose;\r
55   }\r
56 }\r
57 \r
58 static void *open_save_file(const char *fname, int is_save)\r
59 {\r
60   int len = strlen(fname);\r
61   void *afile = NULL;\r
62 \r
63   if (len > 3 && strcasecmp(fname + len - 3, ".gz") == 0)\r
64   {\r
65     if ( (afile = gzopen(fname, is_save ? "wb" : "rb")) ) {\r
66       set_cbs(1);\r
67       if (is_save)\r
68         gzsetparams(afile, 9, Z_DEFAULT_STRATEGY);\r
69     }\r
70   }\r
71   else\r
72   {\r
73     if ( (afile = fopen(fname, is_save ? "wb" : "rb")) ) {\r
74       set_cbs(0);\r
75     }\r
76   }\r
77 \r
78   return afile;\r
79 }\r
80 \r
81 // ---------------------------------------------------------------------------\r
82 \r
83 typedef enum {\r
84   CHUNK_M68K = 1,\r
85   CHUNK_RAM,\r
86   CHUNK_VRAM,\r
87   CHUNK_ZRAM,\r
88   CHUNK_CRAM,    // 5\r
89   CHUNK_VSRAM,\r
90   CHUNK_MISC,\r
91   CHUNK_VIDEO,\r
92   CHUNK_Z80,\r
93   CHUNK_PSG,     // 10\r
94   CHUNK_FM,\r
95   // CD stuff\r
96   CHUNK_S68K,\r
97   CHUNK_PRG_RAM,\r
98   CHUNK_WORD_RAM,\r
99   CHUNK_PCM_RAM, // 15\r
100   CHUNK_BRAM,\r
101   CHUNK_GA_REGS,\r
102   CHUNK_PCM,\r
103   CHUNK_CDC,     // old\r
104   CHUNK_CDD,     // 20 old\r
105   CHUNK_SCD,     // old\r
106   CHUNK_RC,      // old\r
107   CHUNK_MISC_CD,\r
108   //\r
109   CHUNK_IOPORTS, // old\r
110   CHUNK_SMS,     // 25\r
111   // 32x\r
112   CHUNK_MSH2,\r
113   CHUNK_MSH2_DATA,\r
114   CHUNK_MSH2_PERI,\r
115   CHUNK_SSH2,\r
116   CHUNK_SSH2_DATA, // 30\r
117   CHUNK_SSH2_PERI,\r
118   CHUNK_32XSYS,\r
119   CHUNK_M68K_BIOS,\r
120   CHUNK_MSH2_BIOS,\r
121   CHUNK_SSH2_BIOS, // 35\r
122   CHUNK_SDRAM,\r
123   CHUNK_DRAM,\r
124   CHUNK_32XPAL,\r
125   CHUNK_32X_EVT,\r
126   //rename\r
127   CHUNK_32X_FIRST = CHUNK_MSH2,\r
128   CHUNK_32X_LAST = CHUNK_32X_EVT,\r
129   // add new stuff here\r
130   CHUNK_CD_EVT = 50,\r
131   CHUNK_CD_GFX,\r
132   CHUNK_CD_CDC,\r
133   CHUNK_CD_CDD,\r
134   CHUNK_YM2413,\r
135   CHUNK_PICO_PCM,\r
136   CHUNK_PICO,\r
137   CHUNK_CD_MSD,\r
138   CHUNK_VDP,\r
139   CHUNK_FM_TIMERS,\r
140   CHUNK_FMv3 = 60,\r
141   CHUNK_IOPORTSv2,\r
142   //\r
143   CHUNK_DEFAULT_COUNT,\r
144   CHUNK_CARTHW_ = CHUNK_CARTHW,  // 64 (defined in PicoInt)\r
145 \r
146 } chunk_name_e;\r
147 \r
148 static const char * const chunk_names[CHUNK_DEFAULT_COUNT] = {\r
149   "INVALID!",\r
150   "M68K state",\r
151   "RAM",\r
152   "VRAM",\r
153   "ZRAM",\r
154   "CRAM",      // 5\r
155   "VSRAM",\r
156   "emu state",\r
157   "VIDEO",\r
158   "Z80 state",\r
159   "PSG",       // 10\r
160   "FM",\r
161   // CD stuff\r
162   "S68K state",\r
163   "PRG_RAM",\r
164   "WORD_RAM",\r
165   "PCM_RAM",   // 15\r
166   "BRAM",\r
167   "GATE ARRAY regs",\r
168   "PCM state",\r
169   "CDC",\r
170   "CDD",       // 20\r
171   "SCD",\r
172   "GFX chip",\r
173   "MCD state",\r
174   //\r
175   "IO",\r
176   "SMS state", // 25\r
177   // 32x\r
178   "MSH2",\r
179   "MSH2 data",\r
180   "MSH2 peri",\r
181   "SSH2",\r
182   "SSH2 data", // 30\r
183   "SSH2 peri",\r
184   "32X system regs",\r
185   "M68K BIOS",\r
186   "MSH2 BIOS",\r
187   "SSH2 BIOS", // 35\r
188   "SDRAM",\r
189   "DRAM",\r
190   "32X palette",\r
191   "32X events",\r
192 };\r
193 \r
194 static int write_chunk(unsigned char name, int len, void *data, void *file)\r
195 {\r
196   size_t bwritten = 0;\r
197   bwritten += areaWrite(&name, 1, 1, file);\r
198   bwritten += areaWrite(&len, 1, 4, file);\r
199   bwritten += areaWrite(data, 1, len, file);\r
200 \r
201   return (bwritten == len + 4 + 1);\r
202 }\r
203 \r
204 #define CHUNK_LIMIT_W 18772 // sizeof(cdc)\r
205 \r
206 #define CHECKED_WRITE(name,len,data) { \\r
207   if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT && chunk_names[name]) { \\r
208     strncpy(sbuff + 9, chunk_names[name], sizeof(sbuff)-1 - 9); \\r
209     sbuff[sizeof(sbuff)-1] = '\0'; \\r
210     PicoStateProgressCB(sbuff); \\r
211   } \\r
212   if (data == buf2 && len > CHUNK_LIMIT_W) \\r
213     goto out; \\r
214   if (!write_chunk(name, len, data, file)) \\r
215     goto out; \\r
216 }\r
217 \r
218 #define CHECKED_WRITE_BUFF(name,buff) { \\r
219   if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT && chunk_names[name]) { \\r
220     strncpy(sbuff + 9, chunk_names[name], sizeof(sbuff)-1 - 9); \\r
221     sbuff[sizeof(sbuff)-1] = '\0'; \\r
222     PicoStateProgressCB(sbuff); \\r
223   } \\r
224   if (!write_chunk(name, sizeof(buff), &buff, file)) \\r
225     goto out; \\r
226 }\r
227 \r
228 static int state_save(void *file)\r
229 {\r
230   char sbuff[32] = "Saving.. ";\r
231   unsigned char buff[0x60], buff_z80[Z80_STATE_SIZE];\r
232   void *buf2 = NULL;\r
233   int ver = 0x0191; // not really used..\r
234   int retval = -1;\r
235   int len;\r
236 \r
237   buf2 = malloc(CHUNK_LIMIT_W);\r
238   if (buf2 == NULL)\r
239     return -1;\r
240 \r
241   areaWrite("PicoSEXT", 1, 8, file);\r
242   areaWrite(&ver, 1, 4, file);\r
243 \r
244   if (!(PicoIn.AHW & PAHW_SMS)) {\r
245     // the patches can cause incompatible saves with no-idle\r
246     SekFinishIdleDet();\r
247 \r
248     memset(buff, 0, sizeof(buff));\r
249     SekPackCpu(buff, 0);\r
250     CHECKED_WRITE_BUFF(CHUNK_M68K,  buff);\r
251     CHECKED_WRITE_BUFF(CHUNK_RAM,   PicoMem.ram);\r
252     CHECKED_WRITE_BUFF(CHUNK_VSRAM, PicoMem.vsram);\r
253     CHECKED_WRITE_BUFF(CHUNK_IOPORTS, PicoMem.ioports);\r
254     len = io_ports_pack(buf2, CHUNK_LIMIT_W);\r
255     CHECKED_WRITE(CHUNK_IOPORTSv2, len, buf2);\r
256     if (PicoIn.AHW & PAHW_PICO) {\r
257       len = PicoPicoPCMSave(buf2, CHUNK_LIMIT_W);\r
258       CHECKED_WRITE(CHUNK_PICO_PCM, len, buf2);\r
259       CHECKED_WRITE(CHUNK_PICO, sizeof(PicoPicohw), &PicoPicohw);\r
260     } else {\r
261 #ifdef __GP2X__\r
262       void *ym_regs = YM2612GetRegs();\r
263       ym2612_pack_state_old();\r
264       CHECKED_WRITE(CHUNK_FM, 0x200+4, ym_regs);\r
265 #else\r
266       // write fm state first since timer load needs OPN.ST.mode\r
267       len = YM2612PicoStateSave3(buf2, CHUNK_LIMIT_W);\r
268       CHECKED_WRITE(CHUNK_FMv3, len, buf2);\r
269       len = ym2612_pack_timers(buf2, CHUNK_LIMIT_W);\r
270       CHECKED_WRITE(CHUNK_FM_TIMERS, len, buf2);\r
271 #endif\r
272     }\r
273 \r
274     if (!(PicoIn.opt & POPT_DIS_IDLE_DET))\r
275       SekInitIdleDet();\r
276   }\r
277   else {\r
278     CHECKED_WRITE_BUFF(CHUNK_SMS, Pico.ms);\r
279     // only store the FM unit state if it was really used\r
280     if (Pico.m.hardware & PMS_HW_FMUSED) {\r
281       len = ym2413_pack_state(buf2, CHUNK_LIMIT_W);\r
282       CHECKED_WRITE(CHUNK_YM2413, len, buf2);\r
283     }\r
284   }\r
285   CHECKED_WRITE(CHUNK_PSG, 28*4, sn76496_regs);\r
286 \r
287   if (!(PicoIn.AHW & PAHW_PICO)) {\r
288     z80_pack(buff_z80);\r
289     CHECKED_WRITE_BUFF(CHUNK_Z80, buff_z80);\r
290     CHECKED_WRITE_BUFF(CHUNK_ZRAM,  PicoMem.zram);\r
291   }\r
292 \r
293   CHECKED_WRITE_BUFF(CHUNK_VRAM,  PicoMem.vram);\r
294   CHECKED_WRITE_BUFF(CHUNK_CRAM,  PicoMem.cram);\r
295 \r
296   CHECKED_WRITE_BUFF(CHUNK_MISC,  Pico.m);\r
297   len = PicoVideoSave(buf2);\r
298   CHECKED_WRITE(CHUNK_VDP, len, buf2);\r
299   CHECKED_WRITE_BUFF(CHUNK_VIDEO, Pico.video);\r
300 \r
301   if (PicoIn.AHW & PAHW_MCD)\r
302   {\r
303     memset(buff, 0, sizeof(buff));\r
304     SekPackCpu(buff, 1);\r
305     if (Pico_mcd->s68k_regs[3] & 4) // 1M mode?\r
306       wram_1M_to_2M(Pico_mcd->word_ram2M);\r
307     memcpy(&Pico_mcd->m.hint_vector, Pico_mcd->bios + 0x72,\r
308       sizeof(Pico_mcd->m.hint_vector));\r
309 \r
310     CHECKED_WRITE_BUFF(CHUNK_S68K,     buff);\r
311     CHECKED_WRITE_BUFF(CHUNK_PRG_RAM,  Pico_mcd->prg_ram);\r
312     CHECKED_WRITE_BUFF(CHUNK_WORD_RAM, Pico_mcd->word_ram2M); // in 2M format\r
313     CHECKED_WRITE_BUFF(CHUNK_PCM_RAM,  Pico_mcd->pcm_ram);\r
314     CHECKED_WRITE_BUFF(CHUNK_BRAM,     Pico_mcd->bram);\r
315     CHECKED_WRITE_BUFF(CHUNK_GA_REGS,  Pico_mcd->s68k_regs); // GA regs, not CPU regs\r
316     CHECKED_WRITE_BUFF(CHUNK_PCM,      Pico_mcd->pcm);\r
317     CHECKED_WRITE_BUFF(CHUNK_MISC_CD,  Pico_mcd->m);\r
318     memset(buff, 0, 0x40);\r
319     memcpy(buff, pcd_event_times, sizeof(pcd_event_times));\r
320     CHECKED_WRITE(CHUNK_CD_EVT, 0x40, buff);\r
321 \r
322     len = gfx_context_save(buf2);\r
323     CHECKED_WRITE(CHUNK_CD_GFX, len, buf2);\r
324     len = cdc_context_save(buf2);\r
325     CHECKED_WRITE(CHUNK_CD_CDC, len, buf2);\r
326     len = cdd_context_save(buf2);\r
327     CHECKED_WRITE(CHUNK_CD_CDD, len, buf2);\r
328 \r
329     CHECKED_WRITE_BUFF(CHUNK_CD_MSD, Pico_msd);\r
330 \r
331     if (Pico_mcd->s68k_regs[3] & 4) // convert back\r
332       wram_2M_to_1M(Pico_mcd->word_ram2M);\r
333   }\r
334 \r
335 #ifndef NO_32X\r
336   if (PicoIn.AHW & PAHW_32X)\r
337   {\r
338     unsigned char cpubuff[SH2_STATE_SIZE];\r
339 \r
340     memset(cpubuff, 0, sizeof(cpubuff));\r
341 \r
342     sh2_pack(&sh2s[0], cpubuff);\r
343     CHECKED_WRITE_BUFF(CHUNK_MSH2,      cpubuff);\r
344     CHECKED_WRITE_BUFF(CHUNK_MSH2_DATA, sh2s[0].data_array);\r
345     CHECKED_WRITE_BUFF(CHUNK_MSH2_PERI, sh2s[0].peri_regs);\r
346 \r
347     sh2_pack(&sh2s[1], cpubuff);\r
348     CHECKED_WRITE_BUFF(CHUNK_SSH2,      cpubuff);\r
349     CHECKED_WRITE_BUFF(CHUNK_SSH2_DATA, sh2s[1].data_array);\r
350     CHECKED_WRITE_BUFF(CHUNK_SSH2_PERI, sh2s[1].peri_regs);\r
351 \r
352     CHECKED_WRITE_BUFF(CHUNK_32XSYS,    Pico32x);\r
353     CHECKED_WRITE_BUFF(CHUNK_M68K_BIOS, Pico32xMem->m68k_rom);\r
354     CHECKED_WRITE_BUFF(CHUNK_MSH2_BIOS, Pico32xMem->sh2_rom_m);\r
355     CHECKED_WRITE_BUFF(CHUNK_SSH2_BIOS, Pico32xMem->sh2_rom_s);\r
356     CHECKED_WRITE_BUFF(CHUNK_SDRAM,     Pico32xMem->sdram);\r
357     CHECKED_WRITE_BUFF(CHUNK_DRAM,      Pico32xMem->dram);\r
358     CHECKED_WRITE_BUFF(CHUNK_32XPAL,    Pico32xMem->pal);\r
359 \r
360     memset(buff, 0, 0x40);\r
361     memcpy(buff, p32x_event_times, sizeof(p32x_event_times));\r
362     CHECKED_WRITE(CHUNK_32X_EVT, 0x40, buff);\r
363   }\r
364 #endif\r
365 \r
366   if (carthw_chunks != NULL)\r
367   {\r
368     carthw_state_chunk *chwc;\r
369     if (PicoStateProgressCB)\r
370       PicoStateProgressCB("Saving.. cart hw state");\r
371     for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++)\r
372       CHECKED_WRITE(chwc->chunk, chwc->size, chwc->ptr);\r
373   }\r
374 \r
375   CHECKED_WRITE(0, 0, NULL);\r
376   retval = 0;\r
377 \r
378 out:\r
379   if (buf2 != NULL)\r
380     free(buf2);\r
381   return retval;\r
382 }\r
383 \r
384 static int g_read_offs = 0;\r
385 \r
386 #define R_ERROR_RETURN(error) \\r
387 { \\r
388   elprintf(EL_STATUS, "load_state @ %x: " error, g_read_offs); \\r
389   goto out; \\r
390 }\r
391 \r
392 // when is eof really set?\r
393 #define CHECKED_READ(len,data) { \\r
394   if (areaRead(data, 1, len, file) != len) { \\r
395     if (len == 1 && areaEof(file)) goto readend; \\r
396     R_ERROR_RETURN("areaRead: premature EOF\n"); \\r
397   } \\r
398   g_read_offs += len; \\r
399 }\r
400 \r
401 #define CHECKED_READ2(len2,data) { \\r
402   if (len2 != len) { \\r
403     elprintf(EL_STATUS, "unexpected len %i, wanted %i (%s)", len, len2, #len2); \\r
404     if (len > len2) R_ERROR_RETURN("failed."); \\r
405     /* else read anyway and hope for the best.. */ \\r
406   } \\r
407   CHECKED_READ(len, data); \\r
408 }\r
409 \r
410 #define CHECKED_READ_BUFF(buff) CHECKED_READ2(sizeof(buff), &buff);\r
411 \r
412 #define CHUNK_LIMIT_R 0x10960 // sizeof(old_cdc)\r
413 \r
414 #define CHECKED_READ_LIM(data) { \\r
415   if (len > CHUNK_LIMIT_R) \\r
416     R_ERROR_RETURN("chunk size over limit."); \\r
417   CHECKED_READ(len, data); \\r
418 }\r
419 \r
420 static int state_load(void *file)\r
421 {\r
422   unsigned char buff_m68k[0x60], buff_s68k[0x60];\r
423   unsigned char buff_z80[Z80_STATE_SIZE];\r
424   unsigned char buff_sh2[SH2_STATE_SIZE];\r
425   unsigned char buff_vdp[0x200];\r
426   unsigned char *buf = NULL;\r
427   unsigned char chunk;\r
428   void *ym_regs;\r
429   int len_check;\r
430   int retval = -1;\r
431   char header[8];\r
432   int ver, has_32x = 0;\r
433   int len, len_vdp = 0;\r
434 \r
435   memset(buff_m68k, 0, sizeof(buff_m68k));\r
436   memset(buff_s68k, 0, sizeof(buff_s68k));\r
437   memset(buff_z80, 0, sizeof(buff_z80));\r
438 \r
439   buf = malloc(CHUNK_LIMIT_R);\r
440   if (buf == NULL)\r
441     return -1;\r
442 \r
443   g_read_offs = 0;\r
444   CHECKED_READ(8, header);\r
445   if (strncmp(header, "PicoSMCD", 8) && strncmp(header, "PicoSEXT", 8))\r
446     R_ERROR_RETURN("bad header");\r
447   CHECKED_READ(4, &ver);\r
448 \r
449   memset(pcd_event_times, 0, sizeof(pcd_event_times));\r
450   memset(p32x_event_times, 0, sizeof(p32x_event_times));\r
451 \r
452   while (!areaEof(file))\r
453   {\r
454     len_check = 0;\r
455     CHECKED_READ(1, &chunk);\r
456     CHECKED_READ(4, &len);\r
457     if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");\r
458     if (CHUNK_S68K <= chunk && chunk <= CHUNK_MISC_CD && !(PicoIn.AHW & PAHW_MCD))\r
459       R_ERROR_RETURN("cd chunk in non CD state?");\r
460 \r
461     // 32X only appears in PicoDrive after it has been enabled, so track this\r
462     has_32x |= CHUNK_32X_FIRST <= chunk && chunk <= CHUNK_32X_LAST;\r
463     if (has_32x && !(PicoIn.AHW & PAHW_32X))\r
464       Pico32xStartup();\r
465 \r
466     switch (chunk)\r
467     {\r
468       case CHUNK_M68K:\r
469         CHECKED_READ_BUFF(buff_m68k);\r
470         break;\r
471 \r
472       case CHUNK_Z80:\r
473         CHECKED_READ_BUFF(buff_z80);\r
474         break;\r
475 \r
476       case CHUNK_RAM:     CHECKED_READ_BUFF(PicoMem.ram); break;\r
477       case CHUNK_VRAM:    CHECKED_READ_BUFF(PicoMem.vram); break;\r
478       case CHUNK_ZRAM:    CHECKED_READ_BUFF(PicoMem.zram); break;\r
479       case CHUNK_CRAM:    CHECKED_READ_BUFF(PicoMem.cram); break;\r
480       case CHUNK_VSRAM:   CHECKED_READ_BUFF(PicoMem.vsram); break;\r
481       case CHUNK_MISC:    CHECKED_READ_BUFF(Pico.m); break;\r
482       case CHUNK_VIDEO:   CHECKED_READ_BUFF(Pico.video); break;\r
483       case CHUNK_VDP:     CHECKED_READ2((len_vdp = len), buff_vdp); break;\r
484 \r
485       case CHUNK_IOPORTS: CHECKED_READ_BUFF(PicoMem.ioports); break;\r
486       case CHUNK_PSG:     CHECKED_READ2(28*4, sn76496_regs); break;\r
487       case CHUNK_YM2413:\r
488         CHECKED_READ(len, buf);\r
489         ym2413_unpack_state(buf, len);\r
490         Pico.m.hardware |= PMS_HW_FMUSED;\r
491         break;\r
492       case CHUNK_FM:\r
493         ym_regs = YM2612GetRegs();\r
494         CHECKED_READ2(0x200+4, ym_regs);\r
495         ym2612_unpack_state_old();\r
496         break;\r
497       case CHUNK_FM_TIMERS: CHECKED_READ(len, buf); ym2612_unpack_timers(buf, len); break;\r
498       case CHUNK_FMv3:      CHECKED_READ(len, buf); YM2612PicoStateLoad3(buf, len); break;\r
499       case CHUNK_IOPORTSv2: CHECKED_READ(len, buf); io_ports_unpack(buf, len); break;\r
500 \r
501       case CHUNK_PICO_PCM:\r
502         CHECKED_READ(len, buf);\r
503         PicoPicoPCMLoad(buf, len);\r
504         break;\r
505       case CHUNK_PICO:\r
506         CHECKED_READ_BUFF(PicoPicohw);\r
507         break;\r
508 \r
509       case CHUNK_SMS:\r
510         CHECKED_READ_BUFF(Pico.ms);\r
511         break;\r
512 \r
513       // cd stuff\r
514       case CHUNK_S68K:\r
515         CHECKED_READ_BUFF(buff_s68k);\r
516         break;\r
517 \r
518       case CHUNK_PRG_RAM:  CHECKED_READ_BUFF(Pico_mcd->prg_ram); break;\r
519       case CHUNK_WORD_RAM: CHECKED_READ_BUFF(Pico_mcd->word_ram2M); break;\r
520       case CHUNK_PCM_RAM:  CHECKED_READ_BUFF(Pico_mcd->pcm_ram); break;\r
521       case CHUNK_BRAM:     CHECKED_READ_BUFF(Pico_mcd->bram); break;\r
522       case CHUNK_GA_REGS:  CHECKED_READ_BUFF(Pico_mcd->s68k_regs); break;\r
523       case CHUNK_PCM:      CHECKED_READ_BUFF(Pico_mcd->pcm); break;\r
524       case CHUNK_MISC_CD:  CHECKED_READ_BUFF(Pico_mcd->m); break;\r
525       case CHUNK_CD_MSD:   CHECKED_READ_BUFF(Pico_msd); break;\r
526 \r
527       case CHUNK_CD_EVT:\r
528         CHECKED_READ2(0x40, buf);\r
529         memcpy(pcd_event_times, buf, sizeof(pcd_event_times));\r
530         break;\r
531 \r
532       case CHUNK_CD_GFX:\r
533         CHECKED_READ_LIM(buf);\r
534         len_check = gfx_context_load(buf);\r
535         break;\r
536 \r
537       case CHUNK_CD_CDC:\r
538         CHECKED_READ_LIM(buf);\r
539         len_check = cdc_context_load(buf);\r
540         break;\r
541 \r
542       case CHUNK_CD_CDD:\r
543         CHECKED_READ_LIM(buf);\r
544         len_check = cdd_context_load(buf);\r
545         break;\r
546 \r
547       // old, to be removed:\r
548       case CHUNK_CDC:\r
549         CHECKED_READ_LIM(buf);\r
550         cdc_context_load_old(buf);\r
551         break;\r
552 \r
553       case CHUNK_SCD:\r
554         CHECKED_READ_LIM(buf);\r
555         cdd_context_load_old(buf);\r
556         break;\r
557 \r
558       // 32x stuff\r
559 #ifndef NO_32X\r
560       case CHUNK_MSH2:\r
561         CHECKED_READ_BUFF(buff_sh2);\r
562         sh2_unpack(&sh2s[0], buff_sh2);\r
563         break;\r
564 \r
565       case CHUNK_SSH2:\r
566         CHECKED_READ_BUFF(buff_sh2);\r
567         sh2_unpack(&sh2s[1], buff_sh2);\r
568         break;\r
569 \r
570       case CHUNK_MSH2_DATA:   CHECKED_READ_BUFF(sh2s[0].data_array); break;\r
571       case CHUNK_MSH2_PERI:   CHECKED_READ_BUFF(sh2s[0].peri_regs); break;\r
572       case CHUNK_SSH2_DATA:   CHECKED_READ_BUFF(sh2s[1].data_array); break;\r
573       case CHUNK_SSH2_PERI:   CHECKED_READ_BUFF(sh2s[1].peri_regs); break;\r
574       case CHUNK_32XSYS:      CHECKED_READ_BUFF(Pico32x); break;\r
575       case CHUNK_M68K_BIOS:   CHECKED_READ_BUFF(Pico32xMem->m68k_rom); break;\r
576       case CHUNK_MSH2_BIOS:   CHECKED_READ_BUFF(Pico32xMem->sh2_rom_m); break;\r
577       case CHUNK_SSH2_BIOS:   CHECKED_READ_BUFF(Pico32xMem->sh2_rom_s); break;\r
578       case CHUNK_SDRAM:       CHECKED_READ_BUFF(Pico32xMem->sdram); break;\r
579       case CHUNK_DRAM:        CHECKED_READ_BUFF(Pico32xMem->dram); break;\r
580       case CHUNK_32XPAL:      CHECKED_READ_BUFF(Pico32xMem->pal); break;\r
581 \r
582       case CHUNK_32X_EVT:\r
583         CHECKED_READ2(0x40, buf);\r
584         memcpy(p32x_event_times, buf, sizeof(p32x_event_times));\r
585         break;\r
586 #endif\r
587       default:\r
588         if (!len && !chunk)\r
589            goto readend;\r
590         if (carthw_chunks != NULL)\r
591         {\r
592           carthw_state_chunk *chwc;\r
593           for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++) {\r
594             if (chwc->chunk == chunk) {\r
595               CHECKED_READ2(chwc->size, chwc->ptr);\r
596               goto breakswitch;\r
597             }\r
598           }\r
599         }\r
600         elprintf(EL_STATUS, "load_state: skipping unknown chunk %i of size %i", chunk, len);\r
601         areaSeek(file, len, SEEK_CUR);\r
602         break;\r
603     }\r
604 breakswitch:\r
605     if (len_check != 0 && len_check != len)\r
606       elprintf(EL_STATUS, "load_state: chunk %d has bad len %d/%d",\r
607         len, len_check);\r
608   }\r
609 \r
610 readend:\r
611   PicoVideoLoad(buff_vdp, len_vdp);\r
612 \r
613   if (PicoIn.AHW & PAHW_32X)\r
614     if (!has_32x)\r
615       Pico32xShutdown(); // in case of loading a state with 32X disabled\r
616 \r
617   if (PicoIn.AHW & PAHW_SMS)\r
618     PicoStateLoadedMS();\r
619 \r
620   if (PicoIn.AHW & PAHW_32X)\r
621     Pico32xStateLoaded(1);\r
622 \r
623   if (PicoLoadStateHook != NULL)\r
624     PicoLoadStateHook();\r
625 \r
626   // must unpack 68k and z80 after banks are set up\r
627   if (!(PicoIn.AHW & PAHW_SMS))\r
628     SekUnpackCpu(buff_m68k, 0);\r
629   if (PicoIn.AHW & PAHW_MCD)\r
630     SekUnpackCpu(buff_s68k, 1);\r
631 \r
632   z80_unpack(buff_z80);\r
633 \r
634   if (PicoIn.AHW & PAHW_32X)\r
635     Pico32xStateLoaded(0);\r
636   if (PicoIn.AHW & PAHW_MCD)\r
637     pcd_state_loaded();\r
638   if (!(PicoIn.AHW & PAHW_SMS)) {\r
639     Pico.video.status &= ~(SR_VB | SR_F);\r
640     Pico.video.status |= ((Pico.video.reg[1] >> 3) ^ SR_VB) & SR_VB;\r
641     Pico.video.status |= (Pico.video.pending_ints << 2) & SR_F;\r
642   }\r
643 \r
644   Pico.m.dirtyPal = 1;\r
645   retval = 0;\r
646 \r
647 out:\r
648   free(buf);\r
649   return retval;\r
650 }\r
651 \r
652 static int state_load_gfx(void *file)\r
653 {\r
654   int ver, len, found = 0, to_find = 4;\r
655   u8 buff_vdp[0x200];\r
656   int len_vdp = 0;\r
657   char buff[8];\r
658 \r
659   if (PicoIn.AHW & PAHW_32X)\r
660     to_find += 3;\r
661 \r
662   g_read_offs = 0;\r
663   CHECKED_READ(8, buff);\r
664   if (strncmp((char *)buff, "PicoSMCD", 8) && strncmp((char *)buff, "PicoSEXT", 8))\r
665     R_ERROR_RETURN("bad header");\r
666   CHECKED_READ(4, &ver);\r
667 \r
668   while (!areaEof(file) && found < to_find)\r
669   {\r
670     CHECKED_READ(1, buff);\r
671     CHECKED_READ(4, &len);\r
672     if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");\r
673     if (buff[0] > CHUNK_FM && buff[0] <= CHUNK_MISC_CD && !(PicoIn.AHW & PAHW_MCD))\r
674       R_ERROR_RETURN("cd chunk in non CD state?");\r
675 \r
676     switch (buff[0])\r
677     {\r
678       case CHUNK_VRAM:  CHECKED_READ_BUFF(PicoMem.vram);  found++; break;\r
679       case CHUNK_CRAM:  CHECKED_READ_BUFF(PicoMem.cram);  found++; break;\r
680       case CHUNK_VSRAM: CHECKED_READ_BUFF(PicoMem.vsram); found++; break;\r
681       case CHUNK_VIDEO: CHECKED_READ_BUFF(Pico.video); found++; break;\r
682       case CHUNK_VDP:   CHECKED_READ2((len_vdp = len), buff_vdp); break;\r
683 \r
684 #ifndef NO_32X\r
685       case CHUNK_DRAM:\r
686         if (Pico32xMem != NULL)\r
687           CHECKED_READ_BUFF(Pico32xMem->dram);\r
688         found++;\r
689         break;\r
690 \r
691       case CHUNK_32XPAL:\r
692         if (Pico32xMem != NULL)\r
693           CHECKED_READ_BUFF(Pico32xMem->pal);\r
694         found++;\r
695         Pico32x.dirty_pal = 1;\r
696         break;\r
697 \r
698       case CHUNK_32XSYS:\r
699         CHECKED_READ_BUFF(Pico32x);\r
700         found++;\r
701         break;\r
702 #endif\r
703       default:\r
704         areaSeek(file, len, SEEK_CUR);\r
705         break;\r
706     }\r
707   }\r
708   PicoVideoLoad(buff_vdp, len_vdp);\r
709 \r
710 out:\r
711 readend:\r
712   return 0;\r
713 }\r
714 \r
715 static int pico_state_internal(void *afile, int is_save)\r
716 {\r
717   int ret;\r
718 \r
719   if (is_save)\r
720     ret = state_save(afile);\r
721   else\r
722     ret = state_load(afile);\r
723 \r
724   return ret;\r
725 }\r
726 \r
727 int PicoState(const char *fname, int is_save)\r
728 {\r
729   void *afile = NULL;\r
730   int ret;\r
731 \r
732   afile = open_save_file(fname, is_save);\r
733   if (afile == NULL)\r
734     return -1;\r
735 \r
736   ret = pico_state_internal(afile, is_save);\r
737   areaClose(afile);\r
738   return ret;\r
739 }\r
740 \r
741 int PicoStateFP(void *afile, int is_save,\r
742   arearw *read, arearw *write, areaeof *eof, areaseek *seek)\r
743 {\r
744   areaRead  = read;\r
745   areaWrite = write;\r
746   areaEof   = eof;\r
747   areaSeek  = seek;\r
748   areaClose = NULL;\r
749 \r
750   return pico_state_internal(afile, is_save);\r
751 }\r
752 \r
753 int PicoStateLoadGfx(const char *fname)\r
754 {\r
755   void *afile;\r
756   int ret;\r
757 \r
758   afile = open_save_file(fname, 0);\r
759   if (afile == NULL)\r
760     return -1;\r
761 \r
762   ret = state_load_gfx(afile);\r
763   if (ret != 0) {\r
764     // assume legacy\r
765     areaSeek(afile, 0x10020, SEEK_SET);  // skip header and RAM\r
766     areaRead(PicoMem.vram, 1, sizeof(PicoMem.vram), afile);\r
767     areaSeek(afile, 0x2000, SEEK_CUR);\r
768     areaRead(PicoMem.cram, 1, sizeof(PicoMem.cram), afile);\r
769     areaRead(PicoMem.vsram, 1, sizeof(PicoMem.vsram), afile);\r
770     areaSeek(afile, 0x221a0, SEEK_SET);\r
771     areaRead(&Pico.video, 1, sizeof(Pico.video), afile);\r
772     PicoVideoCacheSAT(1);\r
773   }\r
774   areaClose(afile);\r
775 \r
776   Pico.est.rendstatus = -1;\r
777   return 0;\r
778 }\r
779 \r
780 // tmp state\r
781 struct PicoTmp\r
782 {\r
783   unsigned short vram[0x8000];\r
784   unsigned short cram[0x40];\r
785   unsigned short vsram[0x40];\r
786   unsigned int satcache[2*0x80];\r
787 \r
788   //struct PicoMisc m;\r
789   struct PicoVideo video;\r
790   u8 vdp[0x200];\r
791   int vdp_len;\r
792 \r
793   struct {\r
794     struct Pico32x p32x;\r
795     unsigned short dram[2][0x20000/2];\r
796     unsigned short pal[0x100];\r
797   } t32x;\r
798 };\r
799 \r
800 // returns data ptr to free() or PicoTmpStateRestore()\r
801 void *PicoTmpStateSave(void)\r
802 {\r
803   // gfx only for now\r
804   struct PicoTmp *t = malloc(sizeof(*t));\r
805   if (t == NULL)\r
806     return NULL;\r
807 \r
808   memcpy(t->vram, PicoMem.vram, sizeof(PicoMem.vram));\r
809   memcpy(t->cram, PicoMem.cram, sizeof(PicoMem.cram));\r
810   memcpy(t->vsram, PicoMem.vsram, sizeof(PicoMem.vsram));\r
811   memcpy(t->satcache, VdpSATCache, sizeof(VdpSATCache));\r
812   memcpy(&t->video, &Pico.video, sizeof(Pico.video));\r
813   t->vdp_len = PicoVideoSave(t->vdp);\r
814 \r
815 #ifndef NO_32X\r
816   if (PicoIn.AHW & PAHW_32X) {\r
817     memcpy(&t->t32x.p32x, &Pico32x, sizeof(Pico32x));\r
818     memcpy(t->t32x.dram, Pico32xMem->dram, sizeof(Pico32xMem->dram));\r
819     memcpy(t->t32x.pal, Pico32xMem->pal, sizeof(Pico32xMem->pal));\r
820   }\r
821 #endif\r
822 \r
823   return t;\r
824 }\r
825 \r
826 void PicoTmpStateRestore(void *data)\r
827 {\r
828   struct PicoTmp *t = data;\r
829   if (t == NULL)\r
830     return;\r
831 \r
832   memcpy(PicoMem.vram, t->vram, sizeof(PicoMem.vram));\r
833   memcpy(PicoMem.cram, t->cram, sizeof(PicoMem.cram));\r
834   memcpy(PicoMem.vsram, t->vsram, sizeof(PicoMem.vsram));\r
835   memcpy(VdpSATCache, t->satcache, sizeof(VdpSATCache));\r
836   memcpy(&Pico.video, &t->video, sizeof(Pico.video));\r
837   Pico.m.dirtyPal = 1;\r
838   PicoVideoLoad(t->vdp, t->vdp_len);\r
839 \r
840 #ifndef NO_32X\r
841   if (PicoIn.AHW & PAHW_32X) {\r
842     memcpy(&Pico32x, &t->t32x.p32x, sizeof(Pico32x));\r
843     memcpy(Pico32xMem->dram, t->t32x.dram, sizeof(Pico32xMem->dram));\r
844     memcpy(Pico32xMem->pal, t->t32x.pal, sizeof(Pico32xMem->pal));\r
845     Pico32x.dirty_pal = 1;\r
846   }\r
847 #endif\r
848   free(t);\r
849 }\r
850 \r
851 // vim:shiftwidth=2:ts=2:expandtab\r