some more risky timing changes
[picodrive.git] / pico / state.c
index 11eee6f..f4e685f 100644 (file)
@@ -7,19 +7,15 @@
  */\r
 \r
 #include "pico_int.h"\r
-#include <zlib/zlib.h>\r
+#include <zlib.h>\r
 \r
 #include "../cpu/sh2/sh2.h"\r
 #include "sound/ym2612.h"\r
+#include "state.h"\r
 \r
 // sn76496\r
 extern int *sn76496_regs;\r
 \r
-typedef size_t (arearw)(void *p, size_t _size, size_t _n, void *file);\r
-typedef size_t (areaeof)(void *file);\r
-typedef int    (areaseek)(void *file, long offset, int whence);\r
-typedef int    (areaclose)(void *file);\r
-\r
 static arearw    *areaRead;\r
 static arearw    *areaWrite;\r
 static areaeof   *areaEof;\r
@@ -152,10 +148,10 @@ typedef enum {
   CHUNK_BRAM,\r
   CHUNK_GA_REGS,\r
   CHUNK_PCM,\r
-  CHUNK_CDC,\r
-  CHUNK_CDD,     // 20\r
-  CHUNK_SCD,\r
-  CHUNK_RC,\r
+  CHUNK_CDC,     // old\r
+  CHUNK_CDD,     // 20 old\r
+  CHUNK_SCD,     // old\r
+  CHUNK_RC,      // old\r
   CHUNK_MISC_CD,\r
   //\r
   CHUNK_IOPORTS, // versions < 1.70 did not save that..\r
@@ -177,12 +173,17 @@ typedef enum {
   CHUNK_32X_EVT,\r
   CHUNK_32X_FIRST = CHUNK_MSH2,\r
   CHUNK_32X_LAST = CHUNK_32X_EVT,\r
+  // add new stuff here\r
+  CHUNK_CD_EVT = 50,\r
+  CHUNK_CD_GFX,\r
+  CHUNK_CD_CDC,\r
+  CHUNK_CD_CDD,\r
   //\r
   CHUNK_DEFAULT_COUNT,\r
-  CHUNK_CARTHW_ = CHUNK_CARTHW,  // defined in PicoInt\r
+  CHUNK_CARTHW_ = CHUNK_CARTHW,  // 64 (defined in PicoInt)\r
 } chunk_name_e;\r
 \r
-static const char * const chunk_names[] = {\r
+static const char * const chunk_names[CHUNK_DEFAULT_COUNT] = {\r
   "INVALID!",\r
   "M68K state",\r
   "RAM",\r
@@ -238,20 +239,26 @@ static int write_chunk(chunk_name_e name, int len, void *data, void *file)
   return (bwritten == len + 4 + 1);\r
 }\r
 \r
+#define CHUNK_LIMIT_W 18772 // sizeof(cdc)\r
+\r
 #define CHECKED_WRITE(name,len,data) { \\r
-  if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) { \\r
+  if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT && chunk_names[name]) { \\r
     strncpy(sbuff + 9, chunk_names[name], sizeof(sbuff) - 9); \\r
     PicoStateProgressCB(sbuff); \\r
   } \\r
-  if (!write_chunk(name, len, data, file)) return 1; \\r
+  if (data == buf2 && len > CHUNK_LIMIT_W) \\r
+    goto out; \\r
+  if (!write_chunk(name, len, data, file)) \\r
+    goto out; \\r
 }\r
 \r
 #define CHECKED_WRITE_BUFF(name,buff) { \\r
-  if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) { \\r
+  if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT && chunk_names[name]) { \\r
     strncpy(sbuff + 9, chunk_names[name], sizeof(sbuff) - 9); \\r
     PicoStateProgressCB(sbuff); \\r
   } \\r
-  if (!write_chunk(name, sizeof(buff), &buff, file)) return 1; \\r
+  if (!write_chunk(name, sizeof(buff), &buff, file)) \\r
+    goto out; \\r
 }\r
 \r
 static int state_save(void *file)\r
@@ -259,7 +266,10 @@ static int state_save(void *file)
   char sbuff[32] = "Saving.. ";\r
   unsigned char buff[0x60], buff_z80[Z80_STATE_SIZE];\r
   void *ym2612_regs = YM2612GetRegs();\r
-  int ver = 0x0170; // not really used..\r
+  void *buf2 = NULL;\r
+  int ver = 0x0191; // not really used..\r
+  int retval = -1;\r
+  int len;\r
 \r
   areaWrite("PicoSEXT", 1, 8, file);\r
   areaWrite(&ver, 1, 4, file);\r
@@ -290,11 +300,16 @@ static int state_save(void *file)
 \r
   if (PicoAHW & PAHW_MCD)\r
   {\r
+    buf2 = malloc(CHUNK_LIMIT_W);\r
+    if (buf2 == NULL)\r
+      return -1;\r
+\r
     memset(buff, 0, sizeof(buff));\r
     SekPackCpu(buff, 1);\r
     if (Pico_mcd->s68k_regs[3] & 4) // 1M mode?\r
       wram_1M_to_2M(Pico_mcd->word_ram2M);\r
-    Pico_mcd->m.hint_vector = *(unsigned short *)(Pico_mcd->bios + 0x72);\r
+    memcpy(&Pico_mcd->m.hint_vector, Pico_mcd->bios + 0x72,\r
+      sizeof(Pico_mcd->m.hint_vector));\r
 \r
     CHECKED_WRITE_BUFF(CHUNK_S68K,     buff);\r
     CHECKED_WRITE_BUFF(CHUNK_PRG_RAM,  Pico_mcd->prg_ram);\r
@@ -303,11 +318,17 @@ static int state_save(void *file)
     CHECKED_WRITE_BUFF(CHUNK_BRAM,     Pico_mcd->bram);\r
     CHECKED_WRITE_BUFF(CHUNK_GA_REGS,  Pico_mcd->s68k_regs); // GA regs, not CPU regs\r
     CHECKED_WRITE_BUFF(CHUNK_PCM,      Pico_mcd->pcm);\r
-    CHECKED_WRITE_BUFF(CHUNK_CDD,      Pico_mcd->cdd);\r
-    CHECKED_WRITE_BUFF(CHUNK_CDC,      Pico_mcd->cdc);\r
-    CHECKED_WRITE_BUFF(CHUNK_SCD,      Pico_mcd->scd);\r
-    CHECKED_WRITE_BUFF(CHUNK_RC,       Pico_mcd->rot_comp);\r
     CHECKED_WRITE_BUFF(CHUNK_MISC_CD,  Pico_mcd->m);\r
+    memset(buff, 0, 0x40);\r
+    memcpy(buff, pcd_event_times, sizeof(pcd_event_times));\r
+    CHECKED_WRITE(CHUNK_CD_EVT, 0x40, buff);\r
+\r
+    len = gfx_context_save(buf2);\r
+    CHECKED_WRITE(CHUNK_CD_GFX, len, buf2);\r
+    len = cdc_context_save(buf2);\r
+    CHECKED_WRITE(CHUNK_CD_CDC, len, buf2);\r
+    len = cdd_context_save(buf2);\r
+    CHECKED_WRITE(CHUNK_CD_CDD, len, buf2);\r
 \r
     if (Pico_mcd->s68k_regs[3] & 4) // convert back\r
       wram_2M_to_1M(Pico_mcd->word_ram2M);\r
@@ -322,13 +343,13 @@ static int state_save(void *file)
 \r
     sh2_pack(&sh2s[0], cpubuff);\r
     CHECKED_WRITE_BUFF(CHUNK_MSH2,      cpubuff);\r
-    CHECKED_WRITE_BUFF(CHUNK_MSH2_DATA, Pico32xMem->data_array[0]);\r
-    CHECKED_WRITE_BUFF(CHUNK_MSH2_PERI, Pico32xMem->sh2_peri_regs[0]);\r
+    CHECKED_WRITE_BUFF(CHUNK_MSH2_DATA, sh2s[0].data_array);\r
+    CHECKED_WRITE_BUFF(CHUNK_MSH2_PERI, sh2s[0].peri_regs);\r
 \r
     sh2_pack(&sh2s[1], cpubuff);\r
     CHECKED_WRITE_BUFF(CHUNK_SSH2,      cpubuff);\r
-    CHECKED_WRITE_BUFF(CHUNK_SSH2_DATA, Pico32xMem->data_array[1]);\r
-    CHECKED_WRITE_BUFF(CHUNK_SSH2_PERI, Pico32xMem->sh2_peri_regs[1]);\r
+    CHECKED_WRITE_BUFF(CHUNK_SSH2_DATA, sh2s[1].data_array);\r
+    CHECKED_WRITE_BUFF(CHUNK_SSH2_PERI, sh2s[1].peri_regs);\r
 \r
     CHECKED_WRITE_BUFF(CHUNK_32XSYS,    Pico32x);\r
     CHECKED_WRITE_BUFF(CHUNK_M68K_BIOS, Pico32xMem->m68k_rom);\r
@@ -339,7 +360,7 @@ static int state_save(void *file)
     CHECKED_WRITE_BUFF(CHUNK_32XPAL,    Pico32xMem->pal);\r
 \r
     memset(buff, 0, 0x40);\r
-    memcpy(buff, event_times, sizeof(event_times));\r
+    memcpy(buff, p32x_event_times, sizeof(p32x_event_times));\r
     CHECKED_WRITE(CHUNK_32X_EVT, 0x40, buff);\r
   }\r
 #endif\r
@@ -353,7 +374,12 @@ static int state_save(void *file)
       CHECKED_WRITE(chwc->chunk, chwc->size, chwc->ptr);\r
   }\r
 \r
-  return 0;\r
+  retval = 0;\r
+\r
+out:\r
+  if (buf2 != NULL)\r
+    free(buf2);\r
+  return retval;\r
 }\r
 \r
 static int g_read_offs = 0;\r
@@ -361,7 +387,7 @@ static int g_read_offs = 0;
 #define R_ERROR_RETURN(error) \\r
 { \\r
   elprintf(EL_STATUS, "load_state @ %x: " error, g_read_offs); \\r
-  return 1; \\r
+  goto out; \\r
 }\r
 \r
 // when is eof really set?\r
@@ -369,7 +395,6 @@ static int g_read_offs = 0;
   if (areaRead(data, 1, len, file) != len) { \\r
     if (len == 1 && areaEof(file)) goto readend; \\r
     R_ERROR_RETURN("areaRead: premature EOF\n"); \\r
-    return 1; \\r
   } \\r
   g_read_offs += len; \\r
 }\r
@@ -385,14 +410,24 @@ static int g_read_offs = 0;
 \r
 #define CHECKED_READ_BUFF(buff) CHECKED_READ2(sizeof(buff), &buff);\r
 \r
+#define CHUNK_LIMIT_R 0x10960 // sizeof(old_cdc)\r
+\r
+#define CHECKED_READ_LIM(data) { \\r
+  if (len > CHUNK_LIMIT_R) \\r
+    R_ERROR_RETURN("chunk size over limit."); \\r
+  CHECKED_READ(len, data); \\r
+}\r
+\r
 static int state_load(void *file)\r
 {\r
   unsigned char buff_m68k[0x60], buff_s68k[0x60];\r
   unsigned char buff_z80[Z80_STATE_SIZE];\r
   unsigned char buff_sh2[SH2_STATE_SIZE];\r
-  unsigned char buff[0x40];\r
+  unsigned char *buf = NULL;\r
   unsigned char chunk;\r
   void *ym2612_regs;\r
+  int len_check;\r
+  int retval = -1;\r
   char header[8];\r
   int ver, len;\r
 \r
@@ -400,14 +435,22 @@ static int state_load(void *file)
   memset(buff_s68k, 0, sizeof(buff_s68k));\r
   memset(buff_z80, 0, sizeof(buff_z80));\r
 \r
+  buf = malloc(CHUNK_LIMIT_R);\r
+  if (buf == NULL)\r
+    return -1;\r
+\r
   g_read_offs = 0;\r
   CHECKED_READ(8, header);\r
   if (strncmp(header, "PicoSMCD", 8) && strncmp(header, "PicoSEXT", 8))\r
     R_ERROR_RETURN("bad header");\r
   CHECKED_READ(4, &ver);\r
 \r
+  memset(pcd_event_times, 0, sizeof(pcd_event_times));\r
+  memset(p32x_event_times, 0, sizeof(p32x_event_times));\r
+\r
   while (!areaEof(file))\r
   {\r
+    len_check = 0;\r
     CHECKED_READ(1, &chunk);\r
     CHECKED_READ(4, &len);\r
     if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");\r
@@ -456,12 +499,39 @@ static int state_load(void *file)
       case CHUNK_BRAM:     CHECKED_READ_BUFF(Pico_mcd->bram); break;\r
       case CHUNK_GA_REGS:  CHECKED_READ_BUFF(Pico_mcd->s68k_regs); break;\r
       case CHUNK_PCM:      CHECKED_READ_BUFF(Pico_mcd->pcm); break;\r
-      case CHUNK_CDD:      CHECKED_READ_BUFF(Pico_mcd->cdd); break;\r
-      case CHUNK_CDC:      CHECKED_READ_BUFF(Pico_mcd->cdc); break;\r
-      case CHUNK_SCD:      CHECKED_READ_BUFF(Pico_mcd->scd); break;\r
-      case CHUNK_RC:       CHECKED_READ_BUFF(Pico_mcd->rot_comp); break;\r
       case CHUNK_MISC_CD:  CHECKED_READ_BUFF(Pico_mcd->m); break;\r
 \r
+      case CHUNK_CD_EVT:\r
+        CHECKED_READ2(0x40, buf);\r
+        memcpy(pcd_event_times, buf, sizeof(pcd_event_times));\r
+        break;\r
+\r
+      case CHUNK_CD_GFX:\r
+        CHECKED_READ_LIM(buf);\r
+        len_check = gfx_context_load(buf);\r
+        break;\r
+\r
+      case CHUNK_CD_CDC:\r
+        CHECKED_READ_LIM(buf);\r
+        len_check = cdc_context_load(buf);\r
+        break;\r
+\r
+      case CHUNK_CD_CDD:\r
+        CHECKED_READ_LIM(buf);\r
+        len_check = cdd_context_load(buf);\r
+        break;\r
+\r
+      // old, to be removed:\r
+      case CHUNK_CDC:\r
+        CHECKED_READ_LIM(buf);\r
+        cdc_context_load_old(buf);\r
+        break;\r
+\r
+      case CHUNK_SCD:\r
+        CHECKED_READ_LIM(buf);\r
+        cdd_context_load_old(buf);\r
+        break;\r
+\r
       // 32x stuff\r
 #ifndef NO_32X\r
       case CHUNK_MSH2:\r
@@ -474,10 +544,10 @@ static int state_load(void *file)
         sh2_unpack(&sh2s[1], buff_sh2);\r
         break;\r
 \r
-      case CHUNK_MSH2_DATA:   CHECKED_READ_BUFF(Pico32xMem->data_array[0]); break;\r
-      case CHUNK_MSH2_PERI:   CHECKED_READ_BUFF(Pico32xMem->sh2_peri_regs[0]); break;\r
-      case CHUNK_SSH2_DATA:   CHECKED_READ_BUFF(Pico32xMem->data_array[1]); break;\r
-      case CHUNK_SSH2_PERI:   CHECKED_READ_BUFF(Pico32xMem->sh2_peri_regs[1]); break;\r
+      case CHUNK_MSH2_DATA:   CHECKED_READ_BUFF(sh2s[0].data_array); break;\r
+      case CHUNK_MSH2_PERI:   CHECKED_READ_BUFF(sh2s[0].peri_regs); break;\r
+      case CHUNK_SSH2_DATA:   CHECKED_READ_BUFF(sh2s[1].data_array); break;\r
+      case CHUNK_SSH2_PERI:   CHECKED_READ_BUFF(sh2s[1].peri_regs); break;\r
       case CHUNK_32XSYS:      CHECKED_READ_BUFF(Pico32x); break;\r
       case CHUNK_M68K_BIOS:   CHECKED_READ_BUFF(Pico32xMem->m68k_rom); break;\r
       case CHUNK_MSH2_BIOS:   CHECKED_READ_BUFF(Pico32xMem->sh2_rom_m); break;\r
@@ -487,8 +557,8 @@ static int state_load(void *file)
       case CHUNK_32XPAL:      CHECKED_READ_BUFF(Pico32xMem->pal); break;\r
 \r
       case CHUNK_32X_EVT:\r
-        CHECKED_READ_BUFF(buff);\r
-        memcpy(event_times, buff, sizeof(event_times));\r
+        CHECKED_READ2(0x40, buf);\r
+        memcpy(p32x_event_times, buf, sizeof(p32x_event_times));\r
         break;\r
 #endif\r
       default:\r
@@ -506,24 +576,22 @@ static int state_load(void *file)
         areaSeek(file, len, SEEK_CUR);\r
         break;\r
     }\r
-breakswitch:;\r
+breakswitch:\r
+    if (len_check != 0 && len_check != len)\r
+      elprintf(EL_STATUS, "load_state: chunk %d has bad len %d/%d",\r
+        len, len_check);\r
   }\r
 \r
 readend:\r
   if (PicoAHW & PAHW_SMS)\r
     PicoStateLoadedMS();\r
 \r
-  if (PicoAHW & PAHW_MCD)\r
-  {\r
-    PicoMemStateLoaded();\r
-\r
-    if (!(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1))\r
-      cdda_start_play();\r
-  }\r
-\r
   if (PicoAHW & PAHW_32X)\r
     Pico32xStateLoaded(1);\r
 \r
+  if (PicoLoadStateHook != NULL)\r
+    PicoLoadStateHook();\r
+\r
   // must unpack 68k and z80 after banks are set up\r
   if (!(PicoAHW & PAHW_SMS))\r
     SekUnpackCpu(buff_m68k, 0);\r
@@ -533,10 +601,20 @@ readend:
   z80_unpack(buff_z80);\r
 \r
   // due to dep from 68k cycles..\r
+  SekCycleAim = SekCycleCnt;\r
   if (PicoAHW & PAHW_32X)\r
     Pico32xStateLoaded(0);\r
+  if (PicoAHW & PAHW_MCD)\r
+  {\r
+    SekCycleAimS68k = SekCycleCntS68k;\r
+    pcd_state_loaded();\r
+  }\r
 \r
-  return 0;\r
+  retval = 0;\r
+\r
+out:\r
+  free(buf);\r
+  return retval;\r
 }\r
 \r
 static int state_load_gfx(void *file)\r
@@ -590,19 +668,15 @@ static int state_load_gfx(void *file)
     }\r
   }\r
 \r
+out:\r
 readend:\r
   return 0;\r
 }\r
 \r
-int PicoState(const char *fname, int is_save)\r
+static int pico_state_internal(void *afile, int is_save)\r
 {\r
-  void *afile = NULL;\r
   int ret;\r
 \r
-  afile = open_save_file(fname, is_save);\r
-  if (afile == NULL)\r
-    return -1;\r
-\r
   if (is_save)\r
     ret = state_save(afile);\r
   else {\r
@@ -612,15 +686,39 @@ int PicoState(const char *fname, int is_save)
       ret = state_load_legacy(afile);\r
     }\r
 \r
-    if (PicoLoadStateHook != NULL)\r
-      PicoLoadStateHook();\r
     Pico.m.dirtyPal = 1;\r
+    Pico.video.status &= ~(SR_VB | SR_F);\r
   }\r
 \r
+  return ret;\r
+}\r
+\r
+int PicoState(const char *fname, int is_save)\r
+{\r
+  void *afile = NULL;\r
+  int ret;\r
+\r
+  afile = open_save_file(fname, is_save);\r
+  if (afile == NULL)\r
+    return -1;\r
+\r
+  ret = pico_state_internal(afile, is_save);\r
   areaClose(afile);\r
   return ret;\r
 }\r
 \r
+int PicoStateFP(void *afile, int is_save,\r
+  arearw *read, arearw *write, areaeof *eof, areaseek *seek)\r
+{\r
+  areaRead  = read;\r
+  areaWrite = write;\r
+  areaEof   = eof;\r
+  areaSeek  = seek;\r
+  areaClose = NULL;\r
+\r
+  return pico_state_internal(afile, is_save);\r
+}\r
+\r
 int PicoStateLoadGfx(const char *fname)\r
 {\r
   void *afile;\r