2 * (C) GraÅžvydas "notaz" Ignotas, 2010-2015
4 * This work is licensed under the terms of any of these licenses
6 * - GNU GPL, version 2 or later.
7 * - GNU LGPL, version 2.1 or later.
8 * See the COPYING file in the top-level directory.
17 #include <sys/types.h>
26 #include "plugin_lib.h"
30 #include "libpicofe/plat.h"
31 #include "libpicofe/input.h"
32 #include "libpicofe/linux/in_evdev.h"
33 #include "libpicofe/plat.h"
34 #include "../libpcsxcore/misc.h"
35 #include "../libpcsxcore/cdrom.h"
36 #include "../libpcsxcore/cdriso.h"
37 #include "../libpcsxcore/cheat.h"
38 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
39 #include "../plugins/dfinput/externals.h"
40 #include "../plugins/dfsound/spu_config.h"
41 #include "psemu_plugin_defs.h"
42 #include "arm_features.h"
45 #define REARMED_BIRTHDAY_TIME 1293306830 /* 25 Dec 2010 */
47 #define array_size(x) (sizeof(x) / sizeof(x[0]))
58 MA_MAIN_SWAP_CD_MULTI,
89 MA_OPT_SCANLINE_LEVEL,
92 static int last_vout_w, last_vout_h, last_vout_bpp;
93 static int cpu_clock, cpu_clock_st, volume_boost, frameskip;
94 static char last_selected_fname[MAXPATHLEN];
95 static int config_save_counter, region, in_type_sel1, in_type_sel2;
97 static int memcard1_sel = -1, memcard2_sel = -1;
98 extern int g_autostateld_opt;
99 static int menu_iopts[8];
100 int g_opts, g_scaler, g_gamma = 100;
101 int scanlines, scanline_level = 20;
102 int soft_scaling, analog_deadzone; // for Caanoo
105 #ifndef HAVE_PRE_ARMV7
106 #define DEFAULT_PSX_CLOCK (10000 / CYCLE_MULT_DEFAULT)
107 #define DEFAULT_PSX_CLOCK_S "57"
109 #define DEFAULT_PSX_CLOCK 50
110 #define DEFAULT_PSX_CLOCK_S "50"
113 static const char *bioses[24];
114 static const char *gpu_plugins[16];
115 static const char *spu_plugins[16];
116 static const char *memcards[32];
117 static int bios_sel, gpu_plugsel, spu_plugsel;
119 #ifndef UI_FEATURES_H
120 #define MENU_BIOS_PATH "bios/"
121 #define MENU_SHOW_VARSCALER 0
122 #define MENU_SHOW_VOUTMODE 1
123 #define MENU_SHOW_SCALER2 0
124 #define MENU_SHOW_NUBS_BTNS 0
125 #define MENU_SHOW_VIBRATION 0
126 #define MENU_SHOW_DEADZONE 0
127 #define MENU_SHOW_MINIMIZE 0
128 #define MENU_SHOW_FULLSCREEN 1
129 #define MENU_SHOW_VOLUME 0
132 static int min(int x, int y) { return x < y ? x : y; }
133 static int max(int x, int y) { return x > y ? x : y; }
135 void emu_make_path(char *buff, const char *end, int size)
139 end_len = strlen(end);
140 pos = plat_get_root_dir(buff, size);
141 strncpy(buff + pos, end, size - pos);
143 if (pos + end_len > size - 1)
144 printf("Warning: path truncated: %s\n", buff);
147 static int emu_check_save_file(int slot, int *time)
149 char fname[MAXPATHLEN];
153 ret = emu_check_state(slot);
154 if (ret != 0 || time == NULL)
155 return ret == 0 ? 1 : 0;
157 ret = get_state_filename(fname, sizeof(fname), slot);
161 ret = stat(fname, &status);
165 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
166 return 1; // probably bad rtc like on some Caanoos
168 *time = status.st_mtime;
173 static int emu_save_load_game(int load, int unused)
178 ret = emu_load_state(state_slot);
180 // reflect hle/bios mode from savestate
183 else if (bios_sel == 0 && bioses[1] != NULL)
184 // XXX: maybe find the right bios instead
188 ret = emu_save_state(state_slot);
193 static void rm_namelist_entry(struct dirent **namelist,
194 int count, const char *name)
198 for (i = 1; i < count; i++) {
199 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
202 if (strcmp(name, namelist[i]->d_name) == 0) {
210 static int optional_cdimg_filter(struct dirent **namelist, int count,
214 char buf[256], buf2[256];
215 int i, d, ret, good_cue;
222 for (i = 1; i < count; i++) {
223 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
226 ext = strrchr(namelist[i]->d_name, '.');
228 // should not happen but whatever
235 // first find .cue files and remove files they reference
236 if (strcasecmp(ext, "cue") == 0)
238 snprintf(buf, sizeof(buf), "%s/%s", basedir,
239 namelist[i]->d_name);
249 while (fgets(buf, sizeof(buf), f)) {
250 ret = sscanf(buf, " FILE \"%256[^\"]\"", buf2);
252 ret = sscanf(buf, " FILE %256s", buf2);
256 p = strrchr(buf2, '/');
258 p = strrchr(buf2, '\\');
264 snprintf(buf, sizeof(buf), "%s/%s", basedir, p);
265 ret = stat64(buf, &statf);
267 rm_namelist_entry(namelist, count, p);
280 p = strcasestr(namelist[i]->d_name, "track");
282 ret = strtoul(p + 5, NULL, 10);
292 for (i = d = 1; i < count; i++)
293 if (namelist[i] != NULL)
294 namelist[d++] = namelist[i];
299 // propagate menu settings to the emu vars
300 static void menu_sync_config(void)
302 static int allow_abs_only_old;
307 Config.PsxType = region - 1;
309 cycle_multiplier = 10000 / psx_clock;
311 switch (in_type_sel1) {
312 case 1: in_type[0] = PSE_PAD_TYPE_ANALOGPAD; break;
313 case 2: in_type[0] = PSE_PAD_TYPE_NEGCON; break;
314 default: in_type[0] = PSE_PAD_TYPE_STANDARD;
316 switch (in_type_sel2) {
317 case 1: in_type[1] = PSE_PAD_TYPE_ANALOGPAD; break;
318 case 2: in_type[1] = PSE_PAD_TYPE_NEGCON; break;
319 default: in_type[1] = PSE_PAD_TYPE_STANDARD;
321 if (in_evdev_allow_abs_only != allow_abs_only_old) {
323 allow_abs_only_old = in_evdev_allow_abs_only;
326 spu_config.iVolume = 768 + 128 * volume_boost;
327 pl_rearmed_cbs.frameskip = frameskip - 1;
328 pl_timing_prepare(Config.PsxType);
331 static void menu_set_defconfig(void)
333 emu_set_default_config();
336 g_scaler = SCALE_4_3;
340 analog_deadzone = 50;
345 plat_target.vout_fullscreen = 0;
346 psx_clock = DEFAULT_PSX_CLOCK;
349 in_type_sel1 = in_type_sel2 = 0;
350 in_evdev_allow_abs_only = 0;
355 #define CE_CONFIG_STR(val) \
356 { #val, 0, Config.val }
358 #define CE_CONFIG_VAL(val) \
359 { #val, sizeof(Config.val), &Config.val }
361 #define CE_STR(val) \
364 #define CE_INTVAL(val) \
365 { #val, sizeof(val), &val }
367 #define CE_INTVAL_N(name, val) \
368 { name, sizeof(val), &val }
370 #define CE_INTVAL_P(val) \
371 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
373 // 'versioned' var, used when defaults change
374 #define CE_CONFIG_STR_V(val, ver) \
375 { #val #ver, 0, Config.val }
377 #define CE_INTVAL_V(val, ver) \
378 { #val #ver, sizeof(val), &val }
380 #define CE_INTVAL_PV(val, ver) \
381 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
383 static const struct {
389 CE_CONFIG_STR_V(Gpu, 3),
391 // CE_CONFIG_STR(Cdr),
393 // CE_CONFIG_VAL(Sio),
396 CE_CONFIG_VAL(Debug),
397 CE_CONFIG_VAL(PsxOut),
398 CE_CONFIG_VAL(SpuIrq),
399 CE_CONFIG_VAL(RCntFix),
400 CE_CONFIG_VAL(VSyncWA),
401 CE_CONFIG_VAL(icache_emulation),
402 CE_CONFIG_VAL(DisableStalls),
405 CE_INTVAL_V(g_scaler, 3),
407 CE_INTVAL(g_layer_x),
408 CE_INTVAL(g_layer_y),
409 CE_INTVAL(g_layer_w),
410 CE_INTVAL(g_layer_h),
411 CE_INTVAL(soft_filter),
412 CE_INTVAL(scanlines),
413 CE_INTVAL(scanline_level),
414 CE_INTVAL(plat_target.vout_method),
415 CE_INTVAL(plat_target.hwfilter),
416 CE_INTVAL(plat_target.vout_fullscreen),
417 CE_INTVAL(state_slot),
418 CE_INTVAL(cpu_clock),
420 CE_INTVAL(in_type_sel1),
421 CE_INTVAL(in_type_sel2),
422 CE_INTVAL(analog_deadzone),
423 CE_INTVAL(memcard1_sel),
424 CE_INTVAL(memcard2_sel),
425 CE_INTVAL(g_autostateld_opt),
426 CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
427 CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
428 CE_INTVAL_V(frameskip, 3),
429 CE_INTVAL_P(thread_rendering),
430 CE_INTVAL_P(gpu_peops.iUseDither),
431 CE_INTVAL_P(gpu_peops.dwActFixes),
432 CE_INTVAL_P(gpu_unai.ilace_force),
433 CE_INTVAL_P(gpu_unai.pixel_skip),
434 CE_INTVAL_P(gpu_unai.lighting),
435 CE_INTVAL_P(gpu_unai.fast_lighting),
436 CE_INTVAL_P(gpu_unai.blending),
437 CE_INTVAL_P(gpu_unai.dithering),
438 CE_INTVAL_P(gpu_unai.lineskip),
439 CE_INTVAL_P(gpu_unai.abe_hack),
440 CE_INTVAL_P(gpu_unai.no_light),
441 CE_INTVAL_P(gpu_unai.no_blend),
442 CE_INTVAL_P(gpu_unai.scale_hires),
443 CE_INTVAL_P(gpu_neon.allow_interlace),
444 CE_INTVAL_P(gpu_neon.enhancement_enable),
445 CE_INTVAL_P(gpu_neon.enhancement_no_main),
446 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
447 CE_INTVAL_P(gpu_peopsgl.iFilterType),
448 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
449 CE_INTVAL_P(gpu_peopsgl.iUseMask),
450 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
451 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
452 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
453 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
454 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
455 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
456 CE_INTVAL(spu_config.iUseReverb),
457 CE_INTVAL(spu_config.iXAPitch),
458 CE_INTVAL(spu_config.iUseInterpolation),
459 CE_INTVAL(spu_config.iTempo),
460 CE_INTVAL(spu_config.iUseThread),
461 CE_INTVAL(config_save_counter),
462 CE_INTVAL(in_evdev_allow_abs_only),
463 CE_INTVAL(volume_boost),
464 CE_INTVAL(psx_clock),
465 CE_INTVAL(new_dynarec_hacks),
466 CE_INTVAL(in_enable_vibration),
469 static char *get_cd_label(void)
471 static char trimlabel[33];
474 strncpy(trimlabel, CdromLabel, 32);
476 for (j = 31; j >= 0; j--)
477 if (trimlabel[j] == ' ')
483 static void make_cfg_fname(char *buf, size_t size, int is_game)
486 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
488 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
491 static void keys_write_all(FILE *f);
492 static char *mystrip(char *str);
494 static int menu_write_config(int is_game)
496 char cfgfile[MAXPATHLEN];
500 config_save_counter++;
502 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
503 f = fopen(cfgfile, "w");
505 printf("menu_write_config: failed to open: %s\n", cfgfile);
509 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
510 fprintf(f, "%s = ", config_data[i].name);
511 switch (config_data[i].len) {
513 fprintf(f, "%s\n", (char *)config_data[i].val);
516 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
519 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
522 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
525 printf("menu_write_config: unhandled len %d for %s\n",
526 (int)config_data[i].len, config_data[i].name);
537 static int menu_do_last_cd_img(int is_get)
539 static const char *defaults[] = { "/media", "/mnt/sd", "/mnt" };
545 snprintf(path, sizeof(path), "." PCSX_DOT_DIR "lastcdimg.txt");
546 f = fopen(path, is_get ? "r" : "w");
553 ret = fread(last_selected_fname, 1, sizeof(last_selected_fname) - 1, f);
554 last_selected_fname[ret] = 0;
555 mystrip(last_selected_fname);
558 fprintf(f, "%s\n", last_selected_fname);
563 for (i = 0; last_selected_fname[0] == 0
564 || stat64(last_selected_fname, &st) != 0; i++)
566 if (i >= ARRAY_SIZE(defaults))
568 strcpy(last_selected_fname, defaults[i]);
575 static void parse_str_val(char *cval, const char *src)
578 strncpy(cval, src, MAXPATHLEN);
579 cval[MAXPATHLEN - 1] = 0;
580 tmp = strchr(cval, '\n');
582 tmp = strchr(cval, '\r');
587 static void keys_load_all(const char *cfg);
589 static int menu_load_config(int is_game)
591 char cfgfile[MAXPATHLEN];
597 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
598 f = fopen(cfgfile, "r");
600 printf("menu_load_config: failed to open: %s\n", cfgfile);
604 fseek(f, 0, SEEK_END);
607 printf("bad size %ld: %s\n", size, cfgfile);
611 cfg = malloc(size + 1);
615 fseek(f, 0, SEEK_SET);
616 if (fread(cfg, 1, size, f) != size) {
617 printf("failed to read: %s\n", cfgfile);
622 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
626 tmp = strstr(cfg, config_data[i].name);
629 tmp += strlen(config_data[i].name);
630 if (strncmp(tmp, " = ", 3) != 0)
634 if (config_data[i].len == 0) {
635 parse_str_val(config_data[i].val, tmp);
640 val = strtoul(tmp, &tmp2, 16);
641 if (tmp2 == NULL || tmp == tmp2)
642 continue; // parse failed
644 switch (config_data[i].len) {
646 *(u8 *)config_data[i].val = val;
649 *(u16 *)config_data[i].val = val;
652 *(u32 *)config_data[i].val = val;
655 printf("menu_load_config: unhandled len %d for %s\n",
656 (int)config_data[i].len, config_data[i].name);
662 char *tmp = strstr(cfg, "lastcdimg = ");
665 parse_str_val(last_selected_fname, tmp);
680 for (i = bios_sel = 0; bioses[i] != NULL; i++)
681 if (strcmp(Config.Bios, bioses[i]) == 0)
682 { bios_sel = i; break; }
684 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
685 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
686 { gpu_plugsel = i; break; }
688 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
689 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
690 { spu_plugsel = i; break; }
692 // memcard selections
693 char mcd1_old[sizeof(Config.Mcd1)];
694 char mcd2_old[sizeof(Config.Mcd2)];
695 strcpy(mcd1_old, Config.Mcd1);
696 strcpy(mcd2_old, Config.Mcd2);
698 if ((unsigned int)memcard1_sel < ARRAY_SIZE(memcards)) {
699 if (memcard1_sel == 0)
700 strcpy(Config.Mcd1, "none");
701 else if (memcards[memcard1_sel] != NULL)
702 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s",
703 MEMCARD_DIR, memcards[memcard1_sel]);
705 if ((unsigned int)memcard2_sel < ARRAY_SIZE(memcards)) {
706 if (memcard2_sel == 0)
707 strcpy(Config.Mcd2, "none");
708 else if (memcards[memcard2_sel] != NULL)
709 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s",
710 MEMCARD_DIR, memcards[memcard2_sel]);
712 if (strcmp(mcd1_old, Config.Mcd1) || strcmp(mcd2_old, Config.Mcd2))
713 LoadMcds(Config.Mcd1, Config.Mcd2);
718 static const char *filter_exts[] = {
719 "bin", "img", "mdf", "iso", "cue", "z",
723 "bz", "znx", "pbp", "cbn", NULL
726 // rrrr rggg gggb bbbb
727 static unsigned short fname2color(const char *fname)
729 static const char *other_exts[] = {
730 "ccd", "toc", "mds", "sub", "table", "index", "sbi"
732 const char *ext = strrchr(fname, '.');
738 for (i = 0; filter_exts[i] != NULL; i++)
739 if (strcasecmp(ext, filter_exts[i]) == 0)
741 for (i = 0; i < array_size(other_exts); i++)
742 if (strcasecmp(ext, other_exts[i]) == 0)
747 static void draw_savestate_bg(int slot);
749 #define MENU_ALIGN_LEFT
750 #ifndef HAVE_PRE_ARMV7 // assume hires device
756 #include "libpicofe/menu.c"
758 // a bit of black magic here
759 static void draw_savestate_bg(int slot)
761 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
763 char fname[MAXPATHLEN];
770 ret = get_state_filename(fname, sizeof(fname), slot);
774 f = gzopen(fname, "rb");
778 if ((ret = (int)gzseek(f, 0x29933d, SEEK_SET)) != 0x29933d) {
779 fprintf(stderr, "gzseek failed: %d\n", ret);
784 gpu = malloc(sizeof(*gpu));
790 ret = gzread(f, gpu, sizeof(*gpu));
792 if (ret != sizeof(*gpu)) {
793 fprintf(stderr, "gzread failed\n");
797 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
799 if (gpu->ulStatus & 0x800000)
800 goto out; // disabled
802 x = gpu->ulControl[5] & 0x3ff;
803 y = (gpu->ulControl[5] >> 10) & 0x1ff;
804 w = psx_widths[(gpu->ulStatus >> 16) & 7];
805 tmp = gpu->ulControl[7];
806 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
807 if (gpu->ulStatus & 0x80000) // doubleheight
809 if (h <= 0 || h > 512)
815 s = (u16 *)gpu->psxVRam + y * 1024 + x;
817 x = max(0, g_menuscreen_w - w) & ~3;
818 y = max(0, g_menuscreen_h / 2 - h / 2);
819 w = min(g_menuscreen_w, w);
820 h = min(g_menuscreen_h, h);
821 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
823 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
824 if (gpu->ulStatus & 0x200000)
825 bgr888_to_rgb565(d, s, w * 3);
827 bgr555_to_rgb565(d, s, w * 2);
829 // darken this so that menu text is visible
830 if (g_menuscreen_w - w < 320)
831 menu_darken_bg(d, d, w, 0);
838 // -------------- key config --------------
840 me_bind_action me_ctrl_actions[] =
842 { "UP ", 1 << DKEY_UP},
843 { "DOWN ", 1 << DKEY_DOWN },
844 { "LEFT ", 1 << DKEY_LEFT },
845 { "RIGHT ", 1 << DKEY_RIGHT },
846 { "TRIANGLE", 1 << DKEY_TRIANGLE },
847 { "CIRCLE ", 1 << DKEY_CIRCLE },
848 { "CROSS ", 1 << DKEY_CROSS },
849 { "SQUARE ", 1 << DKEY_SQUARE },
850 { "L1 ", 1 << DKEY_L1 },
851 { "R1 ", 1 << DKEY_R1 },
852 { "L2 ", 1 << DKEY_L2 },
853 { "R2 ", 1 << DKEY_R2 },
854 { "L3 ", 1 << DKEY_L3 },
855 { "R3 ", 1 << DKEY_R3 },
856 { "START ", 1 << DKEY_START },
857 { "SELECT ", 1 << DKEY_SELECT },
861 me_bind_action emuctrl_actions[] =
863 { "Save State ", 1 << SACTION_SAVE_STATE },
864 { "Load State ", 1 << SACTION_LOAD_STATE },
865 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
866 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
867 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
868 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
869 { "Show/Hide FPS ", 1 << SACTION_TOGGLE_FPS },
870 #ifndef HAVE_PRE_ARMV7
871 { "Switch Renderer ", 1 << SACTION_SWITCH_DISPMODE },
873 { "Fast Forward ", 1 << SACTION_FAST_FORWARD },
874 #if MENU_SHOW_MINIMIZE
875 { "Minimize ", 1 << SACTION_MINIMIZE },
877 #if MENU_SHOW_FULLSCREEN
878 { "Toggle fullscreen", 1 << SACTION_TOGGLE_FULLSCREEN },
880 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
881 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
882 { "Gun A button ", 1 << SACTION_GUN_A },
883 { "Gun B button ", 1 << SACTION_GUN_B },
884 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
886 { "Volume Up ", 1 << SACTION_VOLUME_UP },
887 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
892 static char *mystrip(char *str)
897 for (i = 0; i < len; i++)
898 if (str[i] != ' ') break;
899 if (i > 0) memmove(str, str + i, len - i + 1);
902 for (i = len - 1; i >= 0; i--)
903 if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
909 static void get_line(char *d, size_t size, const char *s)
914 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
923 static void keys_write_all(FILE *f)
927 for (d = 0; d < IN_MAX_DEVS; d++)
929 const int *binds = in_get_dev_binds(d);
930 const char *name = in_get_dev_name(d, 0, 0);
933 if (binds == NULL || name == NULL)
936 fprintf(f, "binddev = %s\n", name);
937 in_get_config(d, IN_CFG_BIND_COUNT, &count);
939 for (k = 0; k < count; k++)
944 act[0] = act[31] = 0;
945 name = in_get_key_name(d, k);
947 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
948 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
949 mask = me_ctrl_actions[i].mask;
951 strncpy(act, me_ctrl_actions[i].name, 31);
952 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
955 mask = me_ctrl_actions[i].mask << 16;
957 strncpy(act, me_ctrl_actions[i].name, 31);
958 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
963 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
964 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
965 mask = emuctrl_actions[i].mask;
967 strncpy(act, emuctrl_actions[i].name, 31);
968 fprintf(f, "bind %s = %s\n", name, mystrip(act));
974 for (k = 0; k < array_size(in_adev); k++)
977 fprintf(f, "bind_analog = %d\n", k);
982 static int parse_bind_val(const char *val, int *type)
986 *type = IN_BINDTYPE_NONE;
990 if (strncasecmp(val, "player", 6) == 0)
992 int player, shift = 0;
993 player = atoi(val + 6) - 1;
995 if ((unsigned int)player > 1)
1000 *type = IN_BINDTYPE_PLAYER12;
1001 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
1002 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
1003 return me_ctrl_actions[i].mask << shift;
1006 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
1007 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
1008 *type = IN_BINDTYPE_EMU;
1009 return emuctrl_actions[i].mask;
1016 static void keys_load_all(const char *cfg)
1018 char dev[256], key[128], *act;
1024 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
1027 // don't strip 'dev' because there are weird devices
1028 // with names with space at the end
1029 get_line(dev, sizeof(dev), p);
1031 dev_id = in_config_parse_dev(dev);
1033 printf("input: can't handle dev: %s\n", dev);
1037 in_unbind_all(dev_id, -1, -1);
1038 while ((p = strstr(p, "bind"))) {
1039 if (strncmp(p, "binddev = ", 10) == 0)
1042 if (strncmp(p, "bind_analog", 11) == 0) {
1043 ret = sscanf(p, "bind_analog = %d", &bind);
1046 printf("input: parse error: %16s..\n", p);
1049 if ((unsigned int)bind >= array_size(in_adev)) {
1050 printf("input: analog id %d out of range\n", bind);
1053 in_adev[bind] = dev_id;
1059 printf("input: parse error: %16s..\n", p);
1063 get_line(key, sizeof(key), p);
1064 act = strchr(key, '=');
1066 printf("parse failed: %16s..\n", p);
1074 bind = parse_bind_val(act, &bindtype);
1075 if (bind != -1 && bind != 0) {
1076 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
1077 in_config_bind_key(dev_id, key, bind, bindtype);
1080 lprintf("config: unhandled action \"%s\"\n", act);
1086 static int key_config_loop_wrap(int id, int keys)
1089 case MA_CTRL_PLAYER1:
1090 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
1092 case MA_CTRL_PLAYER2:
1093 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
1096 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
1104 static const char h_nubmode[] = "Maps nub-like analog controls to PSX ones better\n"
1105 "Might cause problems with real analog sticks";
1106 static const char *adevnames[IN_MAX_DEVS + 2];
1107 static int stick_sel[2];
1109 static menu_entry e_menu_keyconfig_analog[] =
1111 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
1112 mee_range (" X axis", 0, in_adev_axis[0][0], 0, 7),
1113 mee_range (" Y axis", 0, in_adev_axis[0][1], 0, 7),
1114 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[0], 1, h_nubmode),
1115 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
1116 mee_range (" X axis", 0, in_adev_axis[1][0], 0, 7),
1117 mee_range (" Y axis", 0, in_adev_axis[1][1], 0, 7),
1118 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[1], 1, h_nubmode),
1122 static int key_config_analog(int id, int keys)
1124 int i, d, count, sel = 0;
1125 int sel2dev_map[IN_MAX_DEVS];
1127 memset(adevnames, 0, sizeof(adevnames));
1128 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1129 memset(stick_sel, 0, sizeof(stick_sel));
1131 adevnames[0] = "None";
1133 for (d = 0; d < IN_MAX_DEVS; d++)
1135 const char *name = in_get_dev_name(d, 0, 1);
1140 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1144 if (in_adev[0] == d) stick_sel[0] = i;
1145 if (in_adev[1] == d) stick_sel[1] = i;
1147 adevnames[i++] = name;
1149 adevnames[i] = NULL;
1151 me_loop(e_menu_keyconfig_analog, &sel);
1153 in_adev[0] = sel2dev_map[stick_sel[0]];
1154 in_adev[1] = sel2dev_map[stick_sel[1]];
1159 static const char *mgn_dev_name(int id, int *offs)
1161 const char *name = NULL;
1164 if (id == MA_CTRL_DEV_FIRST)
1167 for (; it < IN_MAX_DEVS; it++) {
1168 name = in_get_dev_name(it, 1, 1);
1177 static const char *mgn_saveloadcfg(int id, int *offs)
1182 static int mh_savecfg(int id, int keys)
1184 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1185 menu_update_msg("config saved");
1187 menu_update_msg("failed to write config");
1192 static int mh_input_rescan(int id, int keys)
1194 //menu_sync_config();
1196 menu_update_msg("rescan complete.");
1201 static const char *men_in_type_sel[] = {
1202 "Standard (SCPH-1080)",
1203 "Analog (SCPH-1150)",
1207 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1208 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1209 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1211 static menu_entry e_menu_keyconfig[] =
1213 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1214 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1215 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
1216 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
1218 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1219 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
1220 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
1221 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
1222 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
1223 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1224 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1225 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1226 mee_handler ("Rescan devices:", mh_input_rescan),
1228 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
1229 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1230 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1231 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1232 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1233 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1234 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1238 static int menu_loop_keyconfig(int id, int keys)
1242 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1243 me_loop(e_menu_keyconfig, &sel);
1247 // ------------ gfx options menu ------------
1249 static const char *men_scaler[] = {
1250 "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL
1252 static const char *men_soft_filter[] = { "None",
1254 "scale2x", "eagle2x",
1257 static const char *men_dummy[] = { NULL };
1258 static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
1259 "int. 4:3 - uses integer if possible, else fractional";
1260 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1261 "using d-pad or move it using R+d-pad";
1262 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1263 static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
1265 static const char h_scanline_l[] = "Scanline brightness, 0-100%";
1268 static int menu_loop_cscaler(int id, int keys)
1272 g_scaler = SCALE_CUSTOM;
1274 plat_gvideo_open(Config.PsxType);
1278 menu_draw_begin(0, 1);
1279 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1280 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1281 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1284 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1285 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1286 if (inp & PBTN_UP) g_layer_y--;
1287 if (inp & PBTN_DOWN) g_layer_y++;
1288 if (inp & PBTN_LEFT) g_layer_x--;
1289 if (inp & PBTN_RIGHT) g_layer_x++;
1290 if (!(inp & PBTN_R)) {
1291 if (inp & PBTN_UP) g_layer_h += 2;
1292 if (inp & PBTN_DOWN) g_layer_h -= 2;
1293 if (inp & PBTN_LEFT) g_layer_w += 2;
1294 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1296 if (inp & (PBTN_MOK|PBTN_MBACK))
1299 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1300 if (g_layer_x < 0) g_layer_x = 0;
1301 if (g_layer_x > 640) g_layer_x = 640;
1302 if (g_layer_y < 0) g_layer_y = 0;
1303 if (g_layer_y > 420) g_layer_y = 420;
1304 if (g_layer_w < 160) g_layer_w = 160;
1305 if (g_layer_h < 60) g_layer_h = 60;
1306 if (g_layer_x + g_layer_w > 800)
1307 g_layer_w = 800 - g_layer_x;
1308 if (g_layer_y + g_layer_h > 480)
1309 g_layer_h = 480 - g_layer_y;
1311 plat_gvideo_open(Config.PsxType);
1315 plat_gvideo_close();
1320 static menu_entry e_menu_gfx_options[] =
1322 mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1323 mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1324 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1325 mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1326 mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1328 mee_onoff ("Scanlines", MA_OPT_SCANLINES, scanlines, 1),
1329 mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1331 mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1332 // mee_onoff ("Vsync", 0, vsync, 1),
1333 mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1337 static int menu_loop_gfx_options(int id, int keys)
1341 me_loop(e_menu_gfx_options, &sel);
1346 // ------------ bios/plugins ------------
1348 #ifdef BUILTIN_GPU_NEON
1350 static const char h_gpu_neon[] =
1351 "Configure built-in NEON GPU plugin";
1352 static const char h_gpu_neon_enhanced[] =
1353 "Renders in double resolution at the cost of lower performance\n"
1354 "(not available for high resolution games)";
1355 static const char h_gpu_neon_enhanced_hack[] =
1356 "Speed hack for above option (glitches some games)";
1357 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1359 static menu_entry e_menu_plugin_gpu_neon[] =
1361 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1362 mee_onoff_h ("Enhanced resolution (slow)", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1363 mee_onoff_h ("Enhanced res. speed hack", 0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1367 static int menu_loop_plugin_gpu_neon(int id, int keys)
1370 me_loop(e_menu_plugin_gpu_neon, &sel);
1376 static menu_entry e_menu_plugin_gpu_unai[] =
1378 //mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1379 //mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1380 //mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1381 //mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1382 mee_onoff ("Interlace", 0, pl_rearmed_cbs.gpu_unai.ilace_force, 1),
1383 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_unai.dithering, 1),
1384 mee_onoff ("Lighting", 0, pl_rearmed_cbs.gpu_unai.lighting, 1),
1385 mee_onoff ("Fast lighting", 0, pl_rearmed_cbs.gpu_unai.fast_lighting, 1),
1386 mee_onoff ("Blending", 0, pl_rearmed_cbs.gpu_unai.blending, 1),
1387 mee_onoff ("Pixel skip", 0, pl_rearmed_cbs.gpu_unai.pixel_skip, 1),
1391 static int menu_loop_plugin_gpu_unai(int id, int keys)
1394 me_loop(e_menu_plugin_gpu_unai, &sel);
1398 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1399 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1400 static const char h_gpu_1[] = "Capcom fighting games";
1401 static const char h_gpu_2[] = "Black screens in Lunar";
1402 static const char h_gpu_3[] = "Compatibility mode";
1403 static const char h_gpu_6[] = "Pandemonium 2";
1404 //static const char h_gpu_7[] = "Skip every second frame";
1405 static const char h_gpu_8[] = "Needed by Dark Forces";
1406 static const char h_gpu_9[] = "better g-colors, worse textures";
1407 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1409 static menu_entry e_menu_plugin_gpu_peops[] =
1411 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1412 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1413 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1414 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1415 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1416 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1417 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1418 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1419 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1420 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1424 static int menu_loop_plugin_gpu_peops(int id, int keys)
1427 me_loop(e_menu_plugin_gpu_peops, &sel);
1431 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1432 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1433 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1435 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1437 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1438 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1439 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1440 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1441 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1442 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1443 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1444 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1445 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1446 mee_label ("Fixes/hacks:"),
1447 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1448 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1449 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1450 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1451 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1452 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1453 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1454 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1455 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1456 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1457 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1461 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1464 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1468 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1469 static const char h_spu_volboost[] = "Large values cause distortion";
1470 static const char h_spu_tempo[] = "Slows down audio if emu is too slow\n"
1471 "This is inaccurate and breaks games";
1473 static menu_entry e_menu_plugin_spu[] =
1475 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1476 mee_onoff ("Reverb", 0, spu_config.iUseReverb, 1),
1477 mee_enum ("Interpolation", 0, spu_config.iUseInterpolation, men_spu_interp),
1478 //mee_onoff ("Adjust XA pitch", 0, spu_config.iXAPitch, 1),
1479 mee_onoff_h ("Adjust tempo", 0, spu_config.iTempo, 1, h_spu_tempo),
1483 static int menu_loop_plugin_spu(int id, int keys)
1486 me_loop(e_menu_plugin_spu, &sel);
1490 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1491 "savestates and can't be changed there. Must save\n"
1492 "config and reload the game for change to take effect";
1493 static const char h_plugin_gpu[] =
1494 #ifdef BUILTIN_GPU_NEON
1495 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1497 "gpu_peops is Pete's soft GPU, slow but accurate\n"
1498 "gpu_unai is GPU from PCSX4ALL, fast but glitchy\n"
1499 "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1500 "must save config and reload the game if changed";
1501 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1502 "must save config and reload the game if changed";
1503 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1504 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1505 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team GPU plugin";
1506 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1508 static menu_entry e_menu_plugin_options[] =
1510 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1511 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1512 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1513 #ifdef BUILTIN_GPU_NEON
1514 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1516 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1517 mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1518 mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1519 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1523 static menu_entry e_menu_main2[];
1525 static int menu_loop_plugin_options(int id, int keys)
1528 me_loop(e_menu_plugin_options, &sel);
1530 // sync BIOS/plugins
1531 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1532 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1533 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1534 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1539 // ------------ adv options menu ------------
1542 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1543 "(lower value - less work for the emu, may be faster)";
1544 static const char h_cfg_noch[] = "Disables game-specific compatibility hacks";
1545 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1546 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1547 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1549 static const char h_cfg_stalls[] = "Will cause some games to run too fast";
1551 static menu_entry e_menu_speed_hacks[] =
1554 mee_range_h ("PSX CPU clock, %%", 0, psx_clock, 1, 500, h_cfg_psxclk),
1555 mee_onoff_h ("Disable compat hacks", 0, new_dynarec_hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1556 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1557 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1558 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1560 mee_onoff_h ("Disable CPU/GTE stalls", 0, menu_iopts[0], 1, h_cfg_stalls),
1564 static int menu_loop_speed_hacks(int id, int keys)
1567 menu_iopts[0] = Config.DisableStalls;
1568 me_loop(e_menu_speed_hacks, &sel);
1569 Config.DisableStalls = menu_iopts[0];
1573 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1574 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1575 "(green: normal, red: fmod, blue: noise)";
1576 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1577 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1578 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1579 "(proper .cue/.bin dump is needed otherwise)";
1580 //static const char h_cfg_sio[] = "You should not need this, breaks games";
1581 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1582 static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix\n"
1583 "(timing hack, breaks other games)";
1585 static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1586 "(timing hack, breaks other games)";
1588 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1589 "Might be useful to overcome some dynarec bugs";
1591 static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1592 static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1594 enum { AMO_XA, AMO_CDDA, AMO_SIO, AMO_SPUI, AMO_IC, AMO_RCNT, AMO_WA, AMO_CPU };
1596 static menu_entry e_menu_adv_options[] =
1598 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1599 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1600 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1601 mee_onoff_h ("Disable XA Decoding", 0, menu_iopts[AMO_XA], 1, h_cfg_xa),
1602 mee_onoff_h ("Disable CD Audio", 0, menu_iopts[AMO_CDDA], 1, h_cfg_cdda),
1603 //mee_onoff_h ("SIO IRQ Always Enabled", 0, menu_iopts[AMO_SIO], 1, h_cfg_sio),
1604 mee_onoff_h ("SPU IRQ Always Enabled", 0, menu_iopts[AMO_SPUI], 1, h_cfg_spuirq),
1605 mee_onoff_h ("ICache emulation", 0, menu_iopts[AMO_IC], 1, h_cfg_icache),
1607 mee_onoff_h ("Rootcounter hack", 0, menu_iopts[AMO_RCNT], 1, h_cfg_rcnt1),
1609 mee_onoff_h ("Rootcounter hack 2", 0, menu_iopts[AMO_WA], 1, h_cfg_rcnt2),
1610 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
1611 mee_onoff_h ("Disable dynarec (slow!)",0, menu_iopts[AMO_CPU], 1, h_cfg_nodrc),
1613 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1617 static int menu_loop_adv_options(int id, int keys)
1624 { &Config.Xa, &menu_iopts[AMO_XA] },
1625 { &Config.Cdda, &menu_iopts[AMO_CDDA] },
1626 { &Config.Sio, &menu_iopts[AMO_SIO] },
1627 { &Config.SpuIrq, &menu_iopts[AMO_SPUI] },
1628 { &Config.icache_emulation, &menu_iopts[AMO_IC] },
1629 { &Config.RCntFix, &menu_iopts[AMO_RCNT] },
1630 { &Config.VSyncWA, &menu_iopts[AMO_WA] },
1631 { &Config.Cpu, &menu_iopts[AMO_CPU] },
1634 for (i = 0; i < ARRAY_SIZE(opts); i++)
1635 *opts[i].mopt = *opts[i].opt;
1636 me_loop(e_menu_adv_options, &sel);
1637 for (i = 0; i < ARRAY_SIZE(opts); i++)
1638 *opts[i].opt = *opts[i].mopt;
1642 // ------------ options menu ------------
1644 static int mh_restore_defaults(int id, int keys)
1646 menu_set_defconfig();
1647 menu_update_msg("defaults restored");
1651 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1652 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1654 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1655 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1656 "loading state or both";
1658 static const char h_restore_def[] = "Switches back to default / recommended\n"
1660 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1662 static menu_entry e_menu_options[] =
1664 // mee_range ("Save slot", 0, state_slot, 0, 9),
1665 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1666 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1667 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1668 mee_enum ("Region", 0, region, men_region),
1669 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1671 mee_onoff ("Use C64x DSP for sound", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1673 mee_onoff ("Threaded SPU", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1675 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1676 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1677 mee_handler ("[Advanced]", menu_loop_adv_options),
1678 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1679 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1680 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1684 static int menu_loop_options(int id, int keys)
1688 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1689 me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1690 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1692 me_loop(e_menu_options, &sel);
1697 // ------------ debug menu ------------
1699 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1701 int w = min(g_menuscreen_w, 1024);
1702 int h = min(g_menuscreen_h, 512);
1703 u16 *d = g_menuscreen_ptr;
1704 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1708 gpuf->ulFreezeVersion = 1;
1709 if (GPU_freeze != NULL)
1710 GPU_freeze(1, gpuf);
1712 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1713 bgr555_to_rgb565(d, s, w * 2);
1715 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1716 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1717 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1718 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1719 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1722 static void debug_menu_loop(void)
1724 int inp, df_x = 0, df_y = 0;
1727 gpuf = malloc(sizeof(*gpuf));
1733 menu_draw_begin(0, 1);
1734 draw_frame_debug(gpuf, df_x, df_y);
1737 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1738 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1739 if (inp & PBTN_MBACK) break;
1740 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1741 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1742 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x -= 2; }
1743 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1749 // --------- memcard manager ---------
1751 static void draw_mc_icon(int dx, int dy, const u16 *s)
1756 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1758 for (y = 0; y < 16; y++, s += 16) {
1759 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1760 for (x = 0; x < 16; x++) {
1762 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1763 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1769 static void draw_mc_bg(void)
1771 McdBlock *blocks1, *blocks2;
1775 blocks1 = malloc(15 * sizeof(blocks1[0]));
1776 blocks2 = malloc(15 * sizeof(blocks1[0]));
1777 if (blocks1 == NULL || blocks2 == NULL)
1780 for (i = 0; i < 15; i++) {
1781 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1782 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1785 menu_draw_begin(1, 1);
1787 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1789 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1793 maxicons = g_menuscreen_h / 32;
1796 row2 = g_menuscreen_w / 2;
1797 for (i = 0; i < maxicons; i++) {
1798 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1799 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1801 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1802 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1805 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1813 static void handle_memcard_sel(void)
1815 strcpy(Config.Mcd1, "none");
1816 if (memcard1_sel != 0)
1817 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1818 strcpy(Config.Mcd2, "none");
1819 if (memcard2_sel != 0)
1820 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1821 LoadMcds(Config.Mcd1, Config.Mcd2);
1825 static menu_entry e_memcard_options[] =
1827 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1828 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1832 static int menu_loop_memcards(int id, int keys)
1838 memcard1_sel = memcard2_sel = 0;
1839 p = strrchr(Config.Mcd1, '/');
1841 for (i = 0; memcards[i] != NULL; i++)
1842 if (strcmp(p + 1, memcards[i]) == 0)
1843 { memcard1_sel = i; break; }
1844 p = strrchr(Config.Mcd2, '/');
1846 for (i = 0; memcards[i] != NULL; i++)
1847 if (strcmp(p + 1, memcards[i]) == 0)
1848 { memcard2_sel = i; break; }
1850 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1852 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1857 // ------------ cheats menu ------------
1859 static void draw_cheatlist(int sel)
1861 int max_cnt, start, i, pos, active;
1863 max_cnt = g_menuscreen_h / me_sfont_h;
1864 start = max_cnt / 2 - sel;
1866 menu_draw_begin(1, 1);
1868 for (i = 0; i < NumCheats; i++) {
1870 if (pos < 0) continue;
1871 if (pos >= max_cnt) break;
1872 active = Cheats[i].Enabled;
1873 smalltext_out16(14, pos * me_sfont_h,
1874 active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1875 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1876 Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1880 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1882 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1886 static void menu_loop_cheats(void)
1888 static int menu_sel = 0;
1893 draw_cheatlist(menu_sel);
1894 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1895 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1896 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1897 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1898 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1899 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1900 if (inp & PBTN_MOK) { // action
1901 if (menu_sel < NumCheats)
1902 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1905 if (inp & PBTN_MBACK)
1910 // --------- main menu help ----------
1912 static void menu_bios_warn(void)
1915 static const char msg[] =
1916 "You don't seem to have copied any BIOS\n"
1918 MENU_BIOS_PATH "\n\n"
1920 "While many games work fine with fake\n"
1921 "(HLE) BIOS, others (like MGS and FF8)\n"
1922 "require BIOS to work.\n"
1923 "After copying the file, you'll also need\n"
1924 "to select it in the emu's menu:\n"
1925 "options->[BIOS/Plugins]\n\n"
1926 "The file is usually named SCPH1001.BIN,\n"
1927 "but other not compressed files can be\n"
1929 "Press %s or %s to continue";
1930 char tmp_msg[sizeof(msg) + 64];
1932 snprintf(tmp_msg, sizeof(tmp_msg), msg,
1933 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1936 draw_menu_message(tmp_msg, NULL);
1938 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
1939 if (inp & (PBTN_MBACK|PBTN_MOK))
1944 // ------------ main menu ------------
1946 static menu_entry e_menu_main[];
1948 static void draw_frame_main(void)
1957 if (CdromId[0] != 0) {
1958 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1959 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1960 Config.HLE ? "HLE" : "BIOS");
1961 smalltext_out16(4, 1, buff, 0x105f);
1965 capacity = plat_target_bat_capacity_get();
1967 tmp = localtime(<ime);
1968 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1969 if (capacity >= 0) {
1970 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1975 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1979 static void draw_frame_credits(void)
1981 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1984 static const char credits_text[] =
1986 "(C) 1999-2003 PCSX Team\n"
1987 "(C) 2005-2009 PCSX-df Team\n"
1988 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1989 "ARM recompiler (C) 2009-2011 Ari64\n"
1990 #ifdef BUILTIN_GPU_NEON
1991 "ARM NEON GPU (c) 2011-2012 Exophase\n"
1993 "PEOpS GPU and SPU by Pete Bernert\n"
1994 " and the P.E.Op.S. team\n"
1995 "PCSX4ALL plugin by PCSX4ALL team\n"
1996 " Chui, Franxis, Unai\n\n"
1997 "integration, optimization and\n"
1998 " frontend (C) 2010-2015 notaz\n";
2000 static int reset_game(void)
2003 if (bios_sel == 0 && !Config.HLE)
2009 if (CheckCdrom() != -1) {
2015 static int reload_plugins(const char *cdimg)
2021 set_cd_image(cdimg);
2023 pcnt_hook_plugins();
2025 if (OpenPlugins() == -1) {
2026 menu_update_msg("failed to open plugins");
2029 plugin_call_rearmed_cbs();
2031 cdrIsoMultidiskCount = 1;
2033 CdromLabel[0] = '\0';
2038 static int run_bios(void)
2044 if (reload_plugins(NULL) != 0)
2052 static int run_exe(void)
2054 const char *exts[] = { "exe", NULL };
2057 fname = menu_loop_romsel(last_selected_fname,
2058 sizeof(last_selected_fname), exts, NULL);
2063 if (reload_plugins(NULL) != 0)
2067 if (Load(fname) != 0) {
2068 menu_update_msg("exe load failed, bad file?");
2077 static int run_cd_image(const char *fname)
2079 int autoload_state = g_autostateld_opt;
2082 reload_plugins(fname);
2084 // always autodetect, menu_sync_config will override as needed
2087 if (CheckCdrom() == -1) {
2088 // Only check the CD if we are starting the console with a CD
2090 menu_update_msg("unsupported/invalid CD image");
2096 // Read main executable directly from CDRom and start it
2097 if (LoadCdrom() == -1) {
2099 menu_update_msg("failed to load CD image");
2106 if (autoload_state) {
2107 unsigned int newest = 0;
2108 int time, slot, newest_slot = -1;
2110 for (slot = 0; slot < 10; slot++) {
2111 if (emu_check_save_file(slot, &time)) {
2112 if ((unsigned int)time > newest) {
2119 if (newest_slot >= 0) {
2120 lprintf("autoload slot %d\n", newest_slot);
2121 emu_load_state(newest_slot);
2124 lprintf("no save to autoload.\n");
2131 static int romsel_run(void)
2133 int prev_gpu, prev_spu;
2136 fname = menu_loop_romsel(last_selected_fname,
2137 sizeof(last_selected_fname), filter_exts,
2138 optional_cdimg_filter);
2142 printf("selected file: %s\n", fname);
2144 new_dynarec_clear_full();
2146 if (run_cd_image(fname) != 0)
2149 prev_gpu = gpu_plugsel;
2150 prev_spu = spu_plugsel;
2151 if (menu_load_config(1) != 0)
2152 menu_load_config(0);
2154 // check for plugin changes, have to repeat
2155 // loading if game config changed plugins to reload them
2156 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2157 printf("plugin change detected, reloading plugins..\n");
2158 if (run_cd_image(fname) != 0)
2162 strcpy(last_selected_fname, fname);
2163 menu_do_last_cd_img(0);
2167 static int swap_cd_image(void)
2171 fname = menu_loop_romsel(last_selected_fname,
2172 sizeof(last_selected_fname), filter_exts,
2173 optional_cdimg_filter);
2177 printf("selected file: %s\n", fname);
2180 CdromLabel[0] = '\0';
2182 set_cd_image(fname);
2183 if (ReloadCdromPlugin() < 0) {
2184 menu_update_msg("failed to load cdr plugin");
2187 if (CDR_open() < 0) {
2188 menu_update_msg("failed to open cdr plugin");
2192 SetCdOpenCaseTime(time(NULL) + 2);
2195 strcpy(last_selected_fname, fname);
2199 static int swap_cd_multidisk(void)
2201 cdrIsoMultidiskSelect++;
2203 CdromLabel[0] = '\0';
2206 if (CDR_open() < 0) {
2207 menu_update_msg("failed to open cdr plugin");
2211 SetCdOpenCaseTime(time(NULL) + 2);
2217 static void load_pcsx_cht(void)
2219 static const char *exts[] = { "cht", NULL };
2223 fname = menu_loop_romsel(last_selected_fname,
2224 sizeof(last_selected_fname), exts, NULL);
2228 printf("selected cheat file: %s\n", fname);
2231 if (NumCheats == 0 && NumCodes == 0)
2232 menu_update_msg("failed to load cheats");
2234 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2235 menu_update_msg(msg);
2237 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2240 static int main_menu_handler(int id, int keys)
2244 case MA_MAIN_RESUME_GAME:
2248 case MA_MAIN_SAVE_STATE:
2250 return menu_loop_savestate(0);
2252 case MA_MAIN_LOAD_STATE:
2254 return menu_loop_savestate(1);
2256 case MA_MAIN_RESET_GAME:
2257 if (ready_to_go && reset_game() == 0)
2260 case MA_MAIN_LOAD_ROM:
2261 if (romsel_run() == 0)
2264 case MA_MAIN_SWAP_CD:
2265 if (swap_cd_image() == 0)
2268 case MA_MAIN_SWAP_CD_MULTI:
2269 if (swap_cd_multidisk() == 0)
2272 case MA_MAIN_RUN_BIOS:
2273 if (run_bios() == 0)
2276 case MA_MAIN_RUN_EXE:
2280 case MA_MAIN_CHEATS:
2283 case MA_MAIN_LOAD_CHEATS:
2286 case MA_MAIN_CREDITS:
2287 draw_menu_message(credits_text, draw_frame_credits);
2288 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2291 emu_core_ask_exit();
2294 lprintf("%s: something unknown selected\n", __FUNCTION__);
2301 static menu_entry e_menu_main2[] =
2303 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2304 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2305 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2306 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2307 mee_handler ("Memcard manager", menu_loop_memcards),
2308 mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS, main_menu_handler),
2312 static int main_menu2_handler(int id, int keys)
2316 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2317 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2318 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2319 me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2321 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2324 static const char h_extra[] = "Change CD, manage memcards..\n";
2326 static menu_entry e_menu_main[] =
2330 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2331 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2332 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2333 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2334 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2335 mee_handler ("Options", menu_loop_options),
2336 mee_handler ("Controls", menu_loop_keyconfig),
2337 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
2338 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2339 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2340 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2344 // ----------------------------
2346 static void menu_leave_emu(void);
2348 void menu_loop(void)
2350 static int warned_about_bios = 0;
2355 if (config_save_counter == 0) {
2357 if (bioses[1] != NULL) {
2358 // autoselect BIOS to make user's life easier
2359 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2362 else if (!warned_about_bios) {
2364 warned_about_bios = 1;
2368 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2369 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2370 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2371 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2372 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2374 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2377 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2378 } while (!ready_to_go && !g_emu_want_quit);
2380 /* wait until menu, ok, back is released */
2381 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2384 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2389 static int qsort_strcmp(const void *p1, const void *p2)
2391 char * const *s1 = (char * const *)p1;
2392 char * const *s2 = (char * const *)p2;
2393 return strcasecmp(*s1, *s2);
2396 static void scan_bios_plugins(void)
2398 char fname[MAXPATHLEN];
2400 int bios_i, gpu_i, spu_i, mc_i;
2405 gpu_plugins[0] = "builtin_gpu";
2406 spu_plugins[0] = "builtin_spu";
2407 memcards[0] = "(none)";
2408 bios_i = gpu_i = spu_i = mc_i = 1;
2410 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2411 dir = opendir(fname);
2413 perror("scan_bios_plugins bios opendir");
2428 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2431 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2432 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2433 printf("bad BIOS file: %s\n", ent->d_name);
2437 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2438 bioses[bios_i++] = strdup(ent->d_name);
2442 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2448 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2449 dir = opendir(fname);
2451 perror("scan_bios_plugins plugins opendir");
2465 p = strstr(ent->d_name, ".so");
2469 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2470 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2472 fprintf(stderr, "%s\n", dlerror());
2476 // now what do we have here?
2477 tmp = dlsym(h, "GPUinit");
2480 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2481 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2485 tmp = dlsym(h, "SPUinit");
2488 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2489 spu_plugins[spu_i++] = strdup(ent->d_name);
2493 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2500 dir = opendir("." MEMCARD_DIR);
2502 perror("scan_bios_plugins memcards opendir");
2517 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2520 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2521 if (stat(fname, &st) != 0) {
2522 printf("bad memcard file: %s\n", ent->d_name);
2526 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2527 memcards[mc_i++] = strdup(ent->d_name);
2531 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2535 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2540 void menu_init(void)
2542 char buff[MAXPATHLEN];
2545 cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2547 scan_bios_plugins();
2550 menu_set_defconfig();
2551 menu_load_config(0);
2552 menu_do_last_cd_img(1);
2557 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2558 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2559 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2560 fprintf(stderr, "OOM\n");
2564 emu_make_path(buff, "skin/background.png", sizeof(buff));
2565 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2567 i = plat_target.cpu_clock_set != NULL
2568 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2569 me_enable(e_menu_gfx_options, MA_OPT_CPU_CLOCKS, i);
2571 i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2572 e_menu_gfx_options[i].data = plat_target.vout_methods;
2573 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2574 plat_target.vout_methods != NULL);
2576 i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2577 e_menu_gfx_options[i].data = plat_target.hwfilters;
2578 me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2579 plat_target.hwfilters != NULL);
2581 me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2582 plat_target.gamma_set != NULL);
2584 #ifdef HAVE_PRE_ARMV7
2585 me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2587 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2588 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2589 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2590 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2591 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2592 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2593 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2596 void menu_notify_mode_change(int w, int h, int bpp)
2600 last_vout_bpp = bpp;
2603 static void menu_leave_emu(void)
2605 if (GPU_close != NULL) {
2606 int ret = GPU_close();
2608 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2611 plat_video_menu_enter(ready_to_go);
2613 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2614 if (pl_vout_buf != NULL && ready_to_go) {
2615 int x = max(0, g_menuscreen_w - last_vout_w);
2616 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2617 int w = min(g_menuscreen_w, last_vout_w);
2618 int h = min(g_menuscreen_h, last_vout_h);
2619 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2620 char *s = pl_vout_buf;
2622 if (last_vout_bpp == 16) {
2623 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2624 menu_darken_bg(d, s, w, 0);
2627 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2628 rgb888_to_rgb565(d, s, w * 3);
2629 menu_darken_bg(d, d, w, 0);
2635 cpu_clock = plat_target_cpu_clock_get();
2638 void menu_prepare_emu(void)
2640 R3000Acpu *prev_cpu = psxCpu;
2642 plat_video_menu_leave();
2644 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
2645 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2649 if (psxCpu != prev_cpu) {
2650 prev_cpu->Shutdown();
2652 // note that this does not really reset, just clears drc caches
2656 psxCpu->ApplyConfig();
2658 // core doesn't care about Config.Cdda changes,
2659 // so handle them manually here
2665 plat_target_cpu_clock_set(cpu_clock);
2667 // push config to GPU plugin
2668 plugin_call_rearmed_cbs();
2670 if (GPU_open != NULL) {
2671 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2673 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2679 void menu_update_msg(const char *msg)
2681 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2682 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2684 menu_error_time = plat_get_ticks_ms();
2685 lprintf("msg: %s\n", menu_error_msg);
2688 void menu_finish(void)
2690 if (cpu_clock_st > 0)
2691 plat_target_cpu_clock_set(cpu_clock_st);