Add async CD access
[pcsx_rearmed.git] / frontend / libretro.c
index 9f7c4fe..bd7dc43 100644 (file)
 #include <sys/syscall.h>
 #endif
 
+#ifdef SWITCH
+#include <switch.h>
+#endif
+
 #include "../libpcsxcore/misc.h"
 #include "../libpcsxcore/psxcounters.h"
 #include "../libpcsxcore/psxmem_map.h"
@@ -33,7 +37,9 @@
 #include "plugin_lib.h"
 #include "arm_features.h"
 #include "revision.h"
-#include "libretro.h"
+
+#include <libretro.h>
+#include "libretro_core_options.h"
 
 #ifdef _3DS
 #include "3ds/3ds_utils.h"
 
 #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;
 
@@ -74,6 +92,16 @@ static bool duping_enable;
 static bool found_bios;
 static bool display_internal_fps = false;
 static unsigned frame_count = 0;
+static bool libretro_supports_bitmasks = false;
+#ifdef GPU_PEOPS
+static int show_advanced_gpu_peops_settings = -1;
+#endif
+#ifdef GPU_UNAI
+static int show_advanced_gpu_unai_settings  = -1;
+#endif
+
+static unsigned previous_width = 0;
+static unsigned previous_height = 0;
 
 static int plugins_opened;
 static int is_pal_mode;
@@ -114,6 +142,8 @@ int in_enable_vibration = 1;
 static int negcon_deadzone = 0;
 static int negcon_linearity = 1;
 
+static bool axis_bounds_modifier;
+
 /* PSX max resolution is 640x512, but with enhancement it's 1024x512 */
 #define VOUT_MAX_WIDTH 1024
 #define VOUT_MAX_HEIGHT 512
@@ -127,6 +157,16 @@ static int snd_init(void){return 0;}
 static void snd_finish(void){}
 static int snd_busy(void){return 0;}
 
+#define GPU_PEOPS_ODD_EVEN_BIT         (1 << 0)
+#define GPU_PEOPS_EXPAND_SCREEN_WIDTH  (1 << 1)
+#define GPU_PEOPS_IGNORE_BRIGHTNESS    (1 << 2)
+#define GPU_PEOPS_DISABLE_COORD_CHECK  (1 << 3)
+#define GPU_PEOPS_LAZY_SCREEN_UPDATE   (1 << 6)
+#define GPU_PEOPS_OLD_FRAME_SKIP       (1 << 7)
+#define GPU_PEOPS_REPEATED_TRIANGLES   (1 << 8)
+#define GPU_PEOPS_QUADS_WITH_TRIANGLES (1 << 9)
+#define GPU_PEOPS_FAKE_BUSY_STATE      (1 << 10)
+
 static void init_memcard(char *mcd_data)
 {
        unsigned off = 0;
@@ -179,6 +219,16 @@ static void vout_set_mode(int w, int h, int raw_w, int raw_h, int bpp)
   vout_width = w;
   vout_height = h;
 
+       if (previous_width != vout_width || previous_height != vout_height)
+       {
+               previous_width = vout_width;
+               previous_height = vout_height;
+
+       struct retro_system_av_info info;
+       retro_get_system_av_info(&info);
+       environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info.geometry);
+       }
+
   set_vout_fb();
 }
 
@@ -469,65 +519,12 @@ void out_register_libretro(struct out_driver *drv)
 /* libretro */
 void retro_set_environment(retro_environment_t cb)
 {
-   static const struct retro_variable vars[] = {
-      { "pcsx_rearmed_frameskip", "Frameskip; 0|1|2|3" },
-      { "pcsx_rearmed_bios", "Use BIOS; auto|HLE" },
-      { "pcsx_rearmed_region", "Region; auto|NTSC|PAL" },
-      { "pcsx_rearmed_memcard2", "Enable second memory card; disabled|enabled" },
-      { "pcsx_rearmed_pad1type", "Pad 1 Type; standard|analog|dualshock|negcon|none" },
-      { "pcsx_rearmed_pad2type", "Pad 2 Type; standard|analog|dualshock|negcon|none" },
-      { "pcsx_rearmed_pad3type", "Pad 3 Type; none|standard|analog|dualshock|negcon" },
-      { "pcsx_rearmed_pad4type", "Pad 4 Type; none|standard|analog|dualshock|negcon" },
-      { "pcsx_rearmed_pad5type", "Pad 5 Type; none|standard|analog|dualshock|negcon" },
-      { "pcsx_rearmed_pad6type", "Pad 6 Type; none|standard|analog|dualshock|negcon" },
-      { "pcsx_rearmed_pad7type", "Pad 7 Type; none|standard|analog|dualshock|negcon" },
-      { "pcsx_rearmed_pad8type", "Pad 8 Type; none|standard|analog|dualshock|negcon" },
-      { "pcsx_rearmed_multitap1", "Multitap 1; auto|disabled|enabled" },
-      { "pcsx_rearmed_multitap2", "Multitap 2; auto|disabled|enabled" },
-      { "pcsx_rearmed_negcon_deadzone", "NegCon Twist Deadzone (percent); 0|5|10|15|20|25|30" },
-      { "pcsx_rearmed_negcon_response", "NegCon Twist Response; linear|quadratic|cubic" },
-      { "pcsx_rearmed_vibration", "Enable Vibration; enabled|disabled" },
-      { "pcsx_rearmed_dithering", "Enable Dithering; enabled|disabled" },
-#ifndef DRC_DISABLE
-      { "pcsx_rearmed_drc", "Dynamic recompiler; enabled|disabled" },
-#ifdef HAVE_PRE_ARMV7
-      { "pcsx_rearmed_psxclock", "PSX cpu clock (default 50); 50|51|52|53|54|55|5657|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49" },
-#else
-      { "pcsx_rearmed_psxclock", "PSX cpu clock (default 57); 57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56" },
-#endif
-#endif
-#ifdef __ARM_NEON__
-      { "pcsx_rearmed_neon_interlace_enable", "Enable interlacing mode(s); disabled|enabled" },
-      { "pcsx_rearmed_neon_enhancement_enable", "Enhanced resolution (slow); disabled|enabled" },
-      { "pcsx_rearmed_neon_enhancement_no_main", "Enhanced resolution speed hack; disabled|enabled" },
-#endif
-      { "pcsx_rearmed_duping_enable", "Frame duping; enabled|disabled" },
-      { "pcsx_rearmed_display_internal_fps", "Display Internal FPS; disabled|enabled" },
-      { "pcsx_rearmed_show_bios_bootlogo", "Show Bios Bootlogo(Breaks some games); disabled|enabled" },
-      { "pcsx_rearmed_spu_reverb", "Sound: Reverb; enabled|disabled" },
-      { "pcsx_rearmed_spu_interpolation", "Sound: Interpolation; simple|gaussian|cubic|off" },
-      { "pcsx_rearmed_idiablofix", "Diablo Music Fix; disabled|enabled" },
-      { "pcsx_rearmed_pe2_fix", "Parasite Eve 2/Vandal Hearts 1/2 Fix; disabled|enabled" },
-      { "pcsx_rearmed_inuyasha_fix", "InuYasha Sengoku Battle Fix; disabled|enabled" },
-
-      /* Advance options */
-      { "pcsx_rearmed_noxadecoding", "XA Decoding; enabled|disabled" },
-      { "pcsx_rearmed_nocdaudio", "CD Audio; enabled|disabled" },
-#ifndef DRC_DISABLE
-      { "pcsx_rearmed_nosmccheck", "(Speed Hack) Disable SMC Checks; disabled|enabled" },
-      { "pcsx_rearmed_gteregsunneeded", "(Speed Hack) Assume GTE Regs Unneeded; disabled|enabled" },
-      { "pcsx_rearmed_nogteflags", "(Speed Hack) Disable GTE Flags; disabled|enabled" },
-#endif
-
-      { NULL, NULL }
-   };
-
-    if (cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging))
-        log_cb = logging.log;
-
    environ_cb = cb;
 
-   cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars);
+   if (cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging))
+      log_cb = logging.log;
+
+   libretro_set_core_options(environ_cb);
 }
 
 void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
@@ -597,6 +594,8 @@ static void update_controller_port_variable(unsigned port)
                        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
@@ -713,11 +712,15 @@ void retro_get_system_info(struct retro_system_info *info)
 
 void retro_get_system_av_info(struct retro_system_av_info *info)
 {
+
+       unsigned geom_height = vout_height > 0 ? vout_height : 240;
+       unsigned geom_width = vout_width > 0 ? vout_width : 320;
+
        memset(info, 0, sizeof(*info));
        info->timing.fps            = is_pal_mode ? 50 : 60;
        info->timing.sample_rate    = 44100;
-       info->geometry.base_width   = 320;
-       info->geometry.base_height  = 240;
+       info->geometry.base_width   = geom_width;
+       info->geometry.base_height  = geom_height;
        info->geometry.max_width    = VOUT_MAX_WIDTH;
        info->geometry.max_height   = VOUT_MAX_HEIGHT;
        info->geometry.aspect_ratio = 4.0 / 3.0;
@@ -863,15 +866,72 @@ void retro_cheat_set(unsigned index, bool enabled, const char *code)
                Cheats[index].Enabled = enabled;
 }
 
+// just in case, maybe a win-rt port in the future?
+#ifdef _WIN32
+#define SLASH '\\'
+#else
+#define SLASH '/'
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX  4096
+#endif
+
 /* multidisk support */
+static unsigned int disk_initial_index;
+static char disk_initial_path[PATH_MAX];
 static bool disk_ejected;
 static unsigned int disk_current_index;
 static unsigned int disk_count;
 static struct disks_state {
        char *fname;
+       char *flabel;
        int internal_index; // for multidisk eboots
 } disks[8];
 
+static void get_disk_label(char *disk_label, const char *disk_path, size_t len)
+{
+       const char *base = NULL;
+
+       if (!disk_path || (*disk_path == '\0'))
+               return;
+
+       base = strrchr(disk_path, SLASH);
+       if (!base)
+               base = disk_path;
+
+       if (*base == SLASH)
+               base++;
+
+       strncpy(disk_label, base, len - 1);
+       disk_label[len - 1] = '\0';
+
+       char *ext = strrchr(disk_label, '.');
+       if (ext)
+               *ext = '\0';
+}
+
+static void disk_init(void)
+{
+       size_t i;
+
+       disk_ejected       = false;
+       disk_current_index = 0;
+       disk_count         = 0;
+
+       for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {
+               if (disks[i].fname != NULL) {
+                       free(disks[i].fname);
+                       disks[i].fname = NULL;
+               }
+               if (disks[i].flabel != NULL) {
+                       free(disks[i].flabel);
+                       disks[i].flabel = NULL;
+               }
+               disks[i].internal_index = 0;
+       }
+}
+
 static bool disk_set_eject_state(bool ejected)
 {
        // weird PCSX API..
@@ -942,18 +1002,29 @@ static unsigned int disk_get_num_images(void)
 static bool disk_replace_image_index(unsigned index,
        const struct retro_game_info *info)
 {
-       char *old_fname;
-       bool ret = true;
+       char *old_fname  = NULL;
+       char *old_flabel = NULL;
+       bool ret         = true;
 
        if (index >= sizeof(disks) / sizeof(disks[0]))
                return false;
 
-       old_fname = disks[index].fname;
-       disks[index].fname = NULL;
+       old_fname  = disks[index].fname;
+       old_flabel = disks[index].flabel;
+
+       disks[index].fname          = NULL;
+       disks[index].flabel         = NULL;
        disks[index].internal_index = 0;
 
        if (info != NULL) {
+               char disk_label[PATH_MAX];
+               disk_label[0] = '\0';
+
                disks[index].fname = strdup(info->path);
+
+               get_disk_label(disk_label, info->path, PATH_MAX);
+               disks[index].flabel = strdup(disk_label);
+
                if (index == disk_current_index)
                        ret = disk_set_image_index(index);
        }
@@ -961,6 +1032,9 @@ static bool disk_replace_image_index(unsigned index,
        if (old_fname != NULL)
                free(old_fname);
 
+       if (old_flabel != NULL)
+               free(old_flabel);
+
        return ret;
 }
 
@@ -973,6 +1047,64 @@ static bool disk_add_image_index(void)
        return true;
 }
 
+static bool disk_set_initial_image(unsigned index, const char *path)
+{
+       if (index >= sizeof(disks) / sizeof(disks[0]))
+               return false;
+
+       if (!path || (*path == '\0'))
+               return false;
+
+       disk_initial_index = index;
+
+       strncpy(disk_initial_path, path, sizeof(disk_initial_path) - 1);
+       disk_initial_path[sizeof(disk_initial_path) - 1] = '\0';
+
+       return true;
+}
+
+static bool disk_get_image_path(unsigned index, char *path, size_t len)
+{
+       const char *fname = NULL;
+
+       if (len < 1)
+               return false;
+
+       if (index >= sizeof(disks) / sizeof(disks[0]))
+               return false;
+
+       fname = disks[index].fname;
+
+       if (!fname || (*fname == '\0'))
+               return false;
+
+       strncpy(path, fname, len - 1);
+       path[len - 1] = '\0';
+
+       return true;
+}
+
+static bool disk_get_image_label(unsigned index, char *label, size_t len)
+{
+       const char *flabel = NULL;
+
+       if (len < 1)
+               return false;
+
+       if (index >= sizeof(disks) / sizeof(disks[0]))
+               return false;
+
+       flabel = disks[index].flabel;
+
+       if (!flabel || (*flabel == '\0'))
+               return false;
+
+       strncpy(label, flabel, len - 1);
+       label[len - 1] = '\0';
+
+       return true;
+}
+
 static struct retro_disk_control_callback disk_control = {
        .set_eject_state = disk_set_eject_state,
        .get_eject_state = disk_get_eject_state,
@@ -983,22 +1115,24 @@ 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
-
-#ifndef PATH_MAX
-#define PATH_MAX  4096
-#endif
+static struct retro_disk_control_ext_callback disk_control_ext = {
+       .set_eject_state = disk_set_eject_state,
+       .get_eject_state = disk_get_eject_state,
+       .get_image_index = disk_get_image_index,
+       .set_image_index = disk_set_image_index,
+       .get_num_images = disk_get_num_images,
+       .replace_image_index = disk_replace_image_index,
+       .add_image_index = disk_add_image_index,
+       .set_initial_image = disk_set_initial_image,
+       .get_image_path = disk_get_image_path,
+       .get_image_label = disk_get_image_label,
+};
 
-static char base_dir[PATH_MAX];
+static char base_dir[1024];
 
 static bool read_m3u(const char *file)
 {
-       char line[PATH_MAX];
+       char line[1024];
        char name[PATH_MAX];
        FILE *f = fopen(file, "r");
        if (!f)
@@ -1016,8 +1150,16 @@ static bool read_m3u(const char *file)
 
                if (line[0] != '\0')
                {
+                       char disk_label[PATH_MAX];
+                       disk_label[0] = '\0';
+
                        snprintf(name, sizeof(name), "%s%c%s", base_dir, SLASH, line);
-                       disks[disk_count++].fname = strdup(name);
+                       disks[disk_count].fname = strdup(name);
+
+                       get_disk_label(disk_label, name, PATH_MAX);
+                       disks[disk_count].flabel = strdup(disk_label);
+
+                       disk_count++;
                }
        }
 
@@ -1071,9 +1213,24 @@ strcasestr(const char *s, const char*find)
 }
 #endif
 
+static void set_retro_memmap(void)
+{
+       struct retro_memory_map retromap = { 0 };
+       struct retro_memory_descriptor mmap =
+       {
+               0, psxM, 0, 0, 0, 0, 0x200000
+       };
+
+       retromap.descriptors = &mmap;
+       retromap.num_descriptors = 1;
+
+    environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &retromap);
+}
+
 bool retro_load_game(const struct retro_game_info *info)
 {
        size_t i;
+       unsigned int cd_index = 0;
        bool is_m3u = (strcasestr(info->path, ".m3u") != NULL);
 
    struct retro_input_descriptor desc[] = {
@@ -1270,15 +1427,8 @@ bool retro_load_game(const struct retro_game_info *info)
                plugins_opened = 0;
        }
 
-       for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {
-               if (disks[i].fname != NULL) {
-                       free(disks[i].fname);
-                       disks[i].fname = NULL;
-               }
-               disks[i].internal_index = 0;
-       }
+       disk_init();
 
-       disk_current_index = 0;
        extract_directory(base_dir, info->path, sizeof(base_dir));
 
        if (is_m3u) {
@@ -1287,11 +1437,30 @@ bool retro_load_game(const struct retro_game_info *info)
                        return false;
                }
        } else {
+               char disk_label[PATH_MAX];
+               disk_label[0] = '\0';
+
                disk_count = 1;
                disks[0].fname = strdup(info->path);
+
+               get_disk_label(disk_label, info->path, PATH_MAX);
+               disks[0].flabel = strdup(disk_label);
+       }
+
+       /* If this is an M3U file, attempt to set the
+        * initial disk image */
+       if (is_m3u &&
+                (disk_initial_index > 0) &&
+                (disk_initial_index < disk_count))     {
+               const char *fname = disks[disk_initial_index].fname;
+
+               if (fname && (*fname != '\0'))
+                       if (strcmp(disk_initial_path, fname) == 0)
+                               cd_index = disk_initial_index;
        }
 
-       set_cd_image(disks[0].fname);
+       set_cd_image(disks[cd_index].fname);
+       disk_current_index = cd_index;
 
        /* have to reload after set_cd_image for correct cdr plugin */
        if (LoadPlugins() == -1) {
@@ -1307,6 +1476,69 @@ bool retro_load_game(const struct retro_game_info *info)
                return false;
        }
 
+       /* Handle multi-disk images (i.e. PBP)
+        * > Cannot do this until after OpenPlugins() is
+        *   called (since this sets the value of
+        *   cdrIsoMultidiskCount) */
+       if (!is_m3u && (cdrIsoMultidiskCount > 1)) {
+               disk_count = cdrIsoMultidiskCount < 8 ? cdrIsoMultidiskCount : 8;
+
+               /* Small annoyance: We need to change the label
+                * of disk 0, so have to clear existing entries */
+               if (disks[0].fname != NULL)
+                       free(disks[0].fname);
+               disks[0].fname = NULL;
+
+               if (disks[0].flabel != NULL)
+                       free(disks[0].flabel);
+               disks[0].flabel = NULL;
+
+               for (i = 0; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++) {
+                       char disk_name[PATH_MAX];
+                       char disk_label[PATH_MAX];
+                       disk_name[0]  = '\0';
+                       disk_label[0] = '\0';
+
+                       disks[i].fname = strdup(info->path);
+
+                       get_disk_label(disk_name, info->path, PATH_MAX);
+                       snprintf(disk_label, sizeof(disk_label), "%s #%u", disk_name, (unsigned)i + 1);
+                       disks[i].flabel = strdup(disk_label);
+
+                       disks[i].internal_index = i;
+               }
+
+               /* This is not an M3U file, so initial disk
+                * image has not yet been set - attempt to
+                * do so now */
+               if ((disk_initial_index > 0) &&
+                        (disk_initial_index < disk_count))     {
+                       const char *fname = disks[disk_initial_index].fname;
+
+                       if (fname && (*fname != '\0'))
+                               if (strcmp(disk_initial_path, fname) == 0)
+                                       cd_index = disk_initial_index;
+               }
+
+               if (cd_index > 0) {
+                       CdromId[0]    = '\0';
+                       CdromLabel[0] = '\0';
+
+                       cdrIsoMultidiskSelect = disks[cd_index].internal_index;
+                       disk_current_index    = cd_index;
+                       set_cd_image(disks[cd_index].fname);
+
+                       if (ReloadCdromPlugin() < 0) {
+                               log_cb(RETRO_LOG_INFO, "failed to reload cdr plugins\n");
+                               return false;
+                       }
+                       if (CDR_open() < 0) {
+                               log_cb(RETRO_LOG_INFO, "failed to open cdr plugin\n");
+                               return false;
+                       }
+               }
+       }
+
        plugin_call_rearmed_cbs();
        dfinput_activate();
 
@@ -1323,14 +1555,7 @@ bool retro_load_game(const struct retro_game_info *info)
        }
        emu_on_new_cd(0);
 
-       // multidisk images
-       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;
-               }
-       }
+       set_retro_memmap();
 
        return true;
 }
@@ -1344,6 +1569,8 @@ void *retro_get_memory_data(unsigned id)
 {
        if (id == RETRO_MEMORY_SAVE_RAM)
                return Mcd1Data;
+       else if (id == RETRO_MEMORY_SYSTEM_RAM)
+               return psxM;
        else
                return NULL;
 }
@@ -1352,6 +1579,8 @@ size_t retro_get_memory_size(unsigned id)
 {
        if (id == RETRO_MEMORY_SAVE_RAM)
                return MCD_SIZE;
+       else if (id == RETRO_MEMORY_SYSTEM_RAM)
+               return 0x200000;
        else
                return 0;
 }
@@ -1387,6 +1616,9 @@ static void update_variables(bool in_flight)
 {
    struct retro_variable var;
    int i;
+#ifdef GPU_PEOPS
+   int gpu_peops_fix = 0;
+#endif
 
    var.value = NULL;
    var.key = "pcsx_rearmed_frameskip";
@@ -1431,6 +1663,18 @@ static void update_variables(bool in_flight)
       }
    }
 
+   var.value = NULL;
+   var.key = "pcsx_rearmed_analog_axis_modifier";
+   axis_bounds_modifier = true;
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "square") == 0) {
+        axis_bounds_modifier = true;
+         } else if (strcmp(var.value, "circle") == 0) {
+        axis_bounds_modifier = false;
+         }
+   }
+
    var.value = NULL;
    var.key = "pcsx_rearmed_vibration";
 
@@ -1450,6 +1694,7 @@ static void update_variables(bool in_flight)
       if (strcmp(var.value, "disabled") == 0) {
          pl_rearmed_cbs.gpu_peops.iUseDither = 0;
          pl_rearmed_cbs.gpu_peopsgl.bDrawDither = 0;
+         pl_rearmed_cbs.gpu_unai.dithering = 0;
 #ifdef __ARM_NEON__
          pl_rearmed_cbs.gpu_neon.allow_dithering = 0;
 #endif
@@ -1457,13 +1702,14 @@ static void update_variables(bool in_flight)
       else if (strcmp(var.value, "enabled") == 0) {
          pl_rearmed_cbs.gpu_peops.iUseDither = 1;
          pl_rearmed_cbs.gpu_peopsgl.bDrawDither = 1;
+         pl_rearmed_cbs.gpu_unai.dithering = 1;
 #ifdef __ARM_NEON__
          pl_rearmed_cbs.gpu_neon.allow_dithering = 1;
 #endif
       }
    }
 
-#ifdef __ARM_NEON__
+#ifdef GPU_NEON
    var.value = "NULL";
    var.key = "pcsx_rearmed_neon_interlace_enable";
 
@@ -1606,6 +1852,18 @@ static void update_variables(bool in_flight)
          Config.VSyncWA = 1;
    }
 
+#ifndef _WIN32
+   var.value = NULL;
+   var.key = "pcsx_rearmed_async_cd";
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "async") == 0)
+        Config.AsyncCD = 1;
+      else
+        Config.AsyncCD = 0;
+   }
+#endif
+
    var.value = NULL;
    var.key = "pcsx_rearmed_noxadecoding";
    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
@@ -1626,6 +1884,264 @@ static void update_variables(bool in_flight)
          Config.Cdda = 0;
    }
 
+   var.value = NULL;
+   var.key = "pcsx_rearmed_spuirq";
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "disabled") == 0)
+         Config.SpuIrq = 0;
+      else
+         Config.SpuIrq = 1;
+   }
+
+#ifndef DRC_DISABLE
+   var.value = NULL;
+   var.key = "pcsx_rearmed_nosmccheck";
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         new_dynarec_hacks |= NDHACK_NO_SMC_CHECK;
+      else
+         new_dynarec_hacks &= ~NDHACK_NO_SMC_CHECK;
+   }
+
+   var.value = NULL;
+   var.key = "pcsx_rearmed_gteregsunneeded";
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         new_dynarec_hacks |= NDHACK_GTE_UNNEEDED;
+      else
+         new_dynarec_hacks &= ~NDHACK_GTE_UNNEEDED;
+   }
+
+   var.value = NULL;
+   var.key = "pcsx_rearmed_nogteflags";
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         new_dynarec_hacks |= NDHACK_GTE_NO_FLAGS;
+      else
+         new_dynarec_hacks &= ~NDHACK_GTE_NO_FLAGS;
+   }
+#endif
+
+#ifdef GPU_PEOPS
+   var.value = "NULL";
+   var.key = "pcsx_rearmed_gpu_peops_odd_even_bit";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         gpu_peops_fix |= GPU_PEOPS_ODD_EVEN_BIT;
+   }
+
+   var.value = "NULL";
+   var.key = "pcsx_rearmed_gpu_peops_expand_screen_width";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         gpu_peops_fix |= GPU_PEOPS_EXPAND_SCREEN_WIDTH;
+   }
+
+   var.value = "NULL";
+   var.key = "pcsx_rearmed_gpu_peops_ignore_brightness";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         gpu_peops_fix |= GPU_PEOPS_IGNORE_BRIGHTNESS;
+   }
+
+   var.value = "NULL";
+   var.key = "pcsx_rearmed_gpu_peops_disable_coord_check";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         gpu_peops_fix |= GPU_PEOPS_DISABLE_COORD_CHECK;
+   }
+
+   var.value = "NULL";
+   var.key = "pcsx_rearmed_gpu_peops_lazy_screen_update";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         gpu_peops_fix |= GPU_PEOPS_LAZY_SCREEN_UPDATE;
+   }
+
+   var.value = "NULL";
+   var.key = "pcsx_rearmed_gpu_peops_old_frame_skip";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         gpu_peops_fix |= GPU_PEOPS_OLD_FRAME_SKIP;
+   }
+
+   var.value = "NULL";
+   var.key = "pcsx_rearmed_gpu_peops_repeated_triangles";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         gpu_peops_fix |= GPU_PEOPS_REPEATED_TRIANGLES;
+   }
+
+   var.value = "NULL";
+   var.key = "pcsx_rearmed_gpu_peops_quads_with_triangles";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         gpu_peops_fix |= GPU_PEOPS_QUADS_WITH_TRIANGLES;
+   }
+
+   var.value = "NULL";
+   var.key = "pcsx_rearmed_gpu_peops_fake_busy_state";
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      if (strcmp(var.value, "enabled") == 0)
+         gpu_peops_fix |= GPU_PEOPS_FAKE_BUSY_STATE;
+   }
+
+   if (pl_rearmed_cbs.gpu_peops.dwActFixes != gpu_peops_fix)
+      pl_rearmed_cbs.gpu_peops.dwActFixes = gpu_peops_fix;
+
+
+   /* Show/hide core options */
+
+   var.key = "pcsx_rearmed_show_gpu_peops_settings";
+   var.value = NULL;
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      int show_advanced_gpu_peops_settings_prev = show_advanced_gpu_peops_settings;
+
+      show_advanced_gpu_peops_settings = 1;
+      if (strcmp(var.value, "disabled") == 0)
+         show_advanced_gpu_peops_settings = 0;
+
+      if (show_advanced_gpu_peops_settings != show_advanced_gpu_peops_settings_prev)
+      {
+         unsigned i;
+         struct retro_core_option_display option_display;
+         char gpu_peops_option[9][45] = {
+            "pcsx_rearmed_gpu_peops_odd_even_bit",
+            "pcsx_rearmed_gpu_peops_expand_screen_width",
+            "pcsx_rearmed_gpu_peops_ignore_brightness",
+            "pcsx_rearmed_gpu_peops_disable_coord_check",
+            "pcsx_rearmed_gpu_peops_lazy_screen_update",
+            "pcsx_rearmed_gpu_peops_old_frame_skip",
+            "pcsx_rearmed_gpu_peops_repeated_triangles",
+            "pcsx_rearmed_gpu_peops_quads_with_triangles",
+            "pcsx_rearmed_gpu_peops_fake_busy_state",
+         };
+
+         option_display.visible = show_advanced_gpu_peops_settings;
+
+         for (i = 0; i < 9; i++)
+         {
+            option_display.key = gpu_peops_option[i];
+            environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
+         }
+      }
+   }
+#endif
+
+#ifdef GPU_UNAI
+   var.key = "pcsx_rearmed_gpu_unai_ilace_force";
+   var.value = NULL;
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "disabled") == 0)
+         pl_rearmed_cbs.gpu_unai.ilace_force = 0;
+      else if (strcmp(var.value, "enabled") == 0)
+         pl_rearmed_cbs.gpu_unai.ilace_force = 1;
+   }
+
+   var.key = "pcsx_rearmed_gpu_unai_pixel_skip";
+   var.value = NULL;
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "disabled") == 0)
+         pl_rearmed_cbs.gpu_unai.pixel_skip = 0;
+      else if (strcmp(var.value, "enabled") == 0)
+         pl_rearmed_cbs.gpu_unai.pixel_skip = 1;
+   }
+
+   var.key = "pcsx_rearmed_gpu_unai_lighting";
+   var.value = NULL;
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "disabled") == 0)
+         pl_rearmed_cbs.gpu_unai.lighting = 0;
+      else if (strcmp(var.value, "enabled") == 0)
+         pl_rearmed_cbs.gpu_unai.lighting = 1;
+   }
+
+   var.key = "pcsx_rearmed_gpu_unai_fast_lighting";
+   var.value = NULL;
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "disabled") == 0)
+         pl_rearmed_cbs.gpu_unai.fast_lighting = 0;
+      else if (strcmp(var.value, "enabled") == 0)
+         pl_rearmed_cbs.gpu_unai.fast_lighting = 1;
+   }
+
+   var.key = "pcsx_rearmed_gpu_unai_blending";
+   var.value = NULL;
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+   {
+      if (strcmp(var.value, "disabled") == 0)
+         pl_rearmed_cbs.gpu_unai.blending = 0;
+      else if (strcmp(var.value, "enabled") == 0)
+         pl_rearmed_cbs.gpu_unai.blending = 1;
+   }
+
+   var.key = "pcsx_rearmed_show_gpu_unai_settings";
+   var.value = NULL;
+
+   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
+   {
+      int show_advanced_gpu_unai_settings_prev = show_advanced_gpu_unai_settings;
+
+      show_advanced_gpu_unai_settings = 1;
+      if (strcmp(var.value, "disabled") == 0)
+         show_advanced_gpu_unai_settings = 0;
+
+      if (show_advanced_gpu_unai_settings != show_advanced_gpu_unai_settings_prev)
+      {
+         unsigned i;
+         struct retro_core_option_display option_display;
+         char gpu_unai_option[5][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",
+         };
+
+         option_display.visible = show_advanced_gpu_unai_settings;
+
+         for (i = 0; i < 5; i++)
+         {
+            option_display.key = gpu_unai_option[i];
+            environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
+         }
+      }
+   }
+#endif // GPU_UNAI
+
    if (in_flight) {
       // inform core things about possible config changes
       plugin_call_rearmed_cbs();
@@ -1656,7 +2172,8 @@ static void update_variables(bool in_flight)
             }
          }
       }
-#ifndef DRC_DISABLE
+
+#if defined(LIGHTREC) || defined(NEW_DYNAREC)
       var.value = "NULL";
       var.key = "pcsx_rearmed_psxclock";
       if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
@@ -1664,50 +2181,18 @@ static void update_variables(bool in_flight)
          int psxclock = atoi(var.value);
          cycle_multiplier = 10000 / psxclock;
       }
-
-      var.value = NULL;
-      var.key = "pcsx_rearmed_nosmccheck";
-      if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
-      {
-         if (strcmp(var.value, "enabled") == 0)
-            new_dynarec_hacks |= NDHACK_NO_SMC_CHECK;
-         else
-            new_dynarec_hacks &= ~NDHACK_NO_SMC_CHECK;
-      }
-
-      var.value = NULL;
-      var.key = "pcsx_rearmed_gteregsunneeded";
-      if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
-      {
-         if (strcmp(var.value, "enabled") == 0)
-            new_dynarec_hacks |= NDHACK_GTE_UNNEEDED;
-         else
-            new_dynarec_hacks &= ~NDHACK_GTE_UNNEEDED;
-      }
-
-      var.value = NULL;
-      var.key = "pcsx_rearmed_nogteflags";
-      if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
-      {
-         if (strcmp(var.value, "enabled") == 0)
-            new_dynarec_hacks |= NDHACK_GTE_NO_FLAGS;
-         else
-            new_dynarec_hacks &= ~NDHACK_GTE_NO_FLAGS;
-      }
 #endif
    }
 }
 
 // Taken from beetle-psx-libretro
-static uint16_t get_analog_button(retro_input_state_t input_state_cb, int player_index, int id)
+static uint16_t get_analog_button(int16_t ret, retro_input_state_t input_state_cb, int player_index, int id)
 {
-       uint16_t button;
-
        // NOTE: Analog buttons were added Nov 2017. Not all front-ends support this
        // feature (or pre-date it) so we need to handle this in a graceful way.
 
        // First, try and get an analog value using the new libretro API constant
-       button = input_state_cb(player_index,
+       uint16_t button = input_state_cb(player_index,
                                                                        RETRO_DEVICE_ANALOG,
                                                                        RETRO_DEVICE_INDEX_ANALOG_BUTTON,
                                                                        id);
@@ -1721,15 +2206,29 @@ static uint16_t get_analog_button(retro_input_state_t input_state_cb, int player
 
                // NOTE: If we're really just not holding the button, we're still going to get zero.
 
-               button = input_state_cb(player_index,
-                                                                               RETRO_DEVICE_JOYPAD,
-                                                                               0,
-                                                                               id) ? 255 : 0;
+               button = (ret & (1 << id)) ? 255 : 0;
        }
 
        return button;
 }
 
+unsigned char axis_range_modifier(int16_t axis_value, bool is_square) {
+       float modifier_axis_range = 0;
+
+       if(is_square) {
+               modifier_axis_range = round((axis_value >> 8) / 0.785) + 128;
+               if(modifier_axis_range < 0) {
+                       modifier_axis_range = 0;
+               } else if(modifier_axis_range > 255) {
+                       modifier_axis_range = 255;
+               }
+       } else {
+               modifier_axis_range = MIN(((axis_value >> 8) + 128), 255);
+       }
+
+       return modifier_axis_range;
+}
+
 void retro_run(void)
 {
        int i;
@@ -1764,9 +2263,9 @@ void retro_run(void)
 
                        environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
                }
-       } else {
-               frame_count = 0;
        }
+   else
+               frame_count = 0;
 
        input_poll_cb();
 
@@ -1781,48 +2280,141 @@ void retro_run(void)
        float negcon_twist_amplitude;
        int negcon_i_rs;
        int negcon_ii_rs;
-       for(i = 0; i < PORTS_NUMBER; i++) {
+       for(i = 0; i < PORTS_NUMBER; i++)
+   {
+      int16_t ret    = 0;
                in_keystate[i] = 0;
 
                if (in_type[i] == 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))
+               ret |= (1 << j);
+         }
+      }
+
+               if (in_type[i] == PSE_PAD_TYPE_GUNCON)
+               {
+                       //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);
+                       }
+                       
+                       // B
+                       if (input_state_cb(1, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE)){
+                               in_keystate[i] |= (1 << DKEY_CROSS);
+                       }
+                       
+                       //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);
+                       
+                       //This adjustment process gives the user the ability to manually align the mouse up better 
+                       //with where the shots are in the emulator.
+                       
+                       //Percentage distance of screen to adjust 
+                       int GunconAdjustX = 0;
+                       int GunconAdjustY = 0;
+                       
+                       //Used when out by a percentage
+                       float GunconAdjustRatioX = 1;
+                       float GunconAdjustRatioY = 1;
+                               
+                       struct retro_variable var;
+                       var.value = NULL;
+                       var.key = "pcsx_rearmed_gunconadjustx";
+                       if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+                       {
+                               GunconAdjustX = atoi(var.value);        
+                       }
+                       
+                       var.value = NULL;
+                       var.key = "pcsx_rearmed_gunconadjusty";
+                       if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+                       {
+                               GunconAdjustY = atoi(var.value);        
+                       } 
+                       
+                       
+                       var.value = NULL;
+                       var.key = "pcsx_rearmed_gunconadjustratiox";
+                       if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+                       {
+                               GunconAdjustRatioX = atof(var.value);   
+                       } 
+                       
+                       
+                       var.value = NULL;
+                       var.key = "pcsx_rearmed_gunconadjustratioy";
+                       if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
+                       {
+                               GunconAdjustRatioY = atof(var.value);   
+                       } 
+                       
+                       //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)
                {
                        // Query digital inputs
                        //
                        // > Pad-Up
-                       if (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)){
+                       if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_UP))
                                in_keystate[i] |= (1 << DKEY_UP);
-                       }
                        // > Pad-Right
-                       if (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)){
+                       if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT))
                                in_keystate[i] |= (1 << DKEY_RIGHT);
-                       }
                        // > Pad-Down
-                       if (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)){
+                       if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN))
                                in_keystate[i] |= (1 << DKEY_DOWN);
-                       }
                        // > Pad-Left
-                       if (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)){
+                       if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT))
                                in_keystate[i] |= (1 << DKEY_LEFT);
-                       }
                        // > Start
-                       if (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)){
+                       if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START))
                                in_keystate[i] |= (1 << DKEY_START);
-                       }
                        // > neGcon A
-                       if (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A)){
+                       if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_A))
                                in_keystate[i] |= (1 << DKEY_CIRCLE);
-                       }
                        // > neGcon B
-                       if (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X)){
+                       if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_X))
                                in_keystate[i] |= (1 << DKEY_TRIANGLE);
-                       }
                        // > neGcon R shoulder (digital)
-                       if (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)){
+                       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':
@@ -1836,24 +2428,23 @@ void retro_run(void)
                        // > 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){
+                       if (lsx > negcon_deadzone)
                                lsx = lsx - negcon_deadzone;
-                       } else if (lsx < -negcon_deadzone){
+                       else if (lsx < -negcon_deadzone)
                                lsx = lsx + negcon_deadzone;
-                       } else {
+                       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){
+                       if (negcon_linearity == 2)
+         {
+                               if (negcon_twist_amplitude < 0.0)
                                        negcon_twist_amplitude = -(negcon_twist_amplitude * negcon_twist_amplitude);
-                               } else {
+            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;
                        }
+         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
@@ -1868,55 +2459,52 @@ void retro_run(void)
                                // (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){
+                               if (rsy > negcon_deadzone)
                                        rsy = rsy - negcon_deadzone;
-                               } else {
+            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 {
-                               if (rsy < -negcon_deadzone){
+                               if (rsy < -negcon_deadzone)
                                        rsy = -1 * (rsy + negcon_deadzone);
-                               } else {
+            else
                                        rsy = 0;
-                               }
                                negcon_i_rs = MIN((int)(((float)rsy / (float)(NEGCON_RANGE - negcon_deadzone)) * 255.0f), 255);
                        }
                        // >> NeGcon I
                        in_analog_right[i][1] = MAX(
                                MAX(
-                                       get_analog_button(input_state_cb, i, RETRO_DEVICE_ID_JOYPAD_R2),
-                                       get_analog_button(input_state_cb, i, RETRO_DEVICE_ID_JOYPAD_B)
+                                       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(input_state_cb, i, RETRO_DEVICE_ID_JOYPAD_L2),
-                                       get_analog_button(input_state_cb, i, RETRO_DEVICE_ID_JOYPAD_Y)
+                                       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(input_state_cb, i, RETRO_DEVICE_ID_JOYPAD_L);
+                       in_analog_left[i][1] = get_analog_button(ret, input_state_cb, i, RETRO_DEVICE_ID_JOYPAD_L);
                }
-               else
+               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 (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, j)){
+                       for (j = 0; j < RETRO_PSX_MAP_LEN; j++)
+                               if (ret & (1 << j))
                                        in_keystate[i] |= retro_psx_map[j];
-                               }
-                       }
+
                        // Query analog inputs
                        if (in_type[i] == PSE_PAD_TYPE_ANALOGJOY || in_type[i] == PSE_PAD_TYPE_ANALOGPAD)
                        {
-                               in_analog_left[i][0] = MIN((input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) / 255) + 128, 255);
-                               in_analog_left[i][1] = MIN((input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / 255) + 128, 255);
-                               in_analog_right[i][0] = MIN((input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X) / 255) + 128, 255);
-                               in_analog_right[i][1] = MIN((input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y) / 255) + 128, 255);
+                               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);
                        }
                }
        }
@@ -1970,7 +2558,7 @@ static bool find_any_bios(const char *dirpath, char *path, size_t path_size)
                return false;
 
        while ((ent = readdir(dir))) {
-               if (strncasecmp(ent->d_name, "scph", 4) != 0)
+               if ((strncasecmp(ent->d_name, "scph", 4) != 0) && (strncasecmp(ent->d_name, "psx", 3) != 0))
                        continue;
 
                snprintf(path, path_size, "%s%c%s", dirpath, SLASH, ent->d_name);
@@ -2032,10 +2620,11 @@ static int init_memcards(void)
 static void loadPSXBios(void)
 {
        const char *dir;
-       char path[256];
+       char path[PATH_MAX];
        unsigned useHLE = 0;
 
        const char *bios[] = {
+               "PSXONPSP660", "psxonpsp660",
                "SCPH101", "scph101",
                "SCPH5501", "scph5501",
                "SCPH7001", "scph7001",
@@ -2090,6 +2679,7 @@ static void loadPSXBios(void)
 
 void retro_init(void)
 {
+       unsigned dci_version = 0;
        struct retro_rumble_interface rumble;
        int ret;
 
@@ -2124,7 +2714,7 @@ void retro_init(void)
 
 #ifdef _3DS
    vout_buf = linearMemAlign(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2, 0x80);
-#elif defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L) && !defined(VITA)
+#elif defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L) && !defined(VITA) && !defined(__SWITCH__)
        posix_memalign(&vout_buf, 16, VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
 #else
        vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
@@ -2135,7 +2725,13 @@ void retro_init(void)
        loadPSXBios();
 
        environ_cb(RETRO_ENVIRONMENT_GET_CAN_DUPE, &vout_can_dupe);
-       environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
+
+       disk_initial_index   = 0;
+       disk_initial_path[0] = '\0';
+       if (environ_cb(RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION, &dci_version) && (dci_version >= 1))
+               environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE, &disk_control_ext);
+       else
+               environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
 
        rumble_cb = NULL;
        if (environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble))
@@ -2149,6 +2745,7 @@ void retro_init(void)
        cycle_multiplier = 200;
 #endif
        pl_rearmed_cbs.gpu_peops.iUseDither = 1;
+       pl_rearmed_cbs.gpu_peops.dwActFixes = GPU_PEOPS_OLD_FRAME_SKIP;
        spu_config.iUseFixedUpdates = 1;
 
        SaveFuncs.open = save_open;
@@ -2157,12 +2754,16 @@ void retro_init(void)
        SaveFuncs.seek = save_seek;
        SaveFuncs.close = save_close;
 
+   if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
+      libretro_supports_bitmasks = true;
+
        update_variables(false);
        check_system_specs();
 }
 
 void retro_deinit(void)
 {
+       ClosePlugins();
        SysClose();
 #ifdef _3DS
    linearFree(vout_buf);
@@ -2174,6 +2775,11 @@ void retro_deinit(void)
 #ifdef VITA
   deinit_vita_mmap();
 #endif
+   libretro_supports_bitmasks = false;
+
+       /* Have to reset disks struct, otherwise
+        * fnames/flabels will leak memory */
+       disk_init();
 }
 
 #ifdef VITA
@@ -2183,3 +2789,15 @@ int usleep (unsigned long us)
    sceKernelDelayThread(us);
 }
 #endif
+
+void SysPrintf(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_INFO, "%s", msg);
+}