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.
22 #include <sys/types.h>
31 #include "plugin_lib.h"
35 #include "libpicofe/plat.h"
36 #include "libpicofe/input.h"
37 #include "libpicofe/linux/in_evdev.h"
38 #include "libpicofe/plat.h"
39 #include "../libpcsxcore/misc.h"
40 #include "../libpcsxcore/cdrom.h"
41 #include "../libpcsxcore/cdriso.h"
42 #include "../libpcsxcore/cheat.h"
43 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
44 #include "../plugins/dfsound/spu_config.h"
45 #include "psemu_plugin_defs.h"
46 #include "arm_features.h"
49 #define REARMED_BIRTHDAY_TIME 1293306830 /* 25 Dec 2010 */
51 #define array_size(x) (sizeof(x) / sizeof(x[0]))
62 MA_MAIN_SWAP_CD_MULTI,
93 MA_OPT_SCANLINE_LEVEL,
97 static int last_vout_w, last_vout_h, last_vout_bpp;
98 static int cpu_clock, cpu_clock_st, volume_boost;
99 static int frameskip = 1; // 0 - auto, 1 - off
100 static char last_selected_fname[MAXPATHLEN];
101 static int config_save_counter, region, in_type_sel1, in_type_sel2;
102 static int psx_clock;
103 static int memcard1_sel = -1, memcard2_sel = -1;
104 extern int g_autostateld_opt;
105 static int menu_iopts[8];
106 int g_opts, g_scaler, g_gamma = 100;
107 int scanlines, scanline_level = 20;
108 int soft_scaling, analog_deadzone; // for Caanoo
111 #ifndef HAVE_PRE_ARMV7
112 #define DEFAULT_PSX_CLOCK (10000 / CYCLE_MULT_DEFAULT)
113 #define DEFAULT_PSX_CLOCK_S "57"
115 #define DEFAULT_PSX_CLOCK 50
116 #define DEFAULT_PSX_CLOCK_S "50"
119 static const char *bioses[32];
120 static const char *gpu_plugins[16];
121 static const char *spu_plugins[16];
122 static const char *memcards[32];
123 static int bios_sel, gpu_plugsel, spu_plugsel;
125 #ifndef UI_FEATURES_H
126 #define MENU_BIOS_PATH "bios/"
127 #define MENU_SHOW_VARSCALER 0
128 #define MENU_SHOW_VOUTMODE 1
129 #define MENU_SHOW_SCALER2 0
130 #define MENU_SHOW_NUBS_BTNS 0
131 #define MENU_SHOW_VIBRATION 0
132 #define MENU_SHOW_DEADZONE 0
133 #define MENU_SHOW_MINIMIZE 0
134 #define MENU_SHOW_FULLSCREEN 1
135 #define MENU_SHOW_VOLUME 0
138 static int min(int x, int y) { return x < y ? x : y; }
139 static int max(int x, int y) { return x > y ? x : y; }
141 void emu_make_path(char *buff, const char *end, int size)
145 end_len = strlen(end);
146 pos = plat_get_root_dir(buff, size);
147 strncpy(buff + pos, end, size - pos);
149 if (pos + end_len > size - 1)
150 printf("Warning: path truncated: %s\n", buff);
153 static int emu_check_save_file(int slot, int *time)
155 char fname[MAXPATHLEN];
159 ret = emu_check_state(slot);
160 if (ret != 0 || time == NULL)
161 return ret == 0 ? 1 : 0;
163 ret = get_state_filename(fname, sizeof(fname), slot);
167 ret = stat(fname, &status);
171 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
172 return 1; // probably bad rtc like on some Caanoos
174 *time = status.st_mtime;
179 static int emu_save_load_game(int load, int unused)
184 ret = emu_load_state(state_slot);
186 // reflect hle/bios mode from savestate
189 else if (bios_sel == 0 && bioses[1] != NULL)
190 // XXX: maybe find the right bios instead
194 ret = emu_save_state(state_slot);
199 static void rm_namelist_entry(struct dirent **namelist,
200 int count, const char *name)
204 for (i = 1; i < count; i++) {
205 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
208 if (strcmp(name, namelist[i]->d_name) == 0) {
216 static int optional_cdimg_filter(struct dirent **namelist, int count,
220 char buf[256], buf2[257];
221 int i, d, ret, good_cue;
228 for (i = 1; i < count; i++) {
229 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
232 ext = strrchr(namelist[i]->d_name, '.');
234 // should not happen but whatever
241 // first find .cue files and remove files they reference
242 if (strcasecmp(ext, "cue") == 0)
244 snprintf(buf, sizeof(buf), "%s/%s", basedir,
245 namelist[i]->d_name);
255 while (fgets(buf, sizeof(buf), f)) {
256 ret = sscanf(buf, " FILE \"%256[^\"]\"", buf2);
258 ret = sscanf(buf, " FILE %256s", buf2);
262 p = strrchr(buf2, '/');
264 p = strrchr(buf2, '\\');
270 snprintf(buf, sizeof(buf), "%s/%s", basedir, p);
271 ret = STAT(buf, &statf);
273 rm_namelist_entry(namelist, count, p);
286 p = strcasestr(namelist[i]->d_name, "track");
288 ret = strtoul(p + 5, NULL, 10);
298 for (i = d = 1; i < count; i++)
299 if (namelist[i] != NULL)
300 namelist[d++] = namelist[i];
305 // propagate menu settings to the emu vars
306 static void menu_sync_config(void)
308 static int allow_abs_only_old;
313 Config.PsxType = region - 1;
315 Config.cycle_multiplier = 10000 / psx_clock;
317 switch (in_type_sel1) {
318 case 1: in_type[0] = PSE_PAD_TYPE_ANALOGPAD; break;
319 case 2: in_type[0] = PSE_PAD_TYPE_GUNCON; break;
320 case 3: in_type[0] = PSE_PAD_TYPE_GUN; break;
321 case 4: in_type[0] = PSE_PAD_TYPE_NONE; break;
322 default: in_type[0] = PSE_PAD_TYPE_STANDARD;
324 switch (in_type_sel2) {
325 case 1: in_type[1] = PSE_PAD_TYPE_ANALOGPAD; break;
326 case 2: in_type[1] = PSE_PAD_TYPE_GUNCON; break;
327 case 3: in_type[1] = PSE_PAD_TYPE_GUN; break;
328 case 4: in_type[1] = PSE_PAD_TYPE_NONE; break;
329 default: in_type[1] = PSE_PAD_TYPE_STANDARD;
331 if (in_evdev_allow_abs_only != allow_abs_only_old) {
333 allow_abs_only_old = in_evdev_allow_abs_only;
336 spu_config.iVolume = 768 + 128 * volume_boost;
337 pl_rearmed_cbs.frameskip = frameskip - 1;
338 pl_timing_prepare(Config.PsxType);
341 static void menu_set_defconfig(void)
343 emu_set_default_config();
346 g_scaler = SCALE_4_3;
349 frameskip = 1; // 1 - off
350 analog_deadzone = 50;
355 plat_target.vout_fullscreen = 0;
356 psx_clock = DEFAULT_PSX_CLOCK;
359 in_type_sel1 = in_type_sel2 = 0;
360 in_evdev_allow_abs_only = 0;
365 #define CE_CONFIG_STR(val) \
366 { #val, 0, Config.val }
368 #define CE_CONFIG_VAL(val) \
369 { #val, sizeof(Config.val), &Config.val }
371 #define CE_STR(val) \
374 #define CE_INTVAL(val) \
375 { #val, sizeof(val), &val }
377 #define CE_INTVAL_N(name, val) \
378 { name, sizeof(val), &val }
380 #define CE_INTVAL_P(val) \
381 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
383 // 'versioned' var, used when defaults change
384 #define CE_CONFIG_STR_V(val, ver) \
385 { #val #ver, 0, Config.val }
387 #define CE_INTVAL_V(val, ver) \
388 { #val #ver, sizeof(val), &val }
390 #define CE_INTVAL_PV(val, ver) \
391 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
393 static const struct {
399 CE_CONFIG_STR_V(Gpu, 3),
401 // CE_CONFIG_STR(Cdr),
405 CE_CONFIG_VAL(Debug),
406 CE_CONFIG_VAL(PsxOut),
407 CE_CONFIG_VAL(icache_emulation),
408 CE_CONFIG_VAL(DisableStalls),
410 CE_CONFIG_VAL(GpuListWalking),
411 CE_CONFIG_VAL(PreciseExceptions),
413 CE_INTVAL_V(g_scaler, 3),
415 CE_INTVAL(g_layer_x),
416 CE_INTVAL(g_layer_y),
417 CE_INTVAL(g_layer_w),
418 CE_INTVAL(g_layer_h),
419 CE_INTVAL(soft_filter),
420 CE_INTVAL(scanlines),
421 CE_INTVAL(scanline_level),
422 CE_INTVAL(plat_target.vout_method),
423 CE_INTVAL(plat_target.hwfilter),
424 CE_INTVAL(plat_target.vout_fullscreen),
425 CE_INTVAL(state_slot),
426 CE_INTVAL(cpu_clock),
428 CE_INTVAL(in_type_sel1),
429 CE_INTVAL(in_type_sel2),
430 CE_INTVAL(analog_deadzone),
431 CE_INTVAL(memcard1_sel),
432 CE_INTVAL(memcard2_sel),
433 CE_INTVAL(g_autostateld_opt),
434 CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
435 CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
436 CE_INTVAL_V(frameskip, 4),
437 CE_INTVAL_P(gpu_peops.iUseDither),
438 CE_INTVAL_P(gpu_peops.dwActFixes),
439 CE_INTVAL_P(gpu_unai_old.lineskip),
440 CE_INTVAL_P(gpu_unai_old.abe_hack),
441 CE_INTVAL_P(gpu_unai_old.no_light),
442 CE_INTVAL_P(gpu_unai_old.no_blend),
443 CE_INTVAL_P(gpu_unai.ilace_force),
444 CE_INTVAL_P(gpu_unai.pixel_skip),
445 CE_INTVAL_P(gpu_unai.lighting),
446 CE_INTVAL_P(gpu_unai.fast_lighting),
447 CE_INTVAL_P(gpu_unai.blending),
448 CE_INTVAL_P(gpu_unai.dithering),
449 CE_INTVAL_P(gpu_unai.scale_hires),
450 CE_INTVAL_P(gpu_neon.allow_interlace),
451 CE_INTVAL_P(gpu_neon.enhancement_enable),
452 CE_INTVAL_P(gpu_neon.enhancement_no_main),
453 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
454 CE_INTVAL_P(gpu_peopsgl.iFilterType),
455 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
456 CE_INTVAL_P(gpu_peopsgl.iUseMask),
457 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
458 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
459 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
460 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
461 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
462 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
463 CE_INTVAL_P(screen_centering_type),
464 CE_INTVAL_P(screen_centering_x),
465 CE_INTVAL_P(screen_centering_y),
466 CE_INTVAL(spu_config.iUseReverb),
467 CE_INTVAL(spu_config.iXAPitch),
468 CE_INTVAL(spu_config.iUseInterpolation),
469 CE_INTVAL(spu_config.iTempo),
470 CE_INTVAL(spu_config.iUseThread),
471 CE_INTVAL(config_save_counter),
472 CE_INTVAL(in_evdev_allow_abs_only),
473 CE_INTVAL(volume_boost),
474 CE_INTVAL(psx_clock),
475 CE_INTVAL(new_dynarec_hacks),
476 CE_INTVAL(in_enable_vibration),
479 static char *get_cd_label(void)
481 static char trimlabel[33];
484 strncpy(trimlabel, CdromLabel, 32);
486 for (j = 31; j >= 0; j--)
487 if (trimlabel[j] == ' ')
493 static void make_cfg_fname(char *buf, size_t size, int is_game)
496 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
498 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
501 static void keys_write_all(FILE *f);
502 static char *mystrip(char *str);
504 static void write_u32_value(FILE *f, u32 v)
508 fprintf(f, "%x\n", v);
511 static int menu_write_config(int is_game)
513 char cfgfile[MAXPATHLEN];
517 config_save_counter++;
519 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
520 f = fopen(cfgfile, "w");
522 printf("menu_write_config: failed to open: %s\n", cfgfile);
526 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
527 fprintf(f, "%s = ", config_data[i].name);
528 switch (config_data[i].len) {
530 fprintf(f, "%s\n", (char *)config_data[i].val);
533 write_u32_value(f, *(u8 *)config_data[i].val);
536 write_u32_value(f, *(u16 *)config_data[i].val);
539 write_u32_value(f, *(u32 *)config_data[i].val);
542 printf("menu_write_config: unhandled len %d for %s\n",
543 (int)config_data[i].len, config_data[i].name);
554 static int menu_do_last_cd_img(int is_get)
556 static const char *defaults[] = { "/media", "/mnt/sd", "/mnt" };
562 snprintf(path, sizeof(path), "." PCSX_DOT_DIR "lastcdimg.txt");
563 f = fopen(path, is_get ? "r" : "w");
570 ret = fread(last_selected_fname, 1, sizeof(last_selected_fname) - 1, f);
571 last_selected_fname[ret] = 0;
572 mystrip(last_selected_fname);
575 fprintf(f, "%s\n", last_selected_fname);
580 for (i = 0; last_selected_fname[0] == 0
581 || STAT(last_selected_fname, &st) != 0; i++)
583 if (i >= ARRAY_SIZE(defaults))
585 strcpy(last_selected_fname, defaults[i]);
592 static void parse_str_val(char *cval, const char *src)
595 strncpy(cval, src, MAXPATHLEN);
596 cval[MAXPATHLEN - 1] = 0;
597 tmp = strchr(cval, '\n');
599 tmp = strchr(cval, '\r');
604 static void keys_load_all(const char *cfg);
606 int menu_load_config(int is_game)
608 char cfgfile[MAXPATHLEN];
614 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
615 f = fopen(cfgfile, "r");
617 printf("menu_load_config: failed to open: %s\n", cfgfile);
621 fseek(f, 0, SEEK_END);
624 printf("bad size %ld: %s\n", size, cfgfile);
628 cfg = malloc(size + 1);
632 fseek(f, 0, SEEK_SET);
633 if (fread(cfg, 1, size, f) != size) {
634 printf("failed to read: %s\n", cfgfile);
639 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
643 tmp = strstr(cfg, config_data[i].name);
646 tmp += strlen(config_data[i].name);
647 if (strncmp(tmp, " = ", 3) != 0)
651 if (config_data[i].len == 0) {
652 parse_str_val(config_data[i].val, tmp);
657 val = strtoul(tmp, &tmp2, 16);
658 if (tmp2 == NULL || tmp == tmp2)
659 continue; // parse failed
661 switch (config_data[i].len) {
663 *(u8 *)config_data[i].val = val;
666 *(u16 *)config_data[i].val = val;
669 *(u32 *)config_data[i].val = val;
672 printf("menu_load_config: unhandled len %d for %s\n",
673 (int)config_data[i].len, config_data[i].name);
679 char *tmp = strstr(cfg, "lastcdimg = ");
682 parse_str_val(last_selected_fname, tmp);
697 for (i = bios_sel = 0; bioses[i] != NULL; i++)
698 if (strcmp(Config.Bios, bioses[i]) == 0)
699 { bios_sel = i; break; }
701 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
702 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
703 { gpu_plugsel = i; break; }
705 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
706 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
707 { spu_plugsel = i; break; }
709 // memcard selections
710 char mcd1_old[sizeof(Config.Mcd1)];
711 char mcd2_old[sizeof(Config.Mcd2)];
712 strcpy(mcd1_old, Config.Mcd1);
713 strcpy(mcd2_old, Config.Mcd2);
715 if ((unsigned int)memcard1_sel < ARRAY_SIZE(memcards)) {
716 if (memcard1_sel == 0)
717 strcpy(Config.Mcd1, "none");
718 else if (memcards[memcard1_sel] != NULL)
719 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s",
720 MEMCARD_DIR, memcards[memcard1_sel]);
722 if ((unsigned int)memcard2_sel < ARRAY_SIZE(memcards)) {
723 if (memcard2_sel == 0)
724 strcpy(Config.Mcd2, "none");
725 else if (memcards[memcard2_sel] != NULL)
726 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s",
727 MEMCARD_DIR, memcards[memcard2_sel]);
729 if (strcmp(mcd1_old, Config.Mcd1) || strcmp(mcd2_old, Config.Mcd2))
730 LoadMcds(Config.Mcd1, Config.Mcd2);
735 static const char *filter_exts[] = {
736 "bin", "img", "mdf", "iso", "cue", "z",
740 "bz", "znx", "pbp", "cbn", NULL
743 // rrrr rggg gggb bbbb
744 static unsigned short fname2color(const char *fname)
746 static const char *other_exts[] = {
747 "ccd", "toc", "mds", "sub", "table", "index", "sbi"
749 const char *ext = strrchr(fname, '.');
755 for (i = 0; filter_exts[i] != NULL; i++)
756 if (strcasecmp(ext, filter_exts[i]) == 0)
758 for (i = 0; i < array_size(other_exts); i++)
759 if (strcasecmp(ext, other_exts[i]) == 0)
764 static void draw_savestate_bg(int slot);
766 #define MENU_ALIGN_LEFT
767 #ifndef HAVE_PRE_ARMV7 // assume hires device
773 #include "libpicofe/menu.c"
775 // a bit of black magic here
776 static void draw_savestate_bg(int slot)
778 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
780 char fname[MAXPATHLEN];
787 ret = get_state_filename(fname, sizeof(fname), slot);
791 f = gzopen(fname, "rb");
795 if ((ret = (int)gzseek(f, 0x29933d, SEEK_SET)) != 0x29933d) {
796 fprintf(stderr, "gzseek failed: %d\n", ret);
801 gpu = malloc(sizeof(*gpu));
807 ret = gzread(f, gpu, sizeof(*gpu));
809 if (ret != sizeof(*gpu)) {
810 fprintf(stderr, "gzread failed\n");
814 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
816 if (gpu->ulStatus & 0x800000)
817 goto out; // disabled
819 x = gpu->ulControl[5] & 0x3ff;
820 y = (gpu->ulControl[5] >> 10) & 0x1ff;
821 w = psx_widths[(gpu->ulStatus >> 16) & 7];
822 tmp = gpu->ulControl[7];
823 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
824 if (gpu->ulStatus & 0x80000) // doubleheight
826 if (h <= 0 || h > 512)
832 s = (u16 *)gpu->psxVRam + y * 1024 + x;
834 x = max(0, g_menuscreen_w - w) & ~3;
835 y = max(0, g_menuscreen_h / 2 - h / 2);
836 w = min(g_menuscreen_w, w);
837 h = min(g_menuscreen_h, h);
838 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
840 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
841 if (gpu->ulStatus & 0x200000)
842 bgr888_to_rgb565(d, s, w * 3);
844 bgr555_to_rgb565(d, s, w * 2);
846 // darken this so that menu text is visible
847 if (g_menuscreen_w - w < 320)
848 menu_darken_bg(d, d, w, 0);
855 // -------------- key config --------------
857 me_bind_action me_ctrl_actions[] =
859 { "UP ", 1 << DKEY_UP},
860 { "DOWN ", 1 << DKEY_DOWN },
861 { "LEFT ", 1 << DKEY_LEFT },
862 { "RIGHT ", 1 << DKEY_RIGHT },
863 { "TRIANGLE", 1 << DKEY_TRIANGLE },
864 { "CIRCLE ", 1 << DKEY_CIRCLE },
865 { "CROSS ", 1 << DKEY_CROSS },
866 { "SQUARE ", 1 << DKEY_SQUARE },
867 { "L1 ", 1 << DKEY_L1 },
868 { "R1 ", 1 << DKEY_R1 },
869 { "L2 ", 1 << DKEY_L2 },
870 { "R2 ", 1 << DKEY_R2 },
871 { "L3 ", 1 << DKEY_L3 },
872 { "R3 ", 1 << DKEY_R3 },
873 { "START ", 1 << DKEY_START },
874 { "SELECT ", 1 << DKEY_SELECT },
878 me_bind_action emuctrl_actions[] =
880 { "Save State ", 1 << SACTION_SAVE_STATE },
881 { "Load State ", 1 << SACTION_LOAD_STATE },
882 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
883 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
884 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
885 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
886 { "Show/Hide FPS ", 1 << SACTION_TOGGLE_FPS },
887 #ifndef HAVE_PRE_ARMV7
888 { "Switch Renderer ", 1 << SACTION_SWITCH_DISPMODE },
890 { "Fast Forward ", 1 << SACTION_FAST_FORWARD },
891 #if MENU_SHOW_MINIMIZE
892 { "Minimize ", 1 << SACTION_MINIMIZE },
894 #if MENU_SHOW_FULLSCREEN
895 { "Toggle fullscreen", 1 << SACTION_TOGGLE_FULLSCREEN },
897 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
898 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
899 { "Gun A button ", 1 << SACTION_GUN_A },
900 { "Gun B button ", 1 << SACTION_GUN_B },
901 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
903 { "Volume Up ", 1 << SACTION_VOLUME_UP },
904 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
909 static char *mystrip(char *str)
914 for (i = 0; i < len; i++)
915 if (str[i] != ' ') break;
916 if (i > 0) memmove(str, str + i, len - i + 1);
919 for (i = len - 1; i >= 0; i--)
920 if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
926 static void get_line(char *d, size_t size, const char *s)
931 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
940 static void keys_write_all(FILE *f)
944 for (d = 0; d < IN_MAX_DEVS; d++)
946 const int *binds = in_get_dev_binds(d);
947 const char *name = in_get_dev_name(d, 0, 0);
950 if (binds == NULL || name == NULL)
953 fprintf(f, "binddev = %s\n", name);
954 in_get_config(d, IN_CFG_BIND_COUNT, &count);
956 for (k = 0; k < count; k++)
961 act[0] = act[31] = 0;
962 name = in_get_key_name(d, k);
964 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
965 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
966 mask = me_ctrl_actions[i].mask;
968 strncpy(act, me_ctrl_actions[i].name, 31);
969 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
972 mask = me_ctrl_actions[i].mask << 16;
974 strncpy(act, me_ctrl_actions[i].name, 31);
975 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
980 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
981 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
982 mask = emuctrl_actions[i].mask;
984 strncpy(act, emuctrl_actions[i].name, 31);
985 fprintf(f, "bind %s = %s\n", name, mystrip(act));
991 for (k = 0; k < array_size(in_adev); k++)
994 fprintf(f, "bind_analog = %d\n", k);
999 static int parse_bind_val(const char *val, int *type)
1003 *type = IN_BINDTYPE_NONE;
1007 if (strncasecmp(val, "player", 6) == 0)
1009 int player, shift = 0;
1010 player = atoi(val + 6) - 1;
1012 if ((unsigned int)player > 1)
1017 *type = IN_BINDTYPE_PLAYER12;
1018 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
1019 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
1020 return me_ctrl_actions[i].mask << shift;
1023 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
1024 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
1025 *type = IN_BINDTYPE_EMU;
1026 return emuctrl_actions[i].mask;
1033 static void keys_load_all(const char *cfg)
1035 char dev[256], key[128], *act;
1041 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
1044 // don't strip 'dev' because there are weird devices
1045 // with names with space at the end
1046 get_line(dev, sizeof(dev), p);
1048 dev_id = in_config_parse_dev(dev);
1050 printf("input: can't handle dev: %s\n", dev);
1054 in_unbind_all(dev_id, -1, -1);
1055 while ((p = strstr(p, "bind"))) {
1056 if (strncmp(p, "binddev = ", 10) == 0)
1059 if (strncmp(p, "bind_analog", 11) == 0) {
1060 ret = sscanf(p, "bind_analog = %d", &bind);
1063 printf("input: parse error: %16s..\n", p);
1066 if ((unsigned int)bind >= array_size(in_adev)) {
1067 printf("input: analog id %d out of range\n", bind);
1070 in_adev[bind] = dev_id;
1076 printf("input: parse error: %16s..\n", p);
1080 get_line(key, sizeof(key), p);
1081 act = strchr(key, '=');
1083 printf("parse failed: %16s..\n", p);
1091 bind = parse_bind_val(act, &bindtype);
1092 if (bind != -1 && bind != 0) {
1093 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
1094 in_config_bind_key(dev_id, key, bind, bindtype);
1097 lprintf("config: unhandled action \"%s\"\n", act);
1103 static int key_config_loop_wrap(int id, int keys)
1106 case MA_CTRL_PLAYER1:
1107 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
1109 case MA_CTRL_PLAYER2:
1110 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
1113 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
1121 static const char h_nubmode[] = "Maps nub-like analog controls to PSX ones better\n"
1122 "Might cause problems with real analog sticks";
1123 static const char *adevnames[IN_MAX_DEVS + 2];
1124 static int stick_sel[2];
1126 static menu_entry e_menu_keyconfig_analog[] =
1128 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
1129 mee_range (" X axis", 0, in_adev_axis[0][0], 0, 7),
1130 mee_range (" Y axis", 0, in_adev_axis[0][1], 0, 7),
1131 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[0], 1, h_nubmode),
1132 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
1133 mee_range (" X axis", 0, in_adev_axis[1][0], 0, 7),
1134 mee_range (" Y axis", 0, in_adev_axis[1][1], 0, 7),
1135 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[1], 1, h_nubmode),
1139 static int key_config_analog(int id, int keys)
1141 int i, d, count, sel = 0;
1142 int sel2dev_map[IN_MAX_DEVS];
1144 memset(adevnames, 0, sizeof(adevnames));
1145 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1146 memset(stick_sel, 0, sizeof(stick_sel));
1148 adevnames[0] = "None";
1150 for (d = 0; d < IN_MAX_DEVS; d++)
1152 const char *name = in_get_dev_name(d, 0, 1);
1157 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1161 if (in_adev[0] == d) stick_sel[0] = i;
1162 if (in_adev[1] == d) stick_sel[1] = i;
1164 adevnames[i++] = name;
1166 adevnames[i] = NULL;
1168 me_loop(e_menu_keyconfig_analog, &sel);
1170 in_adev[0] = sel2dev_map[stick_sel[0]];
1171 in_adev[1] = sel2dev_map[stick_sel[1]];
1176 static const char *mgn_dev_name(int id, int *offs)
1178 const char *name = NULL;
1181 if (id == MA_CTRL_DEV_FIRST)
1184 for (; it < IN_MAX_DEVS; it++) {
1185 name = in_get_dev_name(it, 1, 1);
1194 static const char *mgn_saveloadcfg(int id, int *offs)
1199 static int mh_savecfg(int id, int keys)
1201 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1202 menu_update_msg("config saved");
1204 menu_update_msg("failed to write config");
1209 static int mh_input_rescan(int id, int keys)
1211 //menu_sync_config();
1213 menu_update_msg("rescan complete.");
1218 static const char *men_in_type_sel[] = {
1219 "Standard (SCPH-1080)",
1220 "Analog (SCPH-1150)",
1226 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1227 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1228 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1230 static menu_entry e_menu_keyconfig[] =
1232 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1233 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1234 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
1235 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
1237 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1238 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
1239 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
1240 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
1241 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
1242 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1243 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1244 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1245 mee_handler ("Rescan devices:", mh_input_rescan),
1247 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
1248 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1249 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1250 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1251 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1252 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1253 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1257 static int menu_loop_keyconfig(int id, int keys)
1261 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1262 me_loop(e_menu_keyconfig, &sel);
1266 // ------------ gfx options menu ------------
1268 static const char *men_scaler[] = {
1269 "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL
1271 static const char *men_soft_filter[] = { "None",
1273 "scale2x", "eagle2x",
1276 static const char *men_dummy[] = { NULL };
1277 static const char *men_centering[] = { "Auto", "Ingame", "Borderless", "Force", NULL };
1278 static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
1279 "int. 4:3 - uses integer if possible, else fractional";
1280 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1281 "using d-pad or move it using R+d-pad";
1282 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1283 static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
1285 static const char *men_scanlines[] = { "OFF", "1", "2", "3", NULL };
1286 static const char h_scanline_l[] = "Scanline brightness, 0-100%";
1289 static int menu_loop_cscaler(int id, int keys)
1291 void *saved_layer = NULL;
1292 size_t saved_layer_size = 0;
1293 int was_layer_clipped = 0;
1299 g_scaler = SCALE_CUSTOM;
1300 saved_layer_size = last_vout_w * last_vout_h * last_vout_bpp / 8;
1301 saved_layer = malloc(saved_layer_size);
1303 memcpy(saved_layer, pl_vout_buf, saved_layer_size);
1305 plat_gvideo_open(Config.PsxType);
1307 menu_draw_begin(0, 1);
1308 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1313 if (saved_layer && last_vout_bpp == 16) {
1314 int top_x = max(0, -g_layer_x * last_vout_h / 800) + 1;
1315 int top_y = max(0, -g_layer_y * last_vout_h / 480) + 1;
1317 memcpy(pl_vout_buf, saved_layer, saved_layer_size);
1318 snprintf(text, sizeof(text), "%d,%d %dx%d",
1319 g_layer_x, g_layer_y, g_layer_w, g_layer_h);
1320 basic_text_out16_nf(pl_vout_buf, last_vout_w,
1321 top_x, top_y, text);
1322 basic_text_out16_nf(pl_vout_buf, last_vout_w, 2,
1323 last_vout_h - 20, "d-pad: resize, R+d-pad: move");
1324 pl_vout_buf = plat_gvideo_flip();
1327 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1328 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1329 if (inp & PBTN_UP) g_layer_y--;
1330 if (inp & PBTN_DOWN) g_layer_y++;
1331 if (inp & PBTN_LEFT) g_layer_x--;
1332 if (inp & PBTN_RIGHT) g_layer_x++;
1333 if (!(inp & PBTN_R)) {
1334 if (inp & PBTN_UP) g_layer_h += 2;
1335 if (inp & PBTN_DOWN) g_layer_h -= 2;
1336 if (inp & PBTN_LEFT) g_layer_w += 2;
1337 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1339 if (inp & (PBTN_MOK|PBTN_MBACK))
1342 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1343 int layer_clipped = 0;
1344 g_layer_x = max(-320, min(g_layer_x, 640));
1345 g_layer_y = max(-240, min(g_layer_y, 400));
1346 g_layer_w = max(160, g_layer_w);
1347 g_layer_h = max( 60, g_layer_h);
1348 if (g_layer_x < 0 || g_layer_x + g_layer_w > 800)
1350 if (g_layer_w > 800+400)
1351 g_layer_w = 800+400;
1352 if (g_layer_y < 0 || g_layer_y + g_layer_h > 480)
1354 if (g_layer_h > 480+360)
1355 g_layer_h = 480+360;
1357 plat_gvideo_open(Config.PsxType);
1358 if (layer_clipped || was_layer_clipped)
1359 pl_vout_buf = plat_gvideo_set_mode(&last_vout_w,
1360 &last_vout_h, &last_vout_bpp);
1361 was_layer_clipped = layer_clipped;
1365 plat_gvideo_close();
1371 static menu_entry e_menu_gfx_options[] =
1373 mee_enum ("Screen centering", MA_OPT_CENTERING, pl_rearmed_cbs.screen_centering_type, men_centering),
1374 mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1375 mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1376 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1377 mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1378 mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1380 mee_enum ("Scanlines", MA_OPT_SCANLINES, scanlines, men_scanlines),
1381 mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1383 mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1384 // mee_onoff ("Vsync", 0, vsync, 1),
1385 mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1389 static int menu_loop_gfx_options(int id, int keys)
1393 me_loop(e_menu_gfx_options, &sel);
1398 // ------------ bios/plugins ------------
1400 #ifdef BUILTIN_GPU_NEON
1402 static const char h_gpu_neon[] =
1403 "Configure built-in NEON GPU plugin";
1404 static const char h_gpu_neon_enhanced[] =
1405 "Renders in double resolution at the cost of lower performance\n"
1406 "(not available for high resolution games)";
1407 static const char h_gpu_neon_enhanced_hack[] =
1408 "Speed hack for above option (glitches some games)";
1409 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1411 static menu_entry e_menu_plugin_gpu_neon[] =
1413 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1414 mee_onoff_h ("Enhanced resolution", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1415 mee_onoff_h ("Enhanced res. speed hack", 0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1419 static int menu_loop_plugin_gpu_neon(int id, int keys)
1422 me_loop(e_menu_plugin_gpu_neon, &sel);
1428 static menu_entry e_menu_plugin_gpu_unai_old[] =
1430 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai_old.lineskip, 1),
1431 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai_old.abe_hack, 1),
1432 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai_old.no_light, 1),
1433 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai_old.no_blend, 1),
1437 static int menu_loop_plugin_gpu_unai_old(int id, int keys)
1440 me_loop(e_menu_plugin_gpu_unai_old, &sel);
1444 static menu_entry e_menu_plugin_gpu_unai[] =
1446 mee_onoff ("Interlace", 0, pl_rearmed_cbs.gpu_unai.ilace_force, 1),
1447 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_unai.dithering, 1),
1448 mee_onoff ("Lighting", 0, pl_rearmed_cbs.gpu_unai.lighting, 1),
1449 mee_onoff ("Fast lighting", 0, pl_rearmed_cbs.gpu_unai.fast_lighting, 1),
1450 mee_onoff ("Blending", 0, pl_rearmed_cbs.gpu_unai.blending, 1),
1451 mee_onoff ("Pixel skip", 0, pl_rearmed_cbs.gpu_unai.pixel_skip, 1),
1455 static int menu_loop_plugin_gpu_unai(int id, int keys)
1458 me_loop(e_menu_plugin_gpu_unai, &sel);
1463 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1464 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1465 static const char h_gpu_1[] = "Capcom fighting games";
1466 static const char h_gpu_2[] = "Black screens in Lunar";
1467 static const char h_gpu_3[] = "Compatibility mode";
1468 static const char h_gpu_6[] = "Pandemonium 2";
1469 //static const char h_gpu_7[] = "Skip every second frame";
1470 static const char h_gpu_8[] = "Needed by Dark Forces";
1471 static const char h_gpu_9[] = "better g-colors, worse textures";
1472 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1474 static menu_entry e_menu_plugin_gpu_peops[] =
1476 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1477 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1478 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1479 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1480 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1481 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1482 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1483 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1484 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1485 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1489 static int menu_loop_plugin_gpu_peops(int id, int keys)
1492 me_loop(e_menu_plugin_gpu_peops, &sel);
1496 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1497 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1498 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1500 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1502 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1503 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1504 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1505 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1506 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1507 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1508 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1509 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1510 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1511 mee_label ("Fixes/hacks:"),
1512 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1513 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1514 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1515 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1516 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1517 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1518 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1519 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1520 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1521 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1522 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1526 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1529 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1533 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1534 static const char h_spu_volboost[] = "Large values cause distortion";
1535 static const char h_spu_tempo[] = "Slows down audio if emu is too slow\n"
1536 "This is inaccurate and breaks games";
1538 static menu_entry e_menu_plugin_spu[] =
1540 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1541 mee_onoff ("Reverb", 0, spu_config.iUseReverb, 1),
1542 mee_enum ("Interpolation", 0, spu_config.iUseInterpolation, men_spu_interp),
1543 //mee_onoff ("Adjust XA pitch", 0, spu_config.iXAPitch, 1),
1544 mee_onoff_h ("Adjust tempo", 0, spu_config.iTempo, 1, h_spu_tempo),
1548 static int menu_loop_plugin_spu(int id, int keys)
1551 me_loop(e_menu_plugin_spu, &sel);
1555 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1556 "savestates and can't be changed there. Must save\n"
1557 "config and reload the game for change to take effect";
1558 static const char h_plugin_gpu[] =
1559 #ifdef BUILTIN_GPU_NEON
1560 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1562 "gpu_peops is Pete's soft GPU, slow but accurate\n"
1563 "gpu_unai_old is from old PCSX4ALL, fast but glitchy\n"
1564 "gpu_unai is newer, more accurate but slower\n"
1565 "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1566 "must save config and reload the game if changed";
1567 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1568 "must save config and reload the game if changed";
1569 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1570 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1571 static const char h_gpu_unai_old[] = "Configure Unai/PCSX4ALL Team GPU plugin (old)";
1572 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team plugin (new)";
1573 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1575 static menu_entry e_menu_plugin_options[] =
1577 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1578 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1579 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1580 #ifdef BUILTIN_GPU_NEON
1581 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1583 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1584 mee_handler_h ("Configure gpu_unai_old GPU plugin", menu_loop_plugin_gpu_unai_old, h_gpu_unai_old),
1585 mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1586 mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1587 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1591 static menu_entry e_menu_main2[];
1593 static int menu_loop_plugin_options(int id, int keys)
1596 me_loop(e_menu_plugin_options, &sel);
1598 // sync BIOS/plugins
1599 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1600 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1601 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1602 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1607 // ------------ adv options menu ------------
1610 static const char h_cfg_noch[] = "Disables game-specific compatibility hacks";
1611 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1612 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1613 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1615 static const char h_cfg_stalls[] = "Will cause some games to run too fast";
1617 static menu_entry e_menu_speed_hacks[] =
1620 mee_onoff_h ("Disable compat hacks", 0, new_dynarec_hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1621 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1622 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1623 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1625 mee_onoff_h ("Disable CPU/GTE stalls", 0, menu_iopts[0], 1, h_cfg_stalls),
1629 static int menu_loop_speed_hacks(int id, int keys)
1632 menu_iopts[0] = Config.DisableStalls;
1633 me_loop(e_menu_speed_hacks, &sel);
1634 Config.DisableStalls = menu_iopts[0];
1638 static const char *men_gpul[] = { "Auto", "Off", "On", NULL };
1640 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1641 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1642 "(green: normal, red: fmod, blue: noise)";
1643 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1644 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1645 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1646 "(proper .cue/.bin dump is needed otherwise)";
1648 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1649 "Might be useful to overcome some dynarec bugs";
1651 static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1652 static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1653 static const char h_cfg_exc[] = "Emulate some PSX's debug hw like breakpoints\n"
1654 "and exceptions (slow, interpreter only, keep off)";
1655 static const char h_cfg_gpul[] = "Try enabling this if the game misses some graphics\n"
1656 "causes a performance hit";
1657 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1658 "(adjust this if the game is too slow/too fast/hangs)";
1660 enum { AMO_XA, AMO_CDDA, AMO_IC, AMO_BP, AMO_CPU, AMO_GPUL };
1662 static menu_entry e_menu_adv_options[] =
1664 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1665 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1666 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1667 mee_onoff_h ("Disable XA Decoding", 0, menu_iopts[AMO_XA], 1, h_cfg_xa),
1668 mee_onoff_h ("Disable CD Audio", 0, menu_iopts[AMO_CDDA], 1, h_cfg_cdda),
1669 mee_onoff_h ("ICache emulation", 0, menu_iopts[AMO_IC], 1, h_cfg_icache),
1670 mee_onoff_h ("BP exception emulation", 0, menu_iopts[AMO_BP], 1, h_cfg_exc),
1671 mee_enum_h ("GPU l-list slow walking",0, menu_iopts[AMO_GPUL], men_gpul, h_cfg_gpul),
1672 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
1673 mee_onoff_h ("Disable dynarec (slow!)",0, menu_iopts[AMO_CPU], 1, h_cfg_nodrc),
1675 mee_range_h ("PSX CPU clock, %", 0, psx_clock, 1, 500, h_cfg_psxclk),
1676 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1680 static int menu_loop_adv_options(int id, int keys)
1687 { &Config.Xa, &menu_iopts[AMO_XA] },
1688 { &Config.Cdda, &menu_iopts[AMO_CDDA] },
1689 { &Config.icache_emulation, &menu_iopts[AMO_IC] },
1690 { &Config.PreciseExceptions, &menu_iopts[AMO_BP] },
1691 { &Config.Cpu, &menu_iopts[AMO_CPU] },
1694 for (i = 0; i < ARRAY_SIZE(opts); i++)
1695 *opts[i].mopt = *opts[i].opt;
1696 menu_iopts[AMO_GPUL] = Config.GpuListWalking + 1;
1698 me_loop(e_menu_adv_options, &sel);
1700 for (i = 0; i < ARRAY_SIZE(opts); i++)
1701 *opts[i].opt = *opts[i].mopt;
1702 Config.GpuListWalking = menu_iopts[AMO_GPUL] - 1;
1707 // ------------ options menu ------------
1709 static int mh_restore_defaults(int id, int keys)
1711 menu_set_defconfig();
1712 menu_update_msg("defaults restored");
1716 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1717 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1719 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1720 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1721 "loading state or both";
1723 static const char h_restore_def[] = "Switches back to default / recommended\n"
1725 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1726 static const char h_sputhr[] = "Warning: has some known bugs\n";
1728 static menu_entry e_menu_options[] =
1730 // mee_range ("Save slot", 0, state_slot, 0, 9),
1731 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1732 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1733 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1734 mee_enum ("Region", 0, region, men_region),
1735 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1737 mee_onoff_h ("Use C64x DSP for sound", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1739 mee_onoff_h ("Threaded SPU", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1741 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1742 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1743 mee_handler ("[Advanced]", menu_loop_adv_options),
1744 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1745 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1746 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1750 static int menu_loop_options(int id, int keys)
1754 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1755 me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1756 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1758 me_loop(e_menu_options, &sel);
1763 // ------------ debug menu ------------
1765 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1767 int w = min(g_menuscreen_w, 1024);
1768 int h = min(g_menuscreen_h, 512);
1769 u16 *d = g_menuscreen_ptr;
1770 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1774 gpuf->ulFreezeVersion = 1;
1775 if (GPU_freeze != NULL)
1776 GPU_freeze(1, gpuf);
1778 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1779 bgr555_to_rgb565(d, s, w * 2);
1781 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1782 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1783 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1784 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1785 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1788 static void debug_menu_loop(void)
1790 int inp, df_x = 0, df_y = 0;
1793 gpuf = malloc(sizeof(*gpuf));
1799 menu_draw_begin(0, 1);
1800 draw_frame_debug(gpuf, df_x, df_y);
1803 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1804 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1805 if (inp & PBTN_MBACK) break;
1806 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1807 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1808 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x -= 2; }
1809 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1815 // --------- memcard manager ---------
1817 static void draw_mc_icon(int dx, int dy, const u16 *s)
1822 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1824 for (y = 0; y < 16; y++, s += 16) {
1825 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1826 for (x = 0; x < 16; x++) {
1828 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1829 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1835 static void draw_mc_bg(void)
1837 McdBlock *blocks1, *blocks2;
1841 blocks1 = malloc(15 * sizeof(blocks1[0]));
1842 blocks2 = malloc(15 * sizeof(blocks1[0]));
1843 if (blocks1 == NULL || blocks2 == NULL)
1846 for (i = 0; i < 15; i++) {
1847 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1848 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1851 menu_draw_begin(1, 1);
1853 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1855 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1859 maxicons = g_menuscreen_h / 32;
1862 row2 = g_menuscreen_w / 2;
1863 for (i = 0; i < maxicons; i++) {
1864 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1865 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1867 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1868 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1871 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1879 static void handle_memcard_sel(void)
1881 strcpy(Config.Mcd1, "none");
1882 if (memcard1_sel != 0)
1883 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1884 strcpy(Config.Mcd2, "none");
1885 if (memcard2_sel != 0)
1886 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1887 LoadMcds(Config.Mcd1, Config.Mcd2);
1891 static menu_entry e_memcard_options[] =
1893 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1894 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1898 static int menu_loop_memcards(int id, int keys)
1904 memcard1_sel = memcard2_sel = 0;
1905 p = strrchr(Config.Mcd1, '/');
1907 for (i = 0; memcards[i] != NULL; i++)
1908 if (strcmp(p + 1, memcards[i]) == 0)
1909 { memcard1_sel = i; break; }
1910 p = strrchr(Config.Mcd2, '/');
1912 for (i = 0; memcards[i] != NULL; i++)
1913 if (strcmp(p + 1, memcards[i]) == 0)
1914 { memcard2_sel = i; break; }
1916 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1918 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1923 // ------------ cheats menu ------------
1925 static void draw_cheatlist(int sel)
1927 int max_cnt, start, i, pos, active;
1929 max_cnt = g_menuscreen_h / me_sfont_h;
1930 start = max_cnt / 2 - sel;
1932 menu_draw_begin(1, 1);
1934 for (i = 0; i < NumCheats; i++) {
1936 if (pos < 0) continue;
1937 if (pos >= max_cnt) break;
1938 active = Cheats[i].Enabled;
1939 smalltext_out16(14, pos * me_sfont_h,
1940 active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1941 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1942 Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1946 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1948 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1952 static void menu_loop_cheats(void)
1954 static int menu_sel = 0;
1959 draw_cheatlist(menu_sel);
1960 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1961 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1962 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1963 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1964 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1965 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1966 if (inp & PBTN_MOK) { // action
1967 if (menu_sel < NumCheats)
1968 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1971 if (inp & PBTN_MBACK)
1976 // --------- main menu help ----------
1978 static void menu_bios_warn(void)
1981 static const char msg[] =
1982 "You don't seem to have copied any BIOS\n"
1984 MENU_BIOS_PATH "\n\n"
1986 "While many games work fine with fake\n"
1987 "(HLE) BIOS, others (like MGS and FF8)\n"
1988 "require BIOS to work.\n"
1989 "After copying the file, you'll also need\n"
1990 "to select it in the emu's menu:\n"
1991 "options->[BIOS/Plugins]\n\n"
1992 "The file is usually named SCPH1001.BIN,\n"
1993 "but other not compressed files can be\n"
1995 "Press %s or %s to continue";
1996 char tmp_msg[sizeof(msg) + 64];
1998 snprintf(tmp_msg, sizeof(tmp_msg), msg,
1999 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
2002 draw_menu_message(tmp_msg, NULL);
2004 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2005 if (inp & (PBTN_MBACK|PBTN_MOK))
2010 // ------------ main menu ------------
2012 static menu_entry e_menu_main[];
2014 static void draw_frame_main(void)
2023 if (CdromId[0] != 0) {
2024 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
2025 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
2026 Config.HLE ? "HLE" : "BIOS");
2027 smalltext_out16(4, 1, buff, 0x105f);
2031 capacity = plat_target_bat_capacity_get();
2033 tmp = localtime(<ime);
2034 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
2035 if (capacity >= 0) {
2036 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
2041 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
2045 static void draw_frame_credits(void)
2047 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
2050 static const char credits_text[] =
2052 "(C) 1999-2003 PCSX Team\n"
2053 "(C) 2005-2009 PCSX-df Team\n"
2054 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
2055 "ARM recompiler (C) 2009-2011 Ari64\n"
2056 #ifdef BUILTIN_GPU_NEON
2057 "ARM NEON GPU (c) 2011-2012 Exophase\n"
2059 "PEOpS GPU and SPU by Pete Bernert\n"
2060 " and the P.E.Op.S. team\n"
2061 "PCSX4ALL plugin by PCSX4ALL team\n"
2062 " Chui, Franxis, Unai\n\n"
2063 "integration, optimization and\n"
2064 " frontend (C) 2010-2015 notaz\n";
2066 static int reset_game(void)
2072 if (LoadCdrom() == -1)
2078 static int reload_plugins(const char *cdimg)
2084 set_cd_image(cdimg);
2086 pcnt_hook_plugins();
2088 if (OpenPlugins() == -1) {
2089 menu_update_msg("failed to open plugins");
2092 plugin_call_rearmed_cbs();
2094 cdrIsoMultidiskCount = 1;
2096 CdromLabel[0] = '\0';
2101 static int run_bios(void)
2103 boolean origSlowBoot = Config.SlowBoot;
2109 if (reload_plugins(NULL) != 0)
2111 Config.SlowBoot = 1;
2113 Config.SlowBoot = origSlowBoot;
2119 static int run_exe(void)
2121 const char *exts[] = { "exe", NULL };
2124 fname = menu_loop_romsel(last_selected_fname,
2125 sizeof(last_selected_fname), exts, NULL);
2130 if (reload_plugins(NULL) != 0)
2134 if (Load(fname) != 0) {
2135 menu_update_msg("exe load failed, bad file?");
2144 static int run_cd_image(const char *fname)
2146 int autoload_state = g_autostateld_opt;
2149 reload_plugins(fname);
2151 // always autodetect, menu_sync_config will override as needed
2154 if (CheckCdrom() == -1) {
2155 // Only check the CD if we are starting the console with a CD
2157 menu_update_msg("unsupported/invalid CD image");
2163 // Read main executable directly from CDRom and start it
2164 if (LoadCdrom() == -1) {
2166 menu_update_msg("failed to load CD image");
2173 if (autoload_state) {
2174 unsigned int newest = 0;
2175 int time, slot, newest_slot = -1;
2177 for (slot = 0; slot < 10; slot++) {
2178 if (emu_check_save_file(slot, &time)) {
2179 if ((unsigned int)time > newest) {
2186 if (newest_slot >= 0) {
2187 lprintf("autoload slot %d\n", newest_slot);
2188 emu_load_state(newest_slot);
2191 lprintf("no save to autoload.\n");
2198 static int romsel_run(void)
2200 int prev_gpu, prev_spu;
2203 fname = menu_loop_romsel(last_selected_fname,
2204 sizeof(last_selected_fname), filter_exts,
2205 optional_cdimg_filter);
2209 printf("selected file: %s\n", fname);
2211 new_dynarec_clear_full();
2213 if (run_cd_image(fname) != 0)
2216 prev_gpu = gpu_plugsel;
2217 prev_spu = spu_plugsel;
2218 if (menu_load_config(1) != 0)
2219 menu_load_config(0);
2221 // check for plugin changes, have to repeat
2222 // loading if game config changed plugins to reload them
2223 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2224 printf("plugin change detected, reloading plugins..\n");
2225 if (run_cd_image(fname) != 0)
2229 strcpy(last_selected_fname, fname);
2230 menu_do_last_cd_img(0);
2234 static int swap_cd_image(void)
2238 fname = menu_loop_romsel(last_selected_fname,
2239 sizeof(last_selected_fname), filter_exts,
2240 optional_cdimg_filter);
2244 printf("selected file: %s\n", fname);
2247 CdromLabel[0] = '\0';
2249 set_cd_image(fname);
2250 if (ReloadCdromPlugin() < 0) {
2251 menu_update_msg("failed to load cdr plugin");
2254 if (CDR_open() < 0) {
2255 menu_update_msg("failed to open cdr plugin");
2259 SetCdOpenCaseTime(time(NULL) + 2);
2262 strcpy(last_selected_fname, fname);
2266 static int swap_cd_multidisk(void)
2268 cdrIsoMultidiskSelect++;
2270 CdromLabel[0] = '\0';
2273 if (CDR_open() < 0) {
2274 menu_update_msg("failed to open cdr plugin");
2278 SetCdOpenCaseTime(time(NULL) + 2);
2284 static void load_pcsx_cht(void)
2286 static const char *exts[] = { "cht", NULL };
2290 fname = menu_loop_romsel(last_selected_fname,
2291 sizeof(last_selected_fname), exts, NULL);
2295 printf("selected cheat file: %s\n", fname);
2298 if (NumCheats == 0 && NumCodes == 0)
2299 menu_update_msg("failed to load cheats");
2301 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2302 menu_update_msg(msg);
2304 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2307 static int main_menu_handler(int id, int keys)
2311 case MA_MAIN_RESUME_GAME:
2315 case MA_MAIN_SAVE_STATE:
2317 return menu_loop_savestate(0);
2319 case MA_MAIN_LOAD_STATE:
2321 return menu_loop_savestate(1);
2323 case MA_MAIN_RESET_GAME:
2324 if (ready_to_go && reset_game() == 0)
2327 case MA_MAIN_LOAD_ROM:
2328 if (romsel_run() == 0)
2331 case MA_MAIN_SWAP_CD:
2332 if (swap_cd_image() == 0)
2335 case MA_MAIN_SWAP_CD_MULTI:
2336 if (swap_cd_multidisk() == 0)
2339 case MA_MAIN_RUN_BIOS:
2340 if (run_bios() == 0)
2343 case MA_MAIN_RUN_EXE:
2347 case MA_MAIN_CHEATS:
2350 case MA_MAIN_LOAD_CHEATS:
2353 case MA_MAIN_CREDITS:
2354 draw_menu_message(credits_text, draw_frame_credits);
2355 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2358 emu_core_ask_exit();
2361 lprintf("%s: something unknown selected\n", __FUNCTION__);
2368 static menu_entry e_menu_main2[] =
2370 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2371 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2372 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2373 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2374 mee_handler ("Memcard manager", menu_loop_memcards),
2375 mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS, main_menu_handler),
2379 static int main_menu2_handler(int id, int keys)
2383 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2384 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2385 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2386 me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2388 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2391 static const char h_extra[] = "Change CD, manage memcards..\n";
2393 static menu_entry e_menu_main[] =
2397 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2398 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2399 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2400 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2401 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2402 mee_handler ("Options", menu_loop_options),
2403 mee_handler ("Controls", menu_loop_keyconfig),
2404 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
2405 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2406 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2407 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2411 // ----------------------------
2413 static void menu_leave_emu(void);
2415 void menu_loop(void)
2417 static int warned_about_bios = 0;
2422 if (config_save_counter == 0) {
2424 if (bioses[1] != NULL) {
2425 // autoselect BIOS to make user's life easier
2426 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2429 else if (!warned_about_bios) {
2431 warned_about_bios = 1;
2435 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2436 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2437 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2438 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2439 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2441 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2444 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2445 } while (!ready_to_go && !g_emu_want_quit);
2447 /* wait until menu, ok, back is released */
2448 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2451 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2456 static int qsort_strcmp(const void *p1, const void *p2)
2458 char * const *s1 = (char * const *)p1;
2459 char * const *s2 = (char * const *)p2;
2460 return strcasecmp(*s1, *s2);
2463 static void scan_bios_plugins(void)
2465 char fname[MAXPATHLEN];
2467 int bios_i, gpu_i, spu_i, mc_i;
2472 gpu_plugins[0] = "builtin_gpu";
2473 spu_plugins[0] = "builtin_spu";
2474 memcards[0] = "(none)";
2475 bios_i = gpu_i = spu_i = mc_i = 1;
2477 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2478 dir = opendir(fname);
2480 perror("scan_bios_plugins bios opendir");
2495 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2498 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2499 if (stat(fname, &st) != 0
2500 || (st.st_size != 512*1024 && st.st_size != 4*1024*1024)) {
2501 printf("bad BIOS file: %s\n", ent->d_name);
2505 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2506 bioses[bios_i++] = strdup(ent->d_name);
2510 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2516 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2517 dir = opendir(fname);
2519 perror("scan_bios_plugins plugins opendir");
2533 p = strstr(ent->d_name, ".so");
2537 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2538 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2540 fprintf(stderr, "%s\n", dlerror());
2544 // now what do we have here?
2545 tmp = dlsym(h, "GPUinit");
2548 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2549 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2553 tmp = dlsym(h, "SPUinit");
2556 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2557 spu_plugins[spu_i++] = strdup(ent->d_name);
2561 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2568 dir = opendir("." MEMCARD_DIR);
2570 perror("scan_bios_plugins memcards opendir");
2585 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2588 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2589 if (stat(fname, &st) != 0) {
2590 printf("bad memcard file: %s\n", ent->d_name);
2594 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2595 memcards[mc_i++] = strdup(ent->d_name);
2599 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2603 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2608 void menu_init(void)
2610 char buff[MAXPATHLEN];
2613 cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2615 scan_bios_plugins();
2618 menu_set_defconfig();
2619 menu_load_config(0);
2620 menu_do_last_cd_img(1);
2625 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2626 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2627 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2628 fprintf(stderr, "OOM\n");
2632 emu_make_path(buff, "skin/background.png", sizeof(buff));
2633 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2635 i = plat_target.cpu_clock_set != NULL
2636 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2637 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, i);
2639 i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2640 e_menu_gfx_options[i].data = plat_target.vout_methods;
2641 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2642 plat_target.vout_methods != NULL);
2644 i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2645 e_menu_gfx_options[i].data = plat_target.hwfilters;
2646 me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2647 plat_target.hwfilters != NULL);
2649 me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2650 plat_target.gamma_set != NULL);
2652 #ifdef HAVE_PRE_ARMV7
2653 me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2655 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2656 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2657 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2658 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2659 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2660 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2661 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2664 void menu_notify_mode_change(int w, int h, int bpp)
2668 last_vout_bpp = bpp;
2671 static void menu_leave_emu(void)
2673 if (GPU_close != NULL) {
2674 int ret = GPU_close();
2676 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2679 plat_video_menu_enter(ready_to_go);
2681 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2682 if (pl_vout_buf != NULL && ready_to_go) {
2683 int x = max(0, g_menuscreen_w - last_vout_w);
2684 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2685 int w = min(g_menuscreen_w, last_vout_w);
2686 int h = min(g_menuscreen_h, last_vout_h);
2687 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2688 char *s = pl_vout_buf;
2690 if (last_vout_bpp == 16) {
2691 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2692 menu_darken_bg(d, s, w, 0);
2695 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2696 rgb888_to_rgb565(d, s, w * 3);
2697 menu_darken_bg(d, d, w, 0);
2703 cpu_clock = plat_target_cpu_clock_get();
2706 void menu_prepare_emu(void)
2708 R3000Acpu *prev_cpu = psxCpu;
2710 plat_video_menu_leave();
2712 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
2713 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2717 if (psxCpu != prev_cpu) {
2718 prev_cpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
2719 prev_cpu->Shutdown();
2722 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
2726 psxCpu->ApplyConfig();
2728 // core doesn't care about Config.Cdda changes,
2729 // so handle them manually here
2734 plat_target_cpu_clock_set(cpu_clock);
2736 // push config to GPU plugin
2737 plugin_call_rearmed_cbs();
2739 if (GPU_open != NULL) {
2740 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2742 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2746 void menu_update_msg(const char *msg)
2748 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2749 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2751 menu_error_time = plat_get_ticks_ms();
2752 lprintf("msg: %s\n", menu_error_msg);
2755 void menu_finish(void)
2757 if (cpu_clock_st > 0)
2758 plat_target_cpu_clock_set(cpu_clock_st);