| 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 |