implify multitap core options
[pcsx_rearmed.git] / frontend / libretro.c
index 47b08c7..07601cb 100644 (file)
@@ -39,6 +39,7 @@
 #include "revision.h"
 
 #include <libretro.h>
+#include <compat/fopen_utf8.h>
 #include "libretro_core_options.h"
 
 #ifdef _3DS
 
 #define INTERNAL_FPS_SAMPLE_PERIOD 64
 
-#ifdef DRC_DISABLE
-int stop;
-u32 next_interupt;
-u32 event_cycles[PSXINT_COUNT];
-int cycle_multiplier;
-int new_dynarec_hacks;
-
-void new_dyna_before_save(void) {}
-void new_dyna_after_save(void) {}
-void new_dyna_freeze(void *f, int i) {}
-#endif
-
 //hack to prevent retroarch freezing when reseting in the menu but not while running with the hot key
 static int rebootemu = 0;
 
@@ -83,6 +72,8 @@ static retro_set_rumble_state_t rumble_cb;
 static struct retro_log_callback logging;
 static retro_log_printf_t log_cb;
 
+static unsigned msg_interface_version = 0;
+
 static void *vout_buf;
 static void *vout_buf_ptr;
 static int vout_width, vout_height;
@@ -100,6 +91,7 @@ static int show_advanced_gpu_peops_settings = -1;
 static int show_advanced_gpu_unai_settings = -1;
 #endif
 static int show_other_input_settings = -1;
+static float mouse_sensitivity = 1.0f;
 
 static unsigned previous_width = 0;
 static unsigned previous_height = 0;
@@ -122,9 +114,11 @@ int in_type[8] = {
 int in_analog_left[8][2] = { { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 } };
 int in_analog_right[8][2] = { { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 } };
 unsigned short in_keystate[PORTS_NUMBER];
+int in_mouse[8][2];
 int multitap1 = 0;
 int multitap2 = 0;
 int in_enable_vibration = 1;
+static int input_changed = 0;
 
 // NegCon adjustment parameters
 // > The NegCon 'twist' action is somewhat awkward when mapped
@@ -524,6 +518,41 @@ void out_register_libretro(struct out_driver *drv)
    drv->feed   = snd_feed;
 }
 
+#define RETRO_DEVICE_PSE_STANDARD   RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD,   0)
+#define RETRO_DEVICE_PSE_ANALOG     RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_ANALOG,   0)
+#define RETRO_DEVICE_PSE_DUALSHOCK  RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_ANALOG,   1)
+#define RETRO_DEVICE_PSE_NEGCON     RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_ANALOG,   2)
+#define RETRO_DEVICE_PSE_GUNCON     RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_LIGHTGUN, 0)
+#define RETRO_DEVICE_PSE_MOUSE      RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_MOUSE,    0)
+
+static char *get_pse_pad_label[] = {
+   "none", "mouse", "negcon", "konami gun", "standard", "analog", "guncon", "dualshock"
+};
+
+static const struct retro_controller_description pads[7] =
+{
+   { "standard",  RETRO_DEVICE_JOYPAD },
+   { "analog",    RETRO_DEVICE_PSE_ANALOG },
+   { "dualshock", RETRO_DEVICE_PSE_DUALSHOCK },
+   { "negcon",    RETRO_DEVICE_PSE_NEGCON },
+   { "guncon",    RETRO_DEVICE_PSE_GUNCON },
+   { "mouse",     RETRO_DEVICE_PSE_MOUSE },
+   { NULL, 0 },
+};
+
+static const struct retro_controller_info ports[9] =
+{
+   { pads, 7 },
+   { pads, 7 },
+   { pads, 7 },
+   { pads, 7 },
+   { pads, 7 },
+   { pads, 7 },
+   { pads, 7 },
+   { pads, 7 },
+   { NULL, 0 },
+};
+
 /* libretro */
 void retro_set_environment(retro_environment_t cb)
 {
@@ -532,6 +561,7 @@ void retro_set_environment(retro_environment_t cb)
    if (cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging))
       log_cb = logging.log;
 
+   environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
    libretro_set_core_options(environ_cb);
 }
 
@@ -546,165 +576,76 @@ unsigned retro_api_version(void)
    return RETRO_API_VERSION;
 }
 
-static int controller_port_variable(unsigned port, struct retro_variable *var)
+static void update_multitap(void)
 {
-   if (port >= PORTS_NUMBER)
-      return 0;
-
-   if (!environ_cb)
-      return 0;
+   struct retro_variable var = {};
 
-   var->value = NULL;
-   switch (port)
+   var.value = NULL;
+   var.key = "pcsx_rearmed_multitap";
+   if (environ_cb && (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value))
    {
-   case 0:
-      var->key = "pcsx_rearmed_pad1type";
-      break;
-   case 1:
-      var->key = "pcsx_rearmed_pad2type";
-      break;
-   case 2:
-      var->key = "pcsx_rearmed_pad3type";
-      break;
-   case 3:
-      var->key = "pcsx_rearmed_pad4type";
-      break;
-   case 4:
-      var->key = "pcsx_rearmed_pad5type";
-      break;
-   case 5:
-      var->key = "pcsx_rearmed_pad6type";
-      break;
-   case 6:
-      var->key = "pcsx_rearmed_pad7type";
-      break;
-   case 7:
-      var->key = "pcsx_rearmed_pad8type";
-      break;
+      if (strcmp(var.value, "port 1 only") == 0)
+      {
+         multitap1 = 1;
+         multitap2 = 0;
+      }
+      else if (strcmp(var.value, "port 2 only") == 0)
+      {
+         multitap1 = 0;
+         multitap2 = 1;
+      }
+      else if (strcmp(var.value, "both") == 0)
+      {
+         multitap1 = 1;
+         multitap2 = 1;
+      }
+      else
+      {
+         multitap1 = 0;
+         multitap2 = 0;
+      }
    }
-
-   return environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, var) && var->value;
-}
-
-static void update_controller_port_variable(unsigned port)
-{
-   if (port >= PORTS_NUMBER)
-      return;
-
-   struct retro_variable var;
-
-   if (controller_port_variable(port, &var))
+   else
    {
-      if (strcmp(var.value, "standard") == 0)
-         in_type[port] = PSE_PAD_TYPE_STANDARD;
-      else if (strcmp(var.value, "analog") == 0)
-         in_type[port] = PSE_PAD_TYPE_ANALOGJOY;
-      else if (strcmp(var.value, "dualshock") == 0)
-         in_type[port] = PSE_PAD_TYPE_ANALOGPAD;
-      else if (strcmp(var.value, "negcon") == 0)
-         in_type[port] = PSE_PAD_TYPE_NEGCON;
-      else if (strcmp(var.value, "guncon") == 0)
-         in_type[port] = PSE_PAD_TYPE_GUNCON;
-      else if (strcmp(var.value, "none") == 0)
-         in_type[port] = PSE_PAD_TYPE_NONE;
-      // else 'default' case, do nothing
+      multitap1 = 0;
+      multitap2 = 0;
    }
 }
 
-static void update_controller_port_device(unsigned port, unsigned device)
+void retro_set_controller_port_device(unsigned port, unsigned device)
 {
    if (port >= PORTS_NUMBER)
       return;
 
-   struct retro_variable var;
-
-   if (!controller_port_variable(port, &var))
-      return;
-
-   if (strcmp(var.value, "default") != 0)
-      return;
-
    switch (device)
    {
    case RETRO_DEVICE_JOYPAD:
+   case RETRO_DEVICE_PSE_STANDARD:
       in_type[port] = PSE_PAD_TYPE_STANDARD;
       break;
-   case RETRO_DEVICE_ANALOG:
+   case RETRO_DEVICE_PSE_ANALOG:
+      in_type[port] = PSE_PAD_TYPE_ANALOGJOY;
+      break;
+   case RETRO_DEVICE_PSE_DUALSHOCK:
       in_type[port] = PSE_PAD_TYPE_ANALOGPAD;
       break;
-   case RETRO_DEVICE_MOUSE:
+   case RETRO_DEVICE_PSE_MOUSE:
       in_type[port] = PSE_PAD_TYPE_MOUSE;
       break;
-   case RETRO_DEVICE_LIGHTGUN:
-      in_type[port] = PSE_PAD_TYPE_GUN;
+   case RETRO_DEVICE_PSE_NEGCON:
+      in_type[port] = PSE_PAD_TYPE_NEGCON;
+      break;
+   case RETRO_DEVICE_PSE_GUNCON:
+      in_type[port] = PSE_PAD_TYPE_GUNCON;
       break;
    case RETRO_DEVICE_NONE:
    default:
       in_type[port] = PSE_PAD_TYPE_NONE;
+      break;
    }
-}
-
-static void update_multitap()
-{
-   struct retro_variable var;
-   int auto_case, port;
-
-   var.value = NULL;
-   var.key = "pcsx_rearmed_multitap1";
-   auto_case = 0;
-   if (environ_cb && (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value))
-   {
-      if (strcmp(var.value, "enabled") == 0)
-         multitap1 = 1;
-      else if (strcmp(var.value, "disabled") == 0)
-         multitap1 = 0;
-      else // 'auto' case
-         auto_case = 1;
-   }
-   else
-      auto_case = 1;
-
-   if (auto_case)
-   {
-      // If a gamepad is plugged after port 2, we need a first multitap.
-      multitap1 = 0;
-      for (port = 2; port < PORTS_NUMBER; port++)
-         multitap1 |= in_type[port] != PSE_PAD_TYPE_NONE;
-   }
-
-   var.value = NULL;
-   var.key = "pcsx_rearmed_multitap2";
-   auto_case = 0;
-   if (environ_cb && (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value))
-   {
-      if (strcmp(var.value, "enabled") == 0)
-         multitap2 = 1;
-      else if (strcmp(var.value, "disabled") == 0)
-         multitap2 = 0;
-      else // 'auto' case
-         auto_case = 1;
-   }
-   else
-      auto_case = 1;
 
-   if (auto_case)
-   {
-      // If a gamepad is plugged after port 4, we need a second multitap.
-      multitap2 = 0;
-      for (port = 4; port < PORTS_NUMBER; port++)
-         multitap2 |= in_type[port] != PSE_PAD_TYPE_NONE;
-   }
-}
-
-void retro_set_controller_port_device(unsigned port, unsigned device)
-{
-   SysPrintf("port %u  device %u", port, device);
-
-   if (port >= PORTS_NUMBER)
-      return;
-
-   update_controller_port_device(port, device);
-   update_multitap();
+   SysPrintf("port: %u  device: %s\n", port + 1, get_pse_pad_label[in_type[port]]);
+   input_changed = 1;
 }
 
 void retro_get_system_info(struct retro_system_info *info)
@@ -1157,7 +1098,7 @@ static bool read_m3u(const char *file)
 {
    char line[1024];
    char name[PATH_MAX];
-   FILE *f = fopen(file, "r");
+   FILE *f = fopen_utf8(file, "r");
    if (!f)
       return false;
 
@@ -1242,6 +1183,7 @@ strcasestr(const char *s, const char *find)
 
 static void set_retro_memmap(void)
 {
+#ifndef NDEBUG
    struct retro_memory_map retromap = { 0 };
    struct retro_memory_descriptor mmap = {
       0, psxM, 0, 0, 0, 0, 0x200000
@@ -1251,6 +1193,7 @@ static void set_retro_memmap(void)
    retromap.num_descriptors = 1;
 
    environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &retromap);
+#endif
 }
 
 static void update_variables(bool in_flight);
@@ -1444,7 +1387,7 @@ bool retro_load_game(const struct retro_game_info *info)
    }
 
    plugin_call_rearmed_cbs();
-   dfinput_activate();
+   /* dfinput_activate(); */
 
    if (CheckCdrom() == -1)
    {
@@ -1463,6 +1406,8 @@ bool retro_load_game(const struct retro_game_info *info)
 
    set_retro_memmap();
 
+   input_changed = 1;
+
    return true;
 }
 
@@ -1529,7 +1474,6 @@ static float GunconAdjustRatioY = 1;
 static void update_variables(bool in_flight)
 {
    struct retro_variable var;
-   int i;
 #ifdef GPU_PEOPS
    int gpu_peops_fix = 0;
 #endif
@@ -1552,8 +1496,8 @@ static void update_variables(bool in_flight)
          Config.PsxType = 1;
    }
 
-   for (i = 0; i < PORTS_NUMBER; i++)
-      update_controller_port_variable(i);
+   /*for (i = 0; i < PORTS_NUMBER; i++)
+      update_controller_port_variable(i);*/
 
    update_multitap();
 
@@ -1786,9 +1730,20 @@ static void update_variables(bool in_flight)
    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
    {
       if (strcmp(var.value, "async") == 0)
+      {
          Config.AsyncCD = 1;
-      else
+         Config.CHD_Precache = 0;
+      }
+      else if (strcmp(var.value, "sync") == 0)
+      {
          Config.AsyncCD = 0;
+         Config.CHD_Precache = 0;
+      }
+      else if (strcmp(var.value, "precache") == 0)
+      {
+         Config.AsyncCD = 0;
+         Config.CHD_Precache = 1;
+      }
    }
 #endif
 
@@ -1822,6 +1777,21 @@ static void update_variables(bool in_flight)
          Config.SpuIrq = 1;
    }
 
+#ifdef THREAD_RENDERING
+   var.key = "pcsx_rearmed_gpu_thread_rendering";
+   var.value = NULL;
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "disabled") == 0)
+         pl_rearmed_cbs.thread_rendering = THREAD_RENDERING_OFF;
+      else if (strcmp(var.value, "sync") == 0)
+         pl_rearmed_cbs.thread_rendering = THREAD_RENDERING_SYNC;
+      else if (strcmp(var.value, "async") == 0)
+         pl_rearmed_cbs.thread_rendering = THREAD_RENDERING_ASYNC;
+   }
+#endif
+
 #ifdef GPU_PEOPS
    var.value = NULL;
    var.key = "pcsx_rearmed_gpu_peops_odd_even_bit";
@@ -2003,6 +1973,17 @@ static void update_variables(bool in_flight)
          pl_rearmed_cbs.gpu_unai.blending = 1;
    }
 
+   var.key = "pcsx_rearmed_gpu_unai_scale_hires";
+   var.value = NULL;
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "disabled") == 0)
+         pl_rearmed_cbs.gpu_unai.scale_hires = 0;
+      else if (strcmp(var.value, "enabled") == 0)
+         pl_rearmed_cbs.gpu_unai.scale_hires = 1;
+   }
+
    var.key = "pcsx_rearmed_show_gpu_unai_settings";
    var.value = NULL;
 
@@ -2018,17 +1999,18 @@ static void update_variables(bool in_flight)
       {
          unsigned i;
          struct retro_core_option_display option_display;
-         char gpu_unai_option[5][40] = {
+         char gpu_unai_option[6][40] = {
             "pcsx_rearmed_gpu_unai_blending",
             "pcsx_rearmed_gpu_unai_lighting",
             "pcsx_rearmed_gpu_unai_fast_lighting",
             "pcsx_rearmed_gpu_unai_ilace_force",
-            "pcsx_rearmed_gpu_unai_pixel_skip"
+            "pcsx_rearmed_gpu_unai_pixel_skip",
+            "pcsx_rearmed_gpu_unai_scale_hires",
          };
 
          option_display.visible = show_advanced_gpu_unai_settings;
 
-         for (i = 0; i < 5; i++)
+         for (i = 0; i < 6; i++)
          {
             option_display.key = gpu_unai_option[i];
             environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
@@ -2113,6 +2095,13 @@ static void update_variables(bool in_flight)
    }
 #endif /* NEW_DYNAREC */
 
+   var.value = NULL;
+   var.key = "pcsx_rearmed_input_sensitivity";
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      mouse_sensitivity = atof(var.value);
+   }
+
    var.key = "pcsx_rearmed_show_other_input_settings";
    var.value = NULL;
 
@@ -2129,14 +2118,6 @@ static void update_variables(bool in_flight)
          unsigned i;
          struct retro_core_option_display option_display;
          char gpu_peops_option[][50] = {
-            "pcsx_rearmed_multitap1",
-            "pcsx_rearmed_multitap2",
-            "pcsx_rearmed_pad3type",
-            "pcsx_rearmed_pad4type",
-            "pcsx_rearmed_pad5type",
-            "pcsx_rearmed_pad6type",
-            "pcsx_rearmed_pad7type",
-            "pcsx_rearmed_pad8type",
             "pcsx_rearmed_negcon_deadzone",
             "pcsx_rearmed_negcon_response",
             "pcsx_rearmed_analog_axis_modifier",
@@ -2168,7 +2149,7 @@ static void update_variables(bool in_flight)
          GPU_open(&gpuDisp, "PCSX", NULL);
       }
 
-      dfinput_activate();
+      /* dfinput_activate(); */
    }
    else
    {
@@ -2244,74 +2225,204 @@ unsigned char axis_range_modifier(int16_t axis_value, bool is_square)
    return modifier_axis_range;
 }
 
-void retro_run(void)
+static void update_input_guncon(int port, int ret)
 {
-   int i;
-   //SysReset must be run while core is running,Not in menu (Locks up Retroarch)
-   if (rebootemu != 0)
+   //ToDo move across to:
+   //RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X
+   //RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y
+   //RETRO_DEVICE_ID_LIGHTGUN_TRIGGER
+   //RETRO_DEVICE_ID_LIGHTGUN_RELOAD
+   //RETRO_DEVICE_ID_LIGHTGUN_AUX_A
+   //RETRO_DEVICE_ID_LIGHTGUN_AUX_B
+   //Though not sure these are hooked up properly on the Pi
+
+   //GUNCON has 3 controls, Trigger,A,B which equal Circle,Start,Cross
+
+   // Trigger
+   //The 1 is hardcoded instead of port to prevent the overlay mouse button libretro crash bug
+   if (input_state_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT))
    {
-      rebootemu = 0;
-      SysReset();
-      if (!Config.HLE && !Config.SlowBoot)
-      {
-         // skip BIOS logos
-         psxRegs.pc = psxRegs.GPR.n.ra;
-      }
+      in_keystate[port] |= (1 << DKEY_CIRCLE);
    }
 
-   if (display_internal_fps)
+   // A
+   if (input_state_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT))
    {
-      frame_count++;
+      in_keystate[port] |= (1 << DKEY_START);
+   }
 
-      if (frame_count % INTERNAL_FPS_SAMPLE_PERIOD == 0)
-      {
-         unsigned internal_fps = pl_rearmed_cbs.flip_cnt * (is_pal_mode ? 50 : 60) / INTERNAL_FPS_SAMPLE_PERIOD;
-         char str[64];
-         const char *strc = (const char *)str;
-         struct retro_message msg = {
-            strc,
-            180
-         };
+   // B
+   if (input_state_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE))
+   {
+      in_keystate[port] |= (1 << DKEY_CROSS);
+   }
 
-         str[0] = '\0';
+   int gunx = input_state_cb(port, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X);
+   int guny = input_state_cb(port, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y);
 
-         snprintf(str, sizeof(str), "Internal FPS: %2d", internal_fps);
+   //Mouse range is -32767 -> 32767
+   //1% is about 655
+   //Use the left analog stick field to store the absolute coordinates
+   in_analog_left[port][0] = (gunx * GunconAdjustRatioX) + (GunconAdjustX * 655);
+   in_analog_left[port][1] = (guny * GunconAdjustRatioY) + (GunconAdjustY * 655);
+}
 
-         pl_rearmed_cbs.flip_cnt = 0;
+static void update_input_negcon(int port, int ret)
+{
+   int lsx;
+   int rsy;
+   int negcon_i_rs;
+   int negcon_ii_rs;
+   float negcon_twist_amplitude;
 
-         environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
-      }
+   // Query digital inputs
+   //
+   // > Pad-Up
+   if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_UP))
+      in_keystate[port] |= (1 << DKEY_UP);
+   // > Pad-Right
+   if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT))
+      in_keystate[port] |= (1 << DKEY_RIGHT);
+   // > Pad-Down
+   if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN))
+      in_keystate[port] |= (1 << DKEY_DOWN);
+   // > Pad-Left
+   if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT))
+      in_keystate[port] |= (1 << DKEY_LEFT);
+   // > Start
+   if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START))
+      in_keystate[port] |= (1 << DKEY_START);
+   // > neGcon A
+   if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_A))
+      in_keystate[port] |= (1 << DKEY_CIRCLE);
+   // > neGcon B
+   if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_X))
+      in_keystate[port] |= (1 << DKEY_TRIANGLE);
+   // > neGcon R shoulder (digital)
+   if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_R))
+      in_keystate[port] |= (1 << DKEY_R1);
+   // Query analog inputs
+   //
+   // From studying 'libpcsxcore/plugins.c' and 'frontend/plugin.c':
+   // >> pad->leftJoyX  == in_analog_left[port][0]  == NeGcon II
+   // >> pad->leftJoyY  == in_analog_left[port][1]  == NeGcon L
+   // >> pad->rightJoyX == in_analog_right[port][0] == NeGcon twist
+   // >> pad->rightJoyY == in_analog_right[port][1] == NeGcon I
+   // So we just have to map in_analog_left/right to more
+   // appropriate inputs...
+   //
+   // > NeGcon twist
+   // >> Get raw analog stick value and account for deadzone
+   lsx = input_state_cb(port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
+   if (lsx > negcon_deadzone)
+      lsx = lsx - negcon_deadzone;
+   else if (lsx < -negcon_deadzone)
+      lsx = lsx + negcon_deadzone;
+   else
+      lsx = 0;
+   // >> Convert to an 'amplitude' [-1.0,1.0] and adjust response
+   negcon_twist_amplitude = (float)lsx / (float)(NEGCON_RANGE - negcon_deadzone);
+   if (negcon_linearity == 2)
+   {
+      if (negcon_twist_amplitude < 0.0)
+         negcon_twist_amplitude = -(negcon_twist_amplitude * negcon_twist_amplitude);
+      else
+         negcon_twist_amplitude = negcon_twist_amplitude * negcon_twist_amplitude;
+   }
+   else if (negcon_linearity == 3)
+      negcon_twist_amplitude = negcon_twist_amplitude * negcon_twist_amplitude * negcon_twist_amplitude;
+   // >> Convert to final 'in_analog' integer value [0,255]
+   in_analog_right[port][0] = MAX(MIN((int)(negcon_twist_amplitude * 128.0f) + 128, 255), 0);
+   // > NeGcon I + II
+   // >> Handle right analog stick vertical axis mapping...
+   //    - Up (-Y) == accelerate == neGcon I
+   //    - Down (+Y) == brake == neGcon II
+   negcon_i_rs = 0;
+   negcon_ii_rs = 0;
+   rsy = input_state_cb(port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y);
+   if (rsy >= 0)
+   {
+      // Account for deadzone
+      // (Note: have never encountered a gamepad with significant differences
+      // in deadzone between left/right analog sticks, so use the regular 'twist'
+      // deadzone here)
+      if (rsy > negcon_deadzone)
+         rsy = rsy - negcon_deadzone;
+      else
+         rsy = 0;
+      // Convert to 'in_analog' integer value [0,255]
+      negcon_ii_rs = MIN((int)(((float)rsy / (float)(NEGCON_RANGE - negcon_deadzone)) * 255.0f), 255);
    }
    else
-      frame_count = 0;
+   {
+      if (rsy < -negcon_deadzone)
+         rsy = -1 * (rsy + negcon_deadzone);
+      else
+         rsy = 0;
+      negcon_i_rs = MIN((int)(((float)rsy / (float)(NEGCON_RANGE - negcon_deadzone)) * 255.0f), 255);
+   }
+   // >> NeGcon I
+   in_analog_right[port][1] = MAX(
+       MAX(
+           get_analog_button(ret, input_state_cb, port, RETRO_DEVICE_ID_JOYPAD_R2),
+           get_analog_button(ret, input_state_cb, port, RETRO_DEVICE_ID_JOYPAD_B)),
+       negcon_i_rs);
+   // >> NeGcon II
+   in_analog_left[port][0] = MAX(
+       MAX(
+           get_analog_button(ret, input_state_cb, port, RETRO_DEVICE_ID_JOYPAD_L2),
+           get_analog_button(ret, input_state_cb, port, RETRO_DEVICE_ID_JOYPAD_Y)),
+       negcon_ii_rs);
+   // > NeGcon L
+   in_analog_left[port][1] = get_analog_button(ret, input_state_cb, port, RETRO_DEVICE_ID_JOYPAD_L);
+}
 
-   input_poll_cb();
+static void update_input_mouse(int port, int ret)
+{
+   float raw_x = input_state_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X);
+   float raw_y = input_state_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y);
 
-   bool updated = false;
-   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
-      update_variables(true);
+   int x = (int)roundf(raw_x * mouse_sensitivity);
+   int y = (int)roundf(raw_y * mouse_sensitivity);
+
+   if (x > 127) x = 127;
+   else if (x < -128) x = -128;
+
+   if (y > 127) y = 127;
+   else if (y < -128) y = -128;
+
+   in_mouse[port][0] = x; /* -128..+128 left/right movement, 0 = no movement */
+   in_mouse[port][1] = y; /* -128..+128 down/up movement, 0 = no movement    */
+
+   /* left mouse button state */
+   if (input_state_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT))
+      in_keystate[port] |= 1 << 11;
+
+   /* right mouse button state */
+   if (input_state_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT))
+      in_keystate[port] |= 1 << 10;
+}
 
+static void update_input(void)
+{
    // reset all keystate, query libretro for keystate
+   int i;
    int j;
-   int lsx;
-   int rsy;
-   float negcon_twist_amplitude;
-   int negcon_i_rs;
-   int negcon_ii_rs;
 
    for (i = 0; i < PORTS_NUMBER; i++)
    {
       int16_t ret = 0;
+      int type = in_type[i];
+
       in_keystate[i] = 0;
 
-      if (in_type[i] == PSE_PAD_TYPE_NONE)
+      if (type == PSE_PAD_TYPE_NONE)
          continue;
 
       if (libretro_supports_bitmasks)
          ret = input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
       else
       {
-         unsigned j;
          for (j = 0; j < (RETRO_DEVICE_ID_JOYPAD_R3 + 1); j++)
          {
             if (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, j))
@@ -2319,173 +2430,118 @@ void retro_run(void)
          }
       }
 
-      if (in_type[i] == PSE_PAD_TYPE_GUNCON)
+      switch (type)
       {
-         //ToDo move across to:
-         //RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X
-         //RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y
-         //RETRO_DEVICE_ID_LIGHTGUN_TRIGGER
-         //RETRO_DEVICE_ID_LIGHTGUN_RELOAD
-         //RETRO_DEVICE_ID_LIGHTGUN_AUX_A
-         //RETRO_DEVICE_ID_LIGHTGUN_AUX_B
-         //Though not sure these are hooked up properly on the Pi
-
-         //ToDo
-         //Put the controller index back to i instead of hardcoding to 1 when the libretro overlay crash bug is fixed
-         //This is required for 2 player
-
-         //GUNCON has 3 controls, Trigger,A,B which equal Circle,Start,Cross
-
-         // Trigger
-         //The 1 is hardcoded instead of i to prevent the overlay mouse button libretro crash bug
-         if (input_state_cb(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT))
-         {
-            in_keystate[i] |= (1 << DKEY_CIRCLE);
-         }
-
-         // A
-         if (input_state_cb(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT))
-         {
-            in_keystate[i] |= (1 << DKEY_START);
-         }
+      case PSE_PAD_TYPE_GUNCON:
+         update_input_guncon(i, ret);
+         break;
+      case PSE_PAD_TYPE_NEGCON:
+         update_input_negcon(i, ret);
+         break;
+      case PSE_PAD_TYPE_MOUSE:
+         update_input_mouse(i, ret);
+         break;      
+      default:
+         // Query digital inputs
+         for (j = 0; j < RETRO_PSX_MAP_LEN; j++)
+            if (ret & (1 << j))
+               in_keystate[i] |= retro_psx_map[j];
 
-         // B
-         if (input_state_cb(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE))
+         // Query analog inputs
+         if (type == PSE_PAD_TYPE_ANALOGJOY || type == PSE_PAD_TYPE_ANALOGPAD)
          {
-            in_keystate[i] |= (1 << DKEY_CROSS);
+            in_analog_left[i][0]  = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X), axis_bounds_modifier);
+            in_analog_left[i][1]  = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y), axis_bounds_modifier);
+            in_analog_right[i][0] = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X), axis_bounds_modifier);
+            in_analog_right[i][1] = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y), axis_bounds_modifier);
          }
+      }
+   }
+}
 
-         //The 1 is hardcoded instead of i to prevent the overlay mouse button libretro crash bug
-         int gunx = input_state_cb(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X);
-         int guny = input_state_cb(1, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y);
+static void print_internal_fps(void)
+{
+   if (display_internal_fps)
+   {
+      frame_count++;
 
-         //Mouse range is -32767 -> 32767
-         //1% is about 655
-         //Use the left analog stick field to store the absolute coordinates
-         in_analog_left[0][0] = (gunx * GunconAdjustRatioX) + (GunconAdjustX * 655);
-         in_analog_left[0][1] = (guny * GunconAdjustRatioY) + (GunconAdjustY * 655);
-      }
-      if (in_type[i] == PSE_PAD_TYPE_NEGCON)
+      if (frame_count % INTERNAL_FPS_SAMPLE_PERIOD == 0)
       {
-         // Query digital inputs
-         //
-         // > Pad-Up
-         if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_UP))
-            in_keystate[i] |= (1 << DKEY_UP);
-         // > Pad-Right
-         if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT))
-            in_keystate[i] |= (1 << DKEY_RIGHT);
-         // > Pad-Down
-         if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN))
-            in_keystate[i] |= (1 << DKEY_DOWN);
-         // > Pad-Left
-         if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT))
-            in_keystate[i] |= (1 << DKEY_LEFT);
-         // > Start
-         if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START))
-            in_keystate[i] |= (1 << DKEY_START);
-         // > neGcon A
-         if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_A))
-            in_keystate[i] |= (1 << DKEY_CIRCLE);
-         // > neGcon B
-         if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_X))
-            in_keystate[i] |= (1 << DKEY_TRIANGLE);
-         // > neGcon R shoulder (digital)
-         if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_R))
-            in_keystate[i] |= (1 << DKEY_R1);
-         // Query analog inputs
-         //
-         // From studying 'libpcsxcore/plugins.c' and 'frontend/plugin.c':
-         // >> pad->leftJoyX  == in_analog_left[i][0]  == NeGcon II
-         // >> pad->leftJoyY  == in_analog_left[i][1]  == NeGcon L
-         // >> pad->rightJoyX == in_analog_right[i][0] == NeGcon twist
-         // >> pad->rightJoyY == in_analog_right[i][1] == NeGcon I
-         // So we just have to map in_analog_left/right to more
-         // appropriate inputs...
-         //
-         // > NeGcon twist
-         // >> Get raw analog stick value and account for deadzone
-         lsx = input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
-         if (lsx > negcon_deadzone)
-            lsx = lsx - negcon_deadzone;
-         else if (lsx < -negcon_deadzone)
-            lsx = lsx + negcon_deadzone;
-         else
-            lsx = 0;
-         // >> Convert to an 'amplitude' [-1.0,1.0] and adjust response
-         negcon_twist_amplitude = (float)lsx / (float)(NEGCON_RANGE - negcon_deadzone);
-         if (negcon_linearity == 2)
-         {
-            if (negcon_twist_amplitude < 0.0)
-               negcon_twist_amplitude = -(negcon_twist_amplitude * negcon_twist_amplitude);
-            else
-               negcon_twist_amplitude = negcon_twist_amplitude * negcon_twist_amplitude;
-         }
-         else if (negcon_linearity == 3)
-            negcon_twist_amplitude = negcon_twist_amplitude * negcon_twist_amplitude * negcon_twist_amplitude;
-         // >> Convert to final 'in_analog' integer value [0,255]
-         in_analog_right[i][0] = MAX(MIN((int)(negcon_twist_amplitude * 128.0f) + 128, 255), 0);
-         // > NeGcon I + II
-         // >> Handle right analog stick vertical axis mapping...
-         //    - Up (-Y) == accelerate == neGcon I
-         //    - Down (+Y) == brake == neGcon II
-         negcon_i_rs = 0;
-         negcon_ii_rs = 0;
-         rsy = input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y);
-         if (rsy >= 0)
+         unsigned internal_fps = pl_rearmed_cbs.flip_cnt * (is_pal_mode ? 50 : 60) / INTERNAL_FPS_SAMPLE_PERIOD;
+         char str[64];
+         const char *strc = (const char *)str;
+
+         str[0] = '\0';
+
+         snprintf(str, sizeof(str), "Internal FPS: %2d", internal_fps);
+
+         pl_rearmed_cbs.flip_cnt = 0;
+
+         if (msg_interface_version >= 1)
          {
-            // Account for deadzone
-            // (Note: have never encountered a gamepad with significant differences
-            // in deadzone between left/right analog sticks, so use the regular 'twist'
-            // deadzone here)
-            if (rsy > negcon_deadzone)
-               rsy = rsy - negcon_deadzone;
-            else
-               rsy = 0;
-            // Convert to 'in_analog' integer value [0,255]
-            negcon_ii_rs = MIN((int)(((float)rsy / (float)(NEGCON_RANGE - negcon_deadzone)) * 255.0f), 255);
+            struct retro_message_ext msg = {
+               strc,
+               3000,
+               1,
+               RETRO_LOG_INFO,
+               RETRO_MESSAGE_TARGET_OSD,
+               RETRO_MESSAGE_TYPE_STATUS,
+               -1
+            };
+            environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE_EXT, &msg);
          }
          else
          {
-            if (rsy < -negcon_deadzone)
-               rsy = -1 * (rsy + negcon_deadzone);
-            else
-               rsy = 0;
-            negcon_i_rs = MIN((int)(((float)rsy / (float)(NEGCON_RANGE - negcon_deadzone)) * 255.0f), 255);
+            struct retro_message msg = {
+               strc,
+               180
+            };
+            environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
          }
-         // >> NeGcon I
-         in_analog_right[i][1] = MAX(
-             MAX(
-                 get_analog_button(ret, input_state_cb, i, RETRO_DEVICE_ID_JOYPAD_R2),
-                 get_analog_button(ret, input_state_cb, i, RETRO_DEVICE_ID_JOYPAD_B)),
-             negcon_i_rs);
-         // >> NeGcon II
-         in_analog_left[i][0] = MAX(
-             MAX(
-                 get_analog_button(ret, input_state_cb, i, RETRO_DEVICE_ID_JOYPAD_L2),
-                 get_analog_button(ret, input_state_cb, i, RETRO_DEVICE_ID_JOYPAD_Y)),
-             negcon_ii_rs);
-         // > NeGcon L
-         in_analog_left[i][1] = get_analog_button(ret, input_state_cb, i, RETRO_DEVICE_ID_JOYPAD_L);
       }
-      if (in_type[i] != PSE_PAD_TYPE_NEGCON && in_type[i] != PSE_PAD_TYPE_GUNCON)
-      {
-         // Query digital inputs
-         for (j = 0; j < RETRO_PSX_MAP_LEN; j++)
-            if (ret & (1 << j))
-               in_keystate[i] |= retro_psx_map[j];
+   }
+   else
+      frame_count = 0;
+}
 
-         // Query analog inputs
-         if (in_type[i] == PSE_PAD_TYPE_ANALOGJOY || in_type[i] == PSE_PAD_TYPE_ANALOGPAD)
-         {
-            in_analog_left[i][0] = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X), axis_bounds_modifier);
-            in_analog_left[i][1] = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y), axis_bounds_modifier);
-            in_analog_right[i][0] = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X), axis_bounds_modifier);
-            in_analog_right[i][1] = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y), axis_bounds_modifier);
-         }
+void retro_run(void)
+{
+   /* update multitap when inputs have changed */
+   /* this is only applied on core restart */
+   if (input_changed)
+   {
+      int i;
+      input_changed = 0;
+      update_multitap();
+      for (i = 0; i < 8; i++)
+         SysDLog("Player %d: %s\n", i + 1, get_pse_pad_label[in_type[i]]);
+      SysDLog("Multiplayer 1: %s\n", multitap1 ? "enabled" : "disabled");
+      SysDLog("Multiplayer 2: %s\n", multitap2 ? "enabled" : "disabled");
+   }
+
+   //SysReset must be run while core is running,Not in menu (Locks up Retroarch)
+   if (rebootemu != 0)
+   {
+      rebootemu = 0;
+      SysReset();
+      if (!Config.HLE && !Config.SlowBoot)
+      {
+         // skip BIOS logos
+         psxRegs.pc = psxRegs.GPR.n.ra;
       }
+      return;
    }
 
+   print_internal_fps();
+
+   input_poll_cb();
+
+   update_input();
+
+   bool updated = false;
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
+      update_variables(true);
+
    stop = 0;
    psxCpu->Execute();
 
@@ -2502,7 +2558,7 @@ static bool try_use_bios(const char *path)
    long size;
    const char *name;
 
-   f = fopen(path, "rb");
+   f = fopen_utf8(path, "rb");
    if (f == NULL)
       return false;
 
@@ -2610,6 +2666,7 @@ static void loadPSXBios(void)
    unsigned useHLE = 0;
 
    const char *bios[] = {
+      "PS1_ROM", "ps1_rom",
       "PSXONPSP660", "psxonpsp660",
       "SCPH101", "scph101",
       "SCPH5501", "scph5501",
@@ -2654,14 +2711,41 @@ static void loadPSXBios(void)
       }
    }
 
-   if (useHLE || !found_bios)
+   if (!found_bios)
    {
-      SysPrintf("no BIOS files found.\n");
-      struct retro_message msg = {
-         "No PlayStation BIOS file found - add for better compatibility",
-         180
-      };
-      environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, (void *)&msg);
+      const char *msg_str;
+      if (useHLE)
+      {
+         msg_str = "BIOS set to \'hle\' in core options - real BIOS will be ignored";
+         SysPrintf("Using HLE BIOS.\n");
+      }
+      else
+      {
+         msg_str = "No PlayStation BIOS file found - add for better compatibility";
+         SysPrintf("No BIOS files found.\n");
+      }
+
+      if (msg_interface_version >= 1)
+      {
+         struct retro_message_ext msg = {
+            msg_str,
+            3000,
+            3,
+            RETRO_LOG_WARN,
+            RETRO_MESSAGE_TARGET_ALL,
+            RETRO_MESSAGE_TYPE_NOTIFICATION,
+            -1
+         };
+         environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE_EXT, &msg);
+      }
+      else
+      {
+         struct retro_message msg = {
+            msg_str,
+            180
+         };
+         environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
+      }
    }
 }
 
@@ -2671,7 +2755,10 @@ void retro_init(void)
    struct retro_rumble_interface rumble;
    int ret;
 
-#ifdef __MACH__
+   msg_interface_version = 0;
+   environ_cb(RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION, &msg_interface_version);
+
+#if defined(__MACH__) && !defined(TVOS)
    // magic sauce to make the dynarec work on iOS
    syscall(SYS_ptrace, 0 /*PTRACE_TRACEME*/, 0, 0, 0);
 #endif
@@ -2730,7 +2817,7 @@ void retro_init(void)
     * we have to do this because cache misses and some IO penalties
     * are not emulated. Warning: changing this may break compatibility. */
    cycle_multiplier = 175;
-#ifdef HAVE_PRE_ARMV7
+#if defined(HAVE_PRE_ARMV7) && !defined(_3DS)
    cycle_multiplier = 200;
 #endif
    pl_rearmed_cbs.gpu_peops.iUseDither = 1;
@@ -2794,3 +2881,17 @@ void SysPrintf(const char *fmt, ...)
    if (log_cb)
       log_cb(RETRO_LOG_INFO, "%s", msg);
 }
+
+/* Prints debug-level logs */
+void SysDLog(const char *fmt, ...)
+{
+   va_list list;
+   char msg[512];
+
+   va_start(list, fmt);
+   vsprintf(msg, fmt, list);
+   va_end(list);
+
+   if (log_cb)
+      log_cb(RETRO_LOG_DEBUG, "%s", msg);
+}