gpu_neon: make use of unai's lineskip mode
[pcsx_rearmed.git] / frontend / menu.c
index ce65ccf..d3e16a4 100644 (file)
@@ -13,6 +13,9 @@
 #include <errno.h>
 #include <dlfcn.h>
 #include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #include "main.h"
 #include "menu.h"
 #include "linux/in_evdev.h"
 #include "../libpcsxcore/misc.h"
 #include "../libpcsxcore/cdrom.h"
+#include "../libpcsxcore/cdriso.h"
 #include "../libpcsxcore/psemu_plugin_defs.h"
 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
 #include "../plugins/dfinput/main.h"
 #include "revision.h"
 
+#define REARMED_BIRTHDAY_TIME 1293306830       /* 25 Dec 2010 */
+
 #define array_size(x) (sizeof(x) / sizeof(x[0]))
 
 typedef enum
@@ -44,6 +50,7 @@ typedef enum
        MA_MAIN_RESET_GAME,
        MA_MAIN_LOAD_ROM,
        MA_MAIN_SWAP_CD,
+       MA_MAIN_SWAP_CD_MULTI,
        MA_MAIN_RUN_BIOS,
        MA_MAIN_RUN_EXE,
        MA_MAIN_CONTROLS,
@@ -51,11 +58,13 @@ typedef enum
        MA_MAIN_EXIT,
        MA_CTRL_PLAYER1,
        MA_CTRL_PLAYER2,
+       MA_CTRL_ANALOG,
        MA_CTRL_EMU,
        MA_CTRL_DEV_FIRST,
        MA_CTRL_DEV_NEXT,
        MA_CTRL_NUBS_BTNS,
        MA_CTRL_DEADZONE,
+       MA_CTRL_VIBRATION,
        MA_CTRL_DONE,
        MA_OPT_SAVECFG,
        MA_OPT_SAVECFG_GAME,
@@ -77,8 +86,18 @@ static int scaling, filter, cpu_clock, cpu_clock_st, volume_boost, frameskip;
 static char rom_fname_reload[MAXPATHLEN];
 static char last_selected_fname[MAXPATHLEN];
 static int warned_about_bios, region, in_type_sel1, in_type_sel2;
+static int psx_clock;
 static int memcard1_sel, memcard2_sel;
-int g_opts, analog_deadzone;
+int g_opts;
+int analog_deadzone; // for Caanoo
+
+#ifdef __ARM_ARCH_7A__
+#define DEFAULT_PSX_CLOCK 57
+#define DEFAULT_PSX_CLOCK_S "57"
+#else
+#define DEFAULT_PSX_CLOCK 50
+#define DEFAULT_PSX_CLOCK_S "50"
+#endif
 
 // sound plugin
 extern int iUseReverb;
@@ -110,10 +129,30 @@ void emu_make_path(char *buff, const char *end, int size)
                printf("Warning: path truncated: %s\n", buff);
 }
 
-static int emu_check_save_file(int slot)
+static int emu_check_save_file(int slot, int *time)
 {
-       int ret = emu_check_state(slot);
-       return ret == 0 ? 1 : 0;
+       char fname[MAXPATHLEN];
+       struct stat status;
+       int ret;
+       
+       ret = emu_check_state(slot);
+       if (ret != 0 || time == NULL)
+               return ret == 0 ? 1 : 0;
+
+       ret = get_state_filename(fname, sizeof(fname), slot);
+       if (ret != 0)
+               return 1;
+
+       ret = stat(fname, &status);
+       if (ret != 0)
+               return 1;
+
+       if (status.st_mtime < REARMED_BIRTHDAY_TIME)
+               return 1; // probably bad rtc like on some Caanoos
+
+       *time = status.st_mtime;
+
+       return 1;
 }
 
 static int emu_save_load_game(int load, int unused)
@@ -146,6 +185,8 @@ static void menu_sync_config(void)
                Config.PsxAuto = 0;
                Config.PsxType = region - 1;
        }
+       cycle_multiplier = 10000 / psx_clock;
+
        switch (in_type_sel1) {
        case 1:  in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
        case 2:  in_type1 = PSE_PAD_TYPE_GUNCON;    break;
@@ -157,7 +198,7 @@ static void menu_sync_config(void)
        default: in_type2 = PSE_PAD_TYPE_STANDARD;
        }
        if (in_evdev_allow_abs_only != allow_abs_only_old) {
-               plat_rescan_inputs();
+               in_probe();
                allow_abs_only_old = in_evdev_allow_abs_only;
        }
 
@@ -168,30 +209,18 @@ static void menu_sync_config(void)
 
 static void menu_set_defconfig(void)
 {
+       emu_set_default_config();
+
        g_opts = 0;
        scaling = SCALE_4_3;
        volume_boost = 0;
        frameskip = 0;
-       analog_deadzone = 70;
+       analog_deadzone = 50;
+       psx_clock = DEFAULT_PSX_CLOCK;
 
        region = 0;
        in_type_sel1 = in_type_sel2 = 0;
        in_evdev_allow_abs_only = 0;
-       Config.Xa = Config.Cdda = Config.Sio =
-       Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
-       Config.CdrReschedule = 0;
-
-       pl_rearmed_cbs.gpu_peops.iUseDither = 0;
-       pl_rearmed_cbs.gpu_peops.dwActFixes = 1<<7;
-       pl_rearmed_cbs.gpu_unai.abe_hack =
-       pl_rearmed_cbs.gpu_unai.no_light =
-       pl_rearmed_cbs.gpu_unai.no_blend = 0;
-
-       iUseReverb = 2;
-       iUseInterpolation = 1;
-       iXAPitch = 0;
-       iSPUIRQWait = 1;
-       iUseTimer = 2;
 
        menu_sync_config();
 }
@@ -251,9 +280,10 @@ static const struct {
        CE_INTVAL(in_type_sel1),
        CE_INTVAL(in_type_sel2),
        CE_INTVAL(analog_deadzone),
-       CE_INTVAL_V(frameskip, 2),
+       CE_INTVAL_V(frameskip, 3),
        CE_INTVAL_P(gpu_peops.iUseDither),
        CE_INTVAL_P(gpu_peops.dwActFixes),
+       CE_INTVAL_P(gpu_unai.lineskip),
        CE_INTVAL_P(gpu_unai.abe_hack),
        CE_INTVAL_P(gpu_unai.no_light),
        CE_INTVAL_P(gpu_unai.no_blend),
@@ -265,6 +295,9 @@ static const struct {
        CE_INTVAL(warned_about_bios),
        CE_INTVAL(in_evdev_allow_abs_only),
        CE_INTVAL(volume_boost),
+       CE_INTVAL(psx_clock),
+       CE_INTVAL(new_dynarec_hacks),
+       CE_INTVAL(in_enable_vibration),
 };
 
 static char *get_cd_label(void)
@@ -361,7 +394,7 @@ static int menu_load_config(int is_game)
        f = fopen(cfgfile, "r");
        if (f == NULL) {
                printf("menu_load_config: failed to open: %s\n", cfgfile);
-               return -1;
+               goto fail;
        }
 
        fseek(f, 0, SEEK_END);
@@ -429,6 +462,14 @@ static int menu_load_config(int is_game)
                }
        }
 
+       keys_load_all(cfg);
+       ret = 0;
+fail_read:
+       free(cfg);
+fail:
+       if (f != NULL)
+               fclose(f);
+
        menu_sync_config();
 
        // sync plugins
@@ -444,20 +485,16 @@ static int menu_load_config(int is_game)
                if (strcmp(Config.Spu, spu_plugins[i]) == 0)
                        { spu_plugsel = i; break; }
 
-       keys_load_all(cfg);
-       ret = 0;
-fail_read:
-       free(cfg);
-fail:
-       fclose(f);
        return ret;
 }
 
 // rrrr rggg gggb bbbb
 static unsigned short fname2color(const char *fname)
 {
-       static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z", ".bz", ".znx", ".pbp" };
-       static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub", ".table", ".index", ".sbi" };
+       static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z",
+                                           ".bz", ".znx", ".pbp", ".cbn" };
+       static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub",
+                                           ".table", ".index", ".sbi" };
        const char *ext = strrchr(fname, '.');
        int i;
 
@@ -719,6 +756,10 @@ me_bind_action emuctrl_actions[] =
        { "Gun A button     ", 1 << SACTION_GUN_A },
        { "Gun B button     ", 1 << SACTION_GUN_B },
        { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
+#ifndef __ARM_ARCH_7A__ /* XXX */
+       { "Volume Up        ", 1 << SACTION_VOLUME_UP },
+       { "Volume Down      ", 1 << SACTION_VOLUME_DOWN },
+#endif
        { NULL,                0 }
 };
 
@@ -796,7 +837,7 @@ static void keys_write_all(FILE *f)
                        }
 
                        kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
-                       for (i = 0; kbinds && i < ARRAY_SIZE(emuctrl_actions) - 1; i++) {
+                       for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
                                mask = emuctrl_actions[i].mask;
                                if (mask & kbinds) {
                                        strncpy(act, emuctrl_actions[i].name, 31);
@@ -805,6 +846,12 @@ static void keys_write_all(FILE *f)
                                }
                        }
                }
+
+               for (k = 0; k < array_size(in_adev); k++)
+               {
+                       if (in_adev[k] == d)
+                               fprintf(f, "bind_analog = %d\n", k);
+               }
        }
 }
 
@@ -847,7 +894,7 @@ static void keys_load_all(const char *cfg)
        char dev[256], key[128], *act;
        const char *p;
        int bind, bindtype;
-       int dev_id;
+       int ret, dev_id;
 
        p = cfg;
        while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
@@ -865,6 +912,21 @@ static void keys_load_all(const char *cfg)
                        if (strncmp(p, "binddev = ", 10) == 0)
                                break;
 
+                       if (strncmp(p, "bind_analog", 11) == 0) {
+                               ret = sscanf(p, "bind_analog = %d", &bind);
+                               p += 11;
+                               if (ret != 1) {
+                                       printf("input: parse error: %16s..\n", p);
+                                       continue;
+                               }
+                               if ((unsigned int)bind >= array_size(in_adev)) {
+                                       printf("input: analog id %d out of range\n", bind);
+                                       continue;
+                               }
+                               in_adev[bind] = dev_id;
+                               continue;
+                       }
+
                        p += 4;
                        if (*p != ' ') {
                                printf("input: parse error: %16s..\n", p);
@@ -912,6 +974,57 @@ static int key_config_loop_wrap(int id, int keys)
        return 0;
 }
 
+static const char *adevnames[IN_MAX_DEVS + 2];
+static int stick_sel[2];
+
+static menu_entry e_menu_keyconfig_analog[] =
+{
+       mee_enum ("Left stick (L3)",  0, stick_sel[0], adevnames),
+       mee_range("  X axis",    0, in_adev_axis[0][0], 0, 7),
+       mee_range("  Y axis",    0, in_adev_axis[0][1], 0, 7),
+       mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
+       mee_range("  X axis",    0, in_adev_axis[1][0], 0, 7),
+       mee_range("  Y axis",    0, in_adev_axis[1][1], 0, 7),
+       mee_end,
+};
+
+static int key_config_analog(int id, int keys)
+{
+       int i, d, count, sel = 0;
+       int sel2dev_map[IN_MAX_DEVS];
+
+       memset(adevnames, 0, sizeof(adevnames));
+       memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
+       memset(stick_sel, 0, sizeof(stick_sel));
+
+       adevnames[0] = "None";
+       i = 1;
+       for (d = 0; d < IN_MAX_DEVS; d++)
+       {
+               const char *name = in_get_dev_name(d, 0, 1);
+               if (name == NULL)
+                       continue;
+
+               count = 0;
+               in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
+               if (count == 0)
+                       continue;
+
+               if (in_adev[0] == d) stick_sel[0] = i;
+               if (in_adev[1] == d) stick_sel[1] = i;
+               sel2dev_map[i] = d;
+               adevnames[i++] = name;
+       }
+       adevnames[i] = NULL;
+
+       me_loop(e_menu_keyconfig_analog, &sel);
+
+       in_adev[0] = sel2dev_map[stick_sel[0]];
+       in_adev[1] = sel2dev_map[stick_sel[1]];
+
+       return 0;
+}
+
 static const char *mgn_dev_name(int id, int *offs)
 {
        const char *name = NULL;
@@ -948,7 +1061,7 @@ static int mh_savecfg(int id, int keys)
 static int mh_input_rescan(int id, int keys)
 {
        //menu_sync_config();
-       plat_rescan_inputs();
+       in_probe();
        me_update_msg("rescan complete.");
 
        return 0;
@@ -961,24 +1074,26 @@ static const char *men_in_type_sel[] = {
        NULL
 };
 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
-static const char h_notsgun[] =  "Don't trigger (shoot) when touching screen in gun games.";
+static const char h_notsgun[]  = "Don't trigger (shoot) when touching screen in gun games.";
+static const char h_vibration[]= "Must select analog above and enable this ingame too.";
 
 static menu_entry e_menu_keyconfig[] =
 {
        mee_handler_id("Player 1",              MA_CTRL_PLAYER1,    key_config_loop_wrap),
        mee_handler_id("Player 2",              MA_CTRL_PLAYER2,    key_config_loop_wrap),
+       mee_handler_id("Analog controls",       MA_CTRL_ANALOG,     key_config_analog),
        mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU,        key_config_loop_wrap),
        mee_label     (""),
        mee_enum      ("Port 1 device",     0, in_type_sel1,    men_in_type_sel),
        mee_enum      ("Port 2 device",     0, in_type_sel2,    men_in_type_sel),
        mee_onoff_h   ("Nubs as buttons",   MA_CTRL_NUBS_BTNS,  in_evdev_allow_abs_only, 1, h_nub_btns),
+       mee_onoff_h   ("Vibration",         MA_CTRL_VIBRATION,  in_enable_vibration, 1, h_vibration),
        mee_range     ("Analog deadzone",   MA_CTRL_DEADZONE,   analog_deadzone, 1, 99),
        mee_onoff_h   ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
        mee_cust_nosave("Save global config",       MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
        mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
-       mee_handler   ("Rescan devices",  mh_input_rescan),
+       mee_handler   ("Rescan devices:",  mh_input_rescan),
        mee_label     (""),
-       mee_label     ("Input devices:"),
        mee_label_mk  (MA_CTRL_DEV_FIRST, mgn_dev_name),
        mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
        mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
@@ -1077,6 +1192,7 @@ static int menu_loop_gfx_options(int id, int keys)
 
 static menu_entry e_menu_plugin_gpu_unai[] =
 {
+       mee_onoff     ("Skip every 2nd line",        0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
        mee_onoff     ("Abe's Odyssey hack",         0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
        mee_onoff     ("Disable lighting",           0, pl_rearmed_cbs.gpu_unai.no_light, 1),
        mee_onoff     ("Disable blending",           0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
@@ -1184,6 +1300,27 @@ static int menu_loop_plugin_options(int id, int keys)
 
 // ------------ adv options menu ------------
 
+static const char h_cfg_psxclk[]  = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n";
+static const char h_cfg_nosmc[]   = "Will cause crashes when loading, break memcards";
+static const char h_cfg_gteunn[]  = "May cause graphical glitches";
+static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
+
+static menu_entry e_menu_speed_hacks[] =
+{
+       mee_range_h   ("PSX CPU clock, %%",        0, psx_clock, 1, 500, h_cfg_psxclk),
+       mee_onoff_h   ("Disable SMC checks",       0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
+       mee_onoff_h   ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
+       mee_onoff_h   ("Disable GTE flags",        0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
+       mee_end,
+};
+
+static int menu_loop_speed_hacks(int id, int keys)
+{
+       static int sel = 0;
+       me_loop(e_menu_speed_hacks, &sel);
+       return 0;
+}
+
 static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
 static const char h_cfg_spu[]    = "Shows active SPU channels\n"
@@ -1198,10 +1335,11 @@ static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
                                   "(timing hack, breaks other games)";
 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix\n"
                                   "(timing hack, breaks other games)";
-static const char h_cfg_cdrr[]   = "Compatibility tweak (fixes Team Buddies, maybe more)\n"
-                                  "(CD timing hack, breaks FMVs)";
+static const char h_cfg_cdrr[]   = "Compatibility tweak (CD timing hack, breaks FMVs)";
 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
                                   "Might be useful to overcome some dynarec bugs";
+static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
+                                  "must reload game for any change to take effect";
 
 static menu_entry e_menu_adv_options[] =
 {
@@ -1212,10 +1350,11 @@ static menu_entry e_menu_adv_options[] =
        mee_onoff_h   ("Disable CD Audio",       0, Config.Cdda, 1, h_cfg_cdda),
        mee_onoff_h   ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
        mee_onoff_h   ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
-       mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
+       //mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
        mee_onoff_h   ("Rootcounter hack 2",     0, Config.VSyncWA, 1, h_cfg_rcnt2),
        mee_enum_h    ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
        mee_onoff_h   ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
+       mee_handler_h ("[Speed hacks]",             menu_loop_speed_hacks, h_cfg_shacks),
        mee_end,
 };
 
@@ -1236,7 +1375,7 @@ static int mh_restore_defaults(int id, int keys)
 }
 
 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
-static const char *men_frameskip[]    = { "Auto", "Off", "1", NULL };
+static const char *men_frameskip[]    = { "Auto", "Off", "1", "2", "3", NULL };
 /*
 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
@@ -1440,14 +1579,22 @@ static void menu_bios_warn(void)
 {
        int inp;
        static const char msg[] =
-               "You don't seem to have copied any BIOS files to\n"
+               "You don't seem to have copied any BIOS\n"
+               "files to\n"
+#ifdef __ARM_ARCH_7A__ // XXX
                "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
-               "While many games work fine with fake (HLE) BIOS,\n"
-               "others (like MGS and FF8) require BIOS to work.\n"
-               "After copying the file, you'll also need to\n"
-               "select it in the emu's options->[BIOS/Plugins]\n\n"
-               "The file is usually named SCPH1001.BIN, but\n"
-               "other not compressed files can be used too.\n\n"
+#else
+               "pcsx_rearmed/bios/\n\n"
+#endif
+               "While many games work fine with fake\n"
+               "(HLE) BIOS, others (like MGS and FF8)\n"
+               "require BIOS to work.\n"
+               "After copying the file, you'll also need\n"
+               "to select it in the emu's menu:\n"
+               "options->[BIOS/Plugins]\n\n"
+               "The file is usually named SCPH1001.BIN,\n"
+               "but other not compressed files can be\n"
+               "used too.\n\n"
                "Press (B) or (X) to continue";
 
        while (1)
@@ -1536,6 +1683,7 @@ static int reload_plugins(const char *cdimg)
        }
        plugin_call_rearmed_cbs();
 
+       cdrIsoMultidiskCount = 1;
        CdromId[0] = '\0';
        CdromLabel[0] = '\0';
 
@@ -1604,6 +1752,8 @@ static int run_cd_image(const char *fname)
        }
 
        ready_to_go = 1;
+       snprintf(hud_msg, sizeof(hud_msg), "Booting up...");
+       hud_new_msg = 2;
        return 0;
 }
 
@@ -1636,6 +1786,9 @@ static int romsel_run(void)
                        return -1;
        }
 
+       if (Config.HLE)
+               printf("note: running without BIOS, expect compatibility problems\n");
+
        strcpy(last_selected_fname, rom_fname_reload);
        return 0;
 }
@@ -1670,6 +1823,24 @@ static int swap_cd_image(void)
        return 0;
 }
 
+static int swap_cd_multidisk(void)
+{
+       cdrIsoMultidiskSelect++;
+       CdromId[0] = '\0';
+       CdromLabel[0] = '\0';
+
+       CDR_close();
+       if (CDR_open() < 0) {
+               me_update_msg("failed to open cdr plugin");
+               return -1;
+       }
+
+       SetCdOpenCaseTime(time(NULL) + 2);
+       LidInterrupt();
+
+       return 0;
+}
+
 static int main_menu_handler(int id, int keys)
 {
        switch (id)
@@ -1698,6 +1869,10 @@ static int main_menu_handler(int id, int keys)
                if (swap_cd_image() == 0)
                        return 1;
                break;
+       case MA_MAIN_SWAP_CD_MULTI:
+               if (swap_cd_multidisk() == 0)
+                       return 1;
+               break;
        case MA_MAIN_RUN_BIOS:
                if (run_bios() == 0)
                        return 1;
@@ -1723,9 +1898,10 @@ static int main_menu_handler(int id, int keys)
 
 static menu_entry e_menu_main2[] =
 {
-       mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,     main_menu_handler),
-       mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,    main_menu_handler),
-       mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,     main_menu_handler),
+       mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
+       mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
+       mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
+       mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
        mee_handler   ("Memcard manager",    menu_loop_memcards),
        mee_end,
 };
@@ -1735,6 +1911,7 @@ static int main_menu2_handler(int id, int keys)
        static int sel = 0;
 
        me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
+       me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
        me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
 
        return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
@@ -1971,6 +2148,7 @@ void menu_init(void)
        me_enable(e_menu_options, MA_OPT_DISP_OPTS, 0);
        me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
 #else
+       me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
        me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
 #endif
 }
@@ -1989,16 +2167,10 @@ void menu_notify_mode_change(int w, int h, int bpp)
                g_layer_w = w; g_layer_h = h;
                break;
 
-       case SCALE_4_3:
-               mult = 240.0f / (float)h * 4.0f / 3.0f;
-               if (h > 256)
-                       mult *= 2.0f;
-               g_layer_w = mult * (float)g_menuscreen_h;
-               g_layer_h = g_menuscreen_h;
-               printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
-               break;
-
        case SCALE_4_3v2:
+               if (h > g_menuscreen_h || (240 < h && h <= 360))
+                       goto fractional_4_3;
+
                // 4:3 that prefers integer scaling
                imult = g_menuscreen_h / h;
                g_layer_w = w * imult;
@@ -2009,6 +2181,16 @@ void menu_notify_mode_change(int w, int h, int bpp)
                printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
                break;
 
+       fractional_4_3:
+       case SCALE_4_3:
+               mult = 240.0f / (float)h * 4.0f / 3.0f;
+               if (h > 256)
+                       mult *= 2.0f;
+               g_layer_w = mult * (float)g_menuscreen_h;
+               g_layer_h = g_menuscreen_h;
+               printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
+               break;
+
        case SCALE_FULLSCREEN:
                g_layer_w = g_menuscreen_w;
                g_layer_h = g_menuscreen_h;