libretro: add savestate support
[picodrive.git] / platform / libretro.c
index 0521f18..d46cc4a 100644 (file)
 #endif
 
 #include <pico/pico_int.h>
+#include <pico/state.h>
 #include "common/input_pico.h"
 #include "common/version.h"
 #include "libretro.h"
 
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
 static retro_video_refresh_t video_cb;
 static retro_input_poll_t input_poll_cb;
 static retro_input_state_t input_state_cb;
@@ -36,10 +41,6 @@ static int vout_width, vout_height;
 
 static short __attribute__((aligned(4))) sndBuffer[2*44100/50];
 
-// FIXME: these 2 shouldn't be here
-static unsigned char PicoDraw2FB_[(8+320) * (8+240+8)];
-unsigned char *PicoDraw2FB = PicoDraw2FB_;
-
 static void snd_write(int len);
 
 #ifdef _WIN32
@@ -92,11 +93,35 @@ void *plat_mmap(unsigned long addr, size_t size, int need_exec, int is_fixed)
 
 void *plat_mremap(void *ptr, size_t oldsize, size_t newsize)
 {
+#ifdef __linux__
        void *ret = mremap(ptr, oldsize, newsize, 0);
        if (ret == MAP_FAILED)
                return NULL;
 
        return ret;
+#else
+       void *tmp, *ret;
+       size_t preserve_size;
+       
+       preserve_size = oldsize;
+       if (preserve_size > newsize)
+               preserve_size = newsize;
+       tmp = malloc(preserve_size);
+       if (tmp == NULL)
+               return NULL;
+       memcpy(tmp, ptr, preserve_size);
+
+       munmap(ptr, oldsize);
+       ret = mmap(ptr, newsize, PROT_READ | PROT_WRITE,
+                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+       if (ret == MAP_FAILED) {
+               free(tmp);
+               return NULL;
+       }
+       memcpy(ret, tmp, preserve_size);
+       free(tmp);
+       return ret;
+#endif
 }
 
 void plat_munmap(void *ptr, size_t size)
@@ -123,7 +148,6 @@ void emu_video_mode_change(int start_line, int line_count, int is_32cols)
 
 void emu_32x_startup(void)
 {
-       PicoDrawSetOutFormat(PDF_RGB555, 1);
 }
 
 #ifndef ANDROID
@@ -187,7 +211,7 @@ void retro_get_system_info(struct retro_system_info *info)
        memset(info, 0, sizeof(*info));
        info->library_name = "PicoDrive";
        info->library_version = VERSION;
-       info->valid_extensions = "bin|gen|smd|32x|cue|iso|sms";
+       info->valid_extensions = "bin|gen|smd|md|32x|cue|iso|sms";
        info->need_fullpath = true;
 }
 
@@ -203,20 +227,124 @@ void retro_get_system_av_info(struct retro_system_av_info *info)
        info->geometry.aspect_ratio = 4.0 / 3.0;
 }
 
-/* savestates - TODO */
+/* savestates */
+struct savestate_state {
+       const char *load_buf;
+       char *save_buf;
+       size_t size;
+       size_t pos;
+};
+
+size_t state_read(void *p, size_t size, size_t nmemb, void *file)
+{
+       struct savestate_state *state = file;
+       size_t bsize = size * nmemb;
+
+       if (state->pos + bsize > state->size) {
+               lprintf("savestate error: %u/%u\n",
+                       state->pos + bsize, state->size);
+               bsize = state->size - state->pos;
+               if ((int)bsize <= 0)
+                       return 0;
+       }
+
+       memcpy(p, state->load_buf + state->pos, bsize);
+       state->pos += bsize;
+       return bsize;
+}
+
+size_t state_write(void *p, size_t size, size_t nmemb, void *file)
+{
+       struct savestate_state *state = file;
+       size_t bsize = size * nmemb;
+
+       if (state->pos + bsize > state->size) {
+               lprintf("savestate error: %u/%u\n",
+                       state->pos + bsize, state->size);
+               bsize = state->size - state->pos;
+               if ((int)bsize <= 0)
+                       return 0;
+       }
+
+       memcpy(state->save_buf + state->pos, p, bsize);
+       state->pos += bsize;
+       return bsize;
+}
+
+size_t state_skip(void *p, size_t size, size_t nmemb, void *file)
+{
+       struct savestate_state *state = file;
+       size_t bsize = size * nmemb;
+
+       state->pos += bsize;
+       return bsize;
+}
+
+size_t state_eof(void *file)
+{
+       struct savestate_state *state = file;
+
+       return state->pos >= state->size;
+}
+
+int state_fseek(void *file, long offset, int whence)
+{
+       struct savestate_state *state = file;
+
+       switch (whence) {
+       case SEEK_SET:
+               state->pos = offset;
+               break;
+       case SEEK_CUR:
+               state->pos += offset;
+               break;
+       case SEEK_END:
+               state->pos = state->size + offset;
+               break;
+       }
+       return (int)state->pos;
+}
+
+/* savestate sizes vary wildly depending if cd/32x or
+ * carthw is active, so run the whole thing to get size */
 size_t retro_serialize_size(void) 
 { 
-       return 0;
+       struct savestate_state state = { 0, };
+       int ret;
+
+       ret = PicoStateFP(&state, 1, NULL, state_skip, NULL, state_fseek);
+       if (ret != 0)
+               return 0;
+
+       return state.pos;
 }
 
 bool retro_serialize(void *data, size_t size)
 { 
-       return false;
+       struct savestate_state state = { 0, };
+       int ret;
+
+       state.save_buf = data;
+       state.size = size;
+       state.pos = 0;
+
+       ret = PicoStateFP(&state, 1, NULL, state_write,
+               NULL, state_fseek);
+       return ret == 0;
 }
 
 bool retro_unserialize(const void *data, size_t size)
 {
-       return false;
+       struct savestate_state state = { 0, };
+       int ret;
+
+       state.load_buf = data;
+       state.size = size;
+       state.pos = 0;
+
+       ret = PicoStateFP(&state, 0, state_read, NULL,
+               state_eof, state_fseek);
+       return ret == 0;
 }
 
 /* cheats - TODO */
@@ -581,15 +709,12 @@ void retro_init(void)
        PicoAutoRgnOrder = 0x184; // US, EU, JP
        PicoCDBuffers = 0;
 
-       p32x_msh2_multiplier = MSH2_MULTI_DEFAULT;
-       p32x_ssh2_multiplier = SSH2_MULTI_DEFAULT;
-
        vout_width = 320;
        vout_height = 240;
        vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
 
        PicoInit();
-       PicoDrawSetOutFormat(PDF_RGB555, 1);
+       PicoDrawSetOutFormat(PDF_RGB555, 0);
        PicoDrawSetOutBuf(vout_buf, vout_width * 2);
 
        //PicoMessage = plat_status_msg_busy_next;