libretro: Update input descriptors
[pcsx_rearmed.git] / frontend / libretro.c
index f916f57..b625c1a 100644 (file)
@@ -5,9 +5,11 @@
  * See the COPYING file in the top-level directory.
  */
 
+#define _GNU_SOURCE 1 // strcasestr
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <strings.h>
 
 #include "../libpcsxcore/misc.h"
 #include "../libpcsxcore/psxcounters.h"
@@ -17,6 +19,7 @@
 #include "../libpcsxcore/cdriso.h"
 #include "../libpcsxcore/cheat.h"
 #include "../plugins/dfsound/out.h"
+#include "../plugins/dfinput/externals.h"
 #include "cspace.h"
 #include "main.h"
 #include "plugin.h"
@@ -34,6 +37,7 @@ static void *vout_buf;
 static int vout_width, vout_height;
 static int vout_doffs_old, vout_fb_dirty;
 static bool vout_can_dupe;
+static bool duping_enable;
 
 static int samples_sent, samples_to_send;
 static int plugins_opened;
@@ -49,6 +53,10 @@ int in_a1[2] = { 127, 127 }, in_a2[2] = { 127, 127 };
 int in_keystate;
 int in_enable_vibration;
 
+/* PSX max resolution is 640x512, but with enhancement it's 1024x512 */
+#define VOUT_MAX_WIDTH 1024
+#define VOUT_MAX_HEIGHT 512
+
 static void init_memcard(char *mcd_data)
 {
        unsigned off = 0;
@@ -230,7 +238,29 @@ void out_register_libretro(struct out_driver *drv)
 }
 
 /* libretro */
-void retro_set_environment(retro_environment_t cb) { environ_cb = cb; }
+void retro_set_environment(retro_environment_t cb)
+{
+   static const struct retro_variable vars[] = {
+      { "frameskip", "Frameskip; 0|1|2|3" },
+      { "region", "Region; Auto|NTSC|PAL" },
+      { "pad1type", "Pad 1 Type; standard|analog" },
+#ifndef DRC_DISABLE
+      { "rearmed_drc", "Dynamic recompiler; enabled|disabled" },
+#endif
+#ifdef __ARM_NEON__
+      { "neon_interlace_enable", "Enable interlacing mode(s); disabled|enabled" },
+      { "neon_enhancement_enable", "Enhanced resolution (slow); disabled|enabled" },
+      { "neon_enhancement_no_main", "Enhanced resolution speed hack; disabled|enabled" },
+#endif
+      { "pcsx_rearmed_duping_enable", "Frame duping; on|off" },
+      { NULL, NULL },
+   };
+
+   environ_cb = cb;
+
+   cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars);
+}
+
 void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
 void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
@@ -251,7 +281,7 @@ void retro_get_system_info(struct retro_system_info *info)
        memset(info, 0, sizeof(*info));
        info->library_name = "PCSX-ReARMed";
        info->library_version = "r19";
-       info->valid_extensions = "bin|cue|img|mdf|pbp|cbn";
+       info->valid_extensions = "bin|cue|img|mdf|pbp|toc|cbn|m3u";
        info->need_fullpath = true;
 }
 
@@ -262,8 +292,8 @@ void retro_get_system_av_info(struct retro_system_av_info *info)
        info->timing.sample_rate    = 44100;
        info->geometry.base_width   = 320;
        info->geometry.base_height  = 240;
-       info->geometry.max_width    = 640;
-       info->geometry.max_height   = 512;
+       info->geometry.max_width    = VOUT_MAX_WIDTH;
+       info->geometry.max_height   = VOUT_MAX_HEIGHT;
        info->geometry.aspect_ratio = 4.0 / 3.0;
 }
 
@@ -394,6 +424,7 @@ void retro_cheat_set(unsigned index, bool enabled, const char *code)
 /* multidisk support */
 static bool disk_ejected;
 static unsigned int disk_current_index;
+static unsigned int disk_count;
 static struct disks_state {
        char *fname;
        int internal_index; // for multidisk eboots
@@ -463,14 +494,7 @@ static bool disk_set_image_index(unsigned int index)
 
 static unsigned int disk_get_num_images(void)
 {
-       unsigned int count = 0;
-       size_t i;
-
-       for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++)
-               if (disks[i].fname != NULL)
-                       count++;
-
-       return count;
+       return disk_count;
 }
 
 static bool disk_replace_image_index(unsigned index,
@@ -500,7 +524,10 @@ static bool disk_replace_image_index(unsigned index,
 
 static bool disk_add_image_index(void)
 {
-       // TODO??
+       if (disk_count >= 8)
+               return false;
+
+       disk_count++;
        return true;
 }
 
@@ -514,9 +541,270 @@ static struct retro_disk_control_callback disk_control = {
        .add_image_index = disk_add_image_index,
 };
 
+// just in case, maybe a win-rt port in the future?
+#ifdef _WIN32
+#define SLASH '\\'
+#else
+#define SLASH '/'
+#endif
+
+static char base_dir[PATH_MAX];
+
+static bool read_m3u(const char *file)
+{
+       char line[PATH_MAX];
+       char name[PATH_MAX];
+       FILE *f = fopen(file, "r");
+       if (!f)
+               return false;
+
+       while (fgets(line, sizeof(line), f) && disk_count < sizeof(disks) / sizeof(disks[0])) {
+               if (line[0] == '#')
+                       continue;
+               char *carrige_return = strchr(line, '\r');
+               if (carrige_return)
+                       *carrige_return = '\0';
+               char *newline = strchr(line, '\n');
+               if (newline)
+                       *newline = '\0';
+
+               if (line[0] != '\0')
+               {
+                       snprintf(name, sizeof(name), "%s%c%s", base_dir, SLASH, line);
+                       disks[disk_count++].fname = strdup(name);
+               }
+       }
+
+       fclose(f);
+       return (disk_count != 0);
+}
+
+static void extract_directory(char *buf, const char *path, size_t size)
+{
+   char *base;
+   strncpy(buf, path, size - 1);
+   buf[size - 1] = '\0';
+
+   base = strrchr(buf, '/');
+   if (!base)
+      base = strrchr(buf, '\\');
+
+   if (base)
+      *base = '\0';
+   else
+   {
+      buf[0] = '.';
+      buf[1] = '\0';
+   }
+}
+
+#ifdef __QNX__
+/* Blackberry QNX doesn't have strcasestr */
+
+/*
+ * Find the first occurrence of find in s, ignore case.
+ */
+char *
+strcasestr(const char *s, const char*find)
+{
+       char c, sc;
+       size_t len;
+
+       if ((c = *find++) != 0) {
+               c = tolower((unsigned char)c);
+               len = strlen(find);
+               do {
+                       do {
+                               if ((sc = *s++) == 0)
+                                       return (NULL);
+                       } while ((char)tolower((unsigned char)sc) != c);
+               } while (strncasecmp(s, find, len) != 0);
+               s--;
+       }
+       return ((char *)s);
+}
+#endif
+
 bool retro_load_game(const struct retro_game_info *info)
 {
        size_t i;
+       bool is_m3u = (strcasestr(info->path, ".m3u") != NULL);
+
+   struct retro_input_descriptor desc[] = {
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
+      { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
+      { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
+      { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
+      { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
+      { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
+
+
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
+      { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
+      { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
+      { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
+      { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
+      { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
+
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
+      { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
+      { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
+      { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
+      { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
+      { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
+
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
+      { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
+      { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
+      { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
+      { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
+      { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
+
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
+      { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
+      { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
+      { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
+      { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
+      { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
+
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
+      { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" }, 
+      { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
+      { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
+      { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
+      { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
+
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
+      { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" }, 
+      { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
+      { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
+      { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
+      { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
+
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
+      { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" }, 
+      { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
+      { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
+      { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
+      { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
+
+      { 0 },
+   };
+
+   environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
+
 #ifdef FRONTEND_SUPPORTS_RGB565
        enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
        if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
@@ -543,7 +831,17 @@ bool retro_load_game(const struct retro_game_info *info)
        }
 
        disk_current_index = 0;
-       disks[0].fname = strdup(info->path);
+       extract_directory(base_dir, info->path, sizeof(base_dir));
+
+       if (is_m3u) {
+               if (!read_m3u(info->path)) {
+                       SysPrintf("failed to read m3u file\n");
+                       return false;
+               }
+       } else {
+               disk_count = 1;
+               disks[0].fname = strdup(info->path);
+       }
 
        set_cd_image(disks[0].fname);
 
@@ -562,6 +860,7 @@ bool retro_load_game(const struct retro_game_info *info)
        }
 
        plugin_call_rearmed_cbs();
+       dfinput_activate();
 
        Config.PsxAuto = 1;
        if (CheckCdrom() == -1) {
@@ -578,9 +877,12 @@ bool retro_load_game(const struct retro_game_info *info)
        emu_on_new_cd(0);
 
        // multidisk images
-       for (i = 1; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++) {
-               disks[i].fname = strdup(info->path);
-               disks[i].internal_index = i;
+       if (!is_m3u) {
+               disk_count = cdrIsoMultidiskCount < 8 ? cdrIsoMultidiskCount : 8;
+               for (i = 1; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++) {
+                       disks[i].fname = strdup(info->path);
+                       disks[i].internal_index = i;
+               }
        }
 
        return true;
@@ -602,12 +904,18 @@ unsigned retro_get_region(void)
 
 void *retro_get_memory_data(unsigned id)
 {
-       return Mcd1Data;
+       if (id == RETRO_MEMORY_SAVE_RAM)
+               return Mcd1Data;
+       else
+               return NULL;
 }
 
 size_t retro_get_memory_size(unsigned id)
 {
-       return MCD_SIZE;
+       if (id == RETRO_MEMORY_SAVE_RAM)
+               return MCD_SIZE;
+       else
+               return 0;
 }
 
 void retro_reset(void)
@@ -635,11 +943,131 @@ static const unsigned short retro_psx_map[] = {
 };
 #define RETRO_PSX_MAP_LEN (sizeof(retro_psx_map) / sizeof(retro_psx_map[0]))
 
+static void update_variables(bool in_flight)
+{
+   struct retro_variable var;
+   
+   var.value = NULL;
+   var.key = "frameskip";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+      pl_rearmed_cbs.frameskip = atoi(var.value);
+
+   var.value = NULL;
+   var.key = "region";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      Config.PsxAuto = 0;
+      if (strcmp(var.value, "Automatic") == 0)
+         Config.PsxAuto = 1;
+      else if (strcmp(var.value, "NTSC") == 0)
+         Config.PsxType = 0;
+      else if (strcmp(var.value, "PAL") == 0)
+         Config.PsxType = 1;
+   }
+
+   var.value = NULL;
+   var.key = "pad1type";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      in_type1 = PSE_PAD_TYPE_STANDARD;
+      if (strcmp(var.value, "analog") == 0)
+         in_type1 = PSE_PAD_TYPE_ANALOGPAD;
+   }
+
+#ifdef __ARM_NEON__
+   var.value = "NULL";
+   var.key = "neon_interlace_enable";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "disabled") == 0)
+         pl_rearmed_cbs.gpu_neon.allow_interlace = 0;
+      else if (strcmp(var.value, "enabled") == 0)
+         pl_rearmed_cbs.gpu_neon.allow_interlace = 1;
+   }
+
+   var.value = NULL;
+   var.key = "neon_enhancement_enable";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "disabled") == 0)
+         pl_rearmed_cbs.gpu_neon.enhancement_enable = 0;
+      else if (strcmp(var.value, "enabled") == 0)
+         pl_rearmed_cbs.gpu_neon.enhancement_enable = 1;
+   }
+
+   var.value = NULL;
+   var.key = "neon_enhancement_no_main";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "disabled") == 0)
+         pl_rearmed_cbs.gpu_neon.enhancement_no_main = 0;
+      else if (strcmp(var.value, "enabled") == 0)
+         pl_rearmed_cbs.gpu_neon.enhancement_no_main = 1;
+   }
+#endif
+
+   var.value = "NULL";
+   var.key = "pcsx_rearmed_duping_enable";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "off") == 0)
+         duping_enable = false;
+      else if (strcmp(var.value, "on") == 0)
+         duping_enable = true;
+   }
+
+#ifndef DRC_DISABLE
+   var.value = NULL;
+   var.key = "rearmed_drc";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      R3000Acpu *prev_cpu = psxCpu;
+
+      if (strcmp(var.value, "disabled") == 0)
+         Config.Cpu = CPU_INTERPRETER;
+      else if (strcmp(var.value, "enabled") == 0)
+         Config.Cpu = CPU_DYNAREC;
+
+      psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
+      if (psxCpu != prev_cpu) {
+         prev_cpu->Shutdown();
+         psxCpu->Init();
+         psxCpu->Reset(); // not really a reset..
+      }
+   }
+#endif
+
+   if (in_flight) {
+      // inform core things about possible config changes
+      plugin_call_rearmed_cbs();
+
+      if (GPU_open != NULL && GPU_close != NULL) {
+         GPU_close();
+         GPU_open(&gpuDisp, "PCSX", NULL);
+      }
+
+      dfinput_activate();
+   }
+}
+
 void retro_run(void) 
 {
        int i;
 
        input_poll_cb();
+
+       bool updated = false;
+       if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
+               update_variables(true);
+
        in_keystate = 0;
        for (i = 0; i < RETRO_PSX_MAP_LEN; i++)
                if (input_state_cb(1, RETRO_DEVICE_JOYPAD, 0, i))
@@ -649,23 +1077,91 @@ void retro_run(void)
                if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i))
                        in_keystate |= retro_psx_map[i];
 
+       if (in_type1 == PSE_PAD_TYPE_ANALOGPAD)
+       {
+               in_a1[0] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) / 256) + 128;
+               in_a1[1] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / 256) + 128;
+               in_a2[0] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X) / 256) + 128;
+               in_a2[1] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y) / 256) + 128;
+       }
+
        stop = 0;
        psxCpu->Execute();
 
        samples_to_send += is_pal_mode ? 44100 / 50 : 44100 / 60;
 
-       video_cb((vout_fb_dirty || !vout_can_dupe) ? vout_buf : NULL,
+       video_cb((vout_fb_dirty || !vout_can_dupe || !duping_enable) ? vout_buf : NULL,
                vout_width, vout_height, vout_width * 2);
        vout_fb_dirty = 0;
 }
 
+static bool try_use_bios(const char *path)
+{
+       FILE *f;
+       long size;
+       const char *name;
+
+       f = fopen(path, "rb");
+       if (f == NULL)
+               return false;
+
+       fseek(f, 0, SEEK_END);
+       size = ftell(f);
+       fclose(f);
+
+       if (size != 512 * 1024)
+               return false;
+
+       name = strrchr(path, SLASH);
+       if (name++ == NULL)
+               name = path;
+       snprintf(Config.Bios, sizeof(Config.Bios), "%s", name);
+       return true;
+}
+
+#if 1
+#include <sys/types.h>
+#include <dirent.h>
+
+static bool find_any_bios(const char *dirpath, char *path, size_t path_size)
+{
+       DIR *dir;
+       struct dirent *ent;
+       bool ret = false;
+
+       dir = opendir(dirpath);
+       if (dir == NULL)
+               return false;
+
+       while ((ent = readdir(dir))) {
+               if (strncasecmp(ent->d_name, "scph", 4) != 0)
+                       continue;
+
+               snprintf(path, path_size, "%s/%s", dirpath, ent->d_name);
+               ret = try_use_bios(path);
+               if (ret)
+                       break;
+       }
+       closedir(dir);
+       return ret;
+}
+#else
+#define find_any_bios(...) false
+#endif
+
+static void check_system_specs(void)
+{
+   unsigned level = 6;
+   environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
+}
+
 void retro_init(void)
 {
        const char *bios[] = { "scph1001", "scph5501", "scph7001" };
        const char *dir;
        char path[256];
-       FILE *f = NULL;
        int i, ret, level;
+       bool found_bios = false;
 
        ret = emu_core_preinit();
        ret |= emu_core_init();
@@ -674,7 +1170,11 @@ void retro_init(void)
                exit(1);
        }
 
-       vout_buf = malloc(640 * 512 * 2);
+#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)
+       posix_memalign(&vout_buf, 16, VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
+#else
+       vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
+#endif
 
        if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir)
        {
@@ -682,19 +1182,27 @@ void retro_init(void)
 
                for (i = 0; i < sizeof(bios) / sizeof(bios[0]); i++) {
                        snprintf(path, sizeof(path), "%s/%s.bin", dir, bios[i]);
-                       f = fopen(path, "r");
-                       if (f != NULL) {
-                               snprintf(Config.Bios, sizeof(Config.Bios), "%s.bin", bios[i]);
+                       found_bios = try_use_bios(path);
+                       if (found_bios)
                                break;
-                       }
                }
+
+               if (!found_bios)
+                       found_bios = find_any_bios(dir, path, sizeof(path));
        }
-       if (f != NULL) {
+       if (found_bios) {
                SysPrintf("found BIOS file: %s\n", Config.Bios);
-               fclose(f);
        }
        else
+       {
                SysPrintf("no BIOS files found.\n");
+               struct retro_message msg = 
+               {
+                       "no BIOS found, expect bugs!",
+                       180
+               };
+               environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, (void*)&msg);
+       }
 
        level = 1;
        environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
@@ -720,6 +1228,9 @@ void retro_init(void)
        SaveFuncs.write = save_write;
        SaveFuncs.seek = save_seek;
        SaveFuncs.close = save_close;
+
+       update_variables(false);
+       check_system_specs();
 }
 
 void retro_deinit(void)