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 int g_opts, g_scaler, g_gamma = 100;
100 int scanlines, scanline_level = 20;
101 int soft_scaling, analog_deadzone; // for Caanoo
104 #ifndef HAVE_PRE_ARMV7
105 #define DEFAULT_PSX_CLOCK (10000 / CYCLE_MULT_DEFAULT)
106 #define DEFAULT_PSX_CLOCK_S "57"
108 #define DEFAULT_PSX_CLOCK 50
109 #define DEFAULT_PSX_CLOCK_S "50"
112 static const char *bioses[24];
113 static const char *gpu_plugins[16];
114 static const char *spu_plugins[16];
115 static const char *memcards[32];
116 static int bios_sel, gpu_plugsel, spu_plugsel;
118 #ifndef UI_FEATURES_H
119 #define MENU_BIOS_PATH "bios/"
120 #define MENU_SHOW_VARSCALER 0
121 #define MENU_SHOW_VOUTMODE 1
122 #define MENU_SHOW_SCALER2 0
123 #define MENU_SHOW_NUBS_BTNS 0
124 #define MENU_SHOW_VIBRATION 0
125 #define MENU_SHOW_DEADZONE 0
126 #define MENU_SHOW_MINIMIZE 0
127 #define MENU_SHOW_FULLSCREEN 1
128 #define MENU_SHOW_VOLUME 0
131 static int min(int x, int y) { return x < y ? x : y; }
132 static int max(int x, int y) { return x > y ? x : y; }
134 void emu_make_path(char *buff, const char *end, int size)
138 end_len = strlen(end);
139 pos = plat_get_root_dir(buff, size);
140 strncpy(buff + pos, end, size - pos);
142 if (pos + end_len > size - 1)
143 printf("Warning: path truncated: %s\n", buff);
146 static int emu_check_save_file(int slot, int *time)
148 char fname[MAXPATHLEN];
152 ret = emu_check_state(slot);
153 if (ret != 0 || time == NULL)
154 return ret == 0 ? 1 : 0;
156 ret = get_state_filename(fname, sizeof(fname), slot);
160 ret = stat(fname, &status);
164 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
165 return 1; // probably bad rtc like on some Caanoos
167 *time = status.st_mtime;
172 static int emu_save_load_game(int load, int unused)
177 ret = emu_load_state(state_slot);
179 // reflect hle/bios mode from savestate
182 else if (bios_sel == 0 && bioses[1] != NULL)
183 // XXX: maybe find the right bios instead
187 ret = emu_save_state(state_slot);
192 static void rm_namelist_entry(struct dirent **namelist,
193 int count, const char *name)
197 for (i = 1; i < count; i++) {
198 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
201 if (strcmp(name, namelist[i]->d_name) == 0) {
209 static int optional_cdimg_filter(struct dirent **namelist, int count,
213 char buf[256], buf2[256];
214 int i, d, ret, good_cue;
221 for (i = 1; i < count; i++) {
222 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
225 ext = strrchr(namelist[i]->d_name, '.');
227 // should not happen but whatever
234 // first find .cue files and remove files they reference
235 if (strcasecmp(ext, "cue") == 0)
237 snprintf(buf, sizeof(buf), "%s/%s", basedir,
238 namelist[i]->d_name);
248 while (fgets(buf, sizeof(buf), f)) {
249 ret = sscanf(buf, " FILE \"%256[^\"]\"", buf2);
251 ret = sscanf(buf, " FILE %256s", buf2);
255 p = strrchr(buf2, '/');
257 p = strrchr(buf2, '\\');
263 snprintf(buf, sizeof(buf), "%s/%s", basedir, p);
264 ret = stat64(buf, &statf);
266 rm_namelist_entry(namelist, count, p);
279 p = strcasestr(namelist[i]->d_name, "track");
281 ret = strtoul(p + 5, NULL, 10);
291 for (i = d = 1; i < count; i++)
292 if (namelist[i] != NULL)
293 namelist[d++] = namelist[i];
298 // propagate menu settings to the emu vars
299 static void menu_sync_config(void)
301 static int allow_abs_only_old;
306 Config.PsxType = region - 1;
308 cycle_multiplier = 10000 / psx_clock;
310 switch (in_type_sel1) {
311 case 1: in_type[0] = PSE_PAD_TYPE_ANALOGPAD; break;
312 case 2: in_type[0] = PSE_PAD_TYPE_NEGCON; break;
313 default: in_type[0] = PSE_PAD_TYPE_STANDARD;
315 switch (in_type_sel2) {
316 case 1: in_type[1] = PSE_PAD_TYPE_ANALOGPAD; break;
317 case 2: in_type[1] = PSE_PAD_TYPE_NEGCON; break;
318 default: in_type[1] = PSE_PAD_TYPE_STANDARD;
320 if (in_evdev_allow_abs_only != allow_abs_only_old) {
322 allow_abs_only_old = in_evdev_allow_abs_only;
325 spu_config.iVolume = 768 + 128 * volume_boost;
326 pl_rearmed_cbs.frameskip = frameskip - 1;
327 pl_timing_prepare(Config.PsxType);
330 static void menu_set_defconfig(void)
332 emu_set_default_config();
335 g_scaler = SCALE_4_3;
339 analog_deadzone = 50;
344 plat_target.vout_fullscreen = 0;
345 psx_clock = DEFAULT_PSX_CLOCK;
348 in_type_sel1 = in_type_sel2 = 0;
349 in_evdev_allow_abs_only = 0;
354 #define CE_CONFIG_STR(val) \
355 { #val, 0, Config.val }
357 #define CE_CONFIG_VAL(val) \
358 { #val, sizeof(Config.val), &Config.val }
360 #define CE_STR(val) \
363 #define CE_INTVAL(val) \
364 { #val, sizeof(val), &val }
366 #define CE_INTVAL_N(name, val) \
367 { name, sizeof(val), &val }
369 #define CE_INTVAL_P(val) \
370 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
372 // 'versioned' var, used when defaults change
373 #define CE_CONFIG_STR_V(val, ver) \
374 { #val #ver, 0, Config.val }
376 #define CE_INTVAL_V(val, ver) \
377 { #val #ver, sizeof(val), &val }
379 #define CE_INTVAL_PV(val, ver) \
380 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
382 static const struct {
388 CE_CONFIG_STR_V(Gpu, 3),
390 // CE_CONFIG_STR(Cdr),
392 // CE_CONFIG_VAL(Sio),
395 CE_CONFIG_VAL(Debug),
396 CE_CONFIG_VAL(PsxOut),
397 CE_CONFIG_VAL(SpuIrq),
398 CE_CONFIG_VAL(RCntFix),
399 CE_CONFIG_VAL(VSyncWA),
400 CE_CONFIG_VAL(icache_emulation),
401 CE_CONFIG_VAL(DisableStalls),
404 CE_INTVAL_V(g_scaler, 3),
406 CE_INTVAL(g_layer_x),
407 CE_INTVAL(g_layer_y),
408 CE_INTVAL(g_layer_w),
409 CE_INTVAL(g_layer_h),
410 CE_INTVAL(soft_filter),
411 CE_INTVAL(scanlines),
412 CE_INTVAL(scanline_level),
413 CE_INTVAL(plat_target.vout_method),
414 CE_INTVAL(plat_target.hwfilter),
415 CE_INTVAL(plat_target.vout_fullscreen),
416 CE_INTVAL(state_slot),
417 CE_INTVAL(cpu_clock),
419 CE_INTVAL(in_type_sel1),
420 CE_INTVAL(in_type_sel2),
421 CE_INTVAL(analog_deadzone),
422 CE_INTVAL(memcard1_sel),
423 CE_INTVAL(memcard2_sel),
424 CE_INTVAL(g_autostateld_opt),
425 CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
426 CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
427 CE_INTVAL_V(frameskip, 3),
428 CE_INTVAL_P(thread_rendering),
429 CE_INTVAL_P(gpu_peops.iUseDither),
430 CE_INTVAL_P(gpu_peops.dwActFixes),
431 CE_INTVAL_P(gpu_unai.ilace_force),
432 CE_INTVAL_P(gpu_unai.pixel_skip),
433 CE_INTVAL_P(gpu_unai.lighting),
434 CE_INTVAL_P(gpu_unai.fast_lighting),
435 CE_INTVAL_P(gpu_unai.blending),
436 CE_INTVAL_P(gpu_unai.dithering),
437 CE_INTVAL_P(gpu_unai.lineskip),
438 CE_INTVAL_P(gpu_unai.abe_hack),
439 CE_INTVAL_P(gpu_unai.no_light),
440 CE_INTVAL_P(gpu_unai.no_blend),
441 CE_INTVAL_P(gpu_unai.scale_hires),
442 CE_INTVAL_P(gpu_neon.allow_interlace),
443 CE_INTVAL_P(gpu_neon.enhancement_enable),
444 CE_INTVAL_P(gpu_neon.enhancement_no_main),
445 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
446 CE_INTVAL_P(gpu_peopsgl.iFilterType),
447 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
448 CE_INTVAL_P(gpu_peopsgl.iUseMask),
449 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
450 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
451 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
452 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
453 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
454 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
455 CE_INTVAL(spu_config.iUseReverb),
456 CE_INTVAL(spu_config.iXAPitch),
457 CE_INTVAL(spu_config.iUseInterpolation),
458 CE_INTVAL(spu_config.iTempo),
459 CE_INTVAL(spu_config.iUseThread),
460 CE_INTVAL(config_save_counter),
461 CE_INTVAL(in_evdev_allow_abs_only),
462 CE_INTVAL(volume_boost),
463 CE_INTVAL(psx_clock),
464 CE_INTVAL(new_dynarec_hacks),
465 CE_INTVAL(in_enable_vibration),
468 static char *get_cd_label(void)
470 static char trimlabel[33];
473 strncpy(trimlabel, CdromLabel, 32);
475 for (j = 31; j >= 0; j--)
476 if (trimlabel[j] == ' ')
482 static void make_cfg_fname(char *buf, size_t size, int is_game)
485 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
487 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
490 static void keys_write_all(FILE *f);
491 static char *mystrip(char *str);
493 static int menu_write_config(int is_game)
495 char cfgfile[MAXPATHLEN];
499 config_save_counter++;
501 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
502 f = fopen(cfgfile, "w");
504 printf("menu_write_config: failed to open: %s\n", cfgfile);
508 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
509 fprintf(f, "%s = ", config_data[i].name);
510 switch (config_data[i].len) {
512 fprintf(f, "%s\n", (char *)config_data[i].val);
515 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
518 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
521 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
524 printf("menu_write_config: unhandled len %d for %s\n",
525 (int)config_data[i].len, config_data[i].name);
536 static int menu_do_last_cd_img(int is_get)
538 static const char *defaults[] = { "/media", "/mnt/sd", "/mnt" };
544 snprintf(path, sizeof(path), "." PCSX_DOT_DIR "lastcdimg.txt");
545 f = fopen(path, is_get ? "r" : "w");
552 ret = fread(last_selected_fname, 1, sizeof(last_selected_fname) - 1, f);
553 last_selected_fname[ret] = 0;
554 mystrip(last_selected_fname);
557 fprintf(f, "%s\n", last_selected_fname);
562 for (i = 0; last_selected_fname[0] == 0
563 || stat64(last_selected_fname, &st) != 0; i++)
565 if (i >= ARRAY_SIZE(defaults))
567 strcpy(last_selected_fname, defaults[i]);
574 static void parse_str_val(char *cval, const char *src)
577 strncpy(cval, src, MAXPATHLEN);
578 cval[MAXPATHLEN - 1] = 0;
579 tmp = strchr(cval, '\n');
581 tmp = strchr(cval, '\r');
586 static void keys_load_all(const char *cfg);
588 static int menu_load_config(int is_game)
590 char cfgfile[MAXPATHLEN];
596 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
597 f = fopen(cfgfile, "r");
599 printf("menu_load_config: failed to open: %s\n", cfgfile);
603 fseek(f, 0, SEEK_END);
606 printf("bad size %ld: %s\n", size, cfgfile);
610 cfg = malloc(size + 1);
614 fseek(f, 0, SEEK_SET);
615 if (fread(cfg, 1, size, f) != size) {
616 printf("failed to read: %s\n", cfgfile);
621 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
625 tmp = strstr(cfg, config_data[i].name);
628 tmp += strlen(config_data[i].name);
629 if (strncmp(tmp, " = ", 3) != 0)
633 if (config_data[i].len == 0) {
634 parse_str_val(config_data[i].val, tmp);
639 val = strtoul(tmp, &tmp2, 16);
640 if (tmp2 == NULL || tmp == tmp2)
641 continue; // parse failed
643 switch (config_data[i].len) {
645 *(u8 *)config_data[i].val = val;
648 *(u16 *)config_data[i].val = val;
651 *(u32 *)config_data[i].val = val;
654 printf("menu_load_config: unhandled len %d for %s\n",
655 (int)config_data[i].len, config_data[i].name);
661 char *tmp = strstr(cfg, "lastcdimg = ");
664 parse_str_val(last_selected_fname, tmp);
679 for (i = bios_sel = 0; bioses[i] != NULL; i++)
680 if (strcmp(Config.Bios, bioses[i]) == 0)
681 { bios_sel = i; break; }
683 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
684 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
685 { gpu_plugsel = i; break; }
687 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
688 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
689 { spu_plugsel = i; break; }
691 // memcard selections
692 char mcd1_old[sizeof(Config.Mcd1)];
693 char mcd2_old[sizeof(Config.Mcd2)];
694 strcpy(mcd1_old, Config.Mcd1);
695 strcpy(mcd2_old, Config.Mcd2);
697 if ((unsigned int)memcard1_sel < ARRAY_SIZE(memcards)) {
698 if (memcard1_sel == 0)
699 strcpy(Config.Mcd1, "none");
700 else if (memcards[memcard1_sel] != NULL)
701 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s",
702 MEMCARD_DIR, memcards[memcard1_sel]);
704 if ((unsigned int)memcard2_sel < ARRAY_SIZE(memcards)) {
705 if (memcard2_sel == 0)
706 strcpy(Config.Mcd2, "none");
707 else if (memcards[memcard2_sel] != NULL)
708 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s",
709 MEMCARD_DIR, memcards[memcard2_sel]);
711 if (strcmp(mcd1_old, Config.Mcd1) || strcmp(mcd2_old, Config.Mcd2))
712 LoadMcds(Config.Mcd1, Config.Mcd2);
717 static const char *filter_exts[] = {
718 "bin", "img", "mdf", "iso", "cue", "z",
722 "bz", "znx", "pbp", "cbn", NULL
725 // rrrr rggg gggb bbbb
726 static unsigned short fname2color(const char *fname)
728 static const char *other_exts[] = {
729 "ccd", "toc", "mds", "sub", "table", "index", "sbi"
731 const char *ext = strrchr(fname, '.');
737 for (i = 0; filter_exts[i] != NULL; i++)
738 if (strcasecmp(ext, filter_exts[i]) == 0)
740 for (i = 0; i < array_size(other_exts); i++)
741 if (strcasecmp(ext, other_exts[i]) == 0)
746 static void draw_savestate_bg(int slot);
748 #define MENU_ALIGN_LEFT
749 #ifndef HAVE_PRE_ARMV7 // assume hires device
755 #include "libpicofe/menu.c"
757 // a bit of black magic here
758 static void draw_savestate_bg(int slot)
760 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
762 char fname[MAXPATHLEN];
769 ret = get_state_filename(fname, sizeof(fname), slot);
773 f = gzopen(fname, "rb");
777 if ((ret = (int)gzseek(f, 0x29933d, SEEK_SET)) != 0x29933d) {
778 fprintf(stderr, "gzseek failed: %d\n", ret);
783 gpu = malloc(sizeof(*gpu));
789 ret = gzread(f, gpu, sizeof(*gpu));
791 if (ret != sizeof(*gpu)) {
792 fprintf(stderr, "gzread failed\n");
796 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
798 if (gpu->ulStatus & 0x800000)
799 goto out; // disabled
801 x = gpu->ulControl[5] & 0x3ff;
802 y = (gpu->ulControl[5] >> 10) & 0x1ff;
803 w = psx_widths[(gpu->ulStatus >> 16) & 7];
804 tmp = gpu->ulControl[7];
805 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
806 if (gpu->ulStatus & 0x80000) // doubleheight
808 if (h <= 0 || h > 512)
814 s = (u16 *)gpu->psxVRam + y * 1024 + x;
816 x = max(0, g_menuscreen_w - w) & ~3;
817 y = max(0, g_menuscreen_h / 2 - h / 2);
818 w = min(g_menuscreen_w, w);
819 h = min(g_menuscreen_h, h);
820 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
822 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
823 if (gpu->ulStatus & 0x200000)
824 bgr888_to_rgb565(d, s, w * 3);
826 bgr555_to_rgb565(d, s, w * 2);
828 // darken this so that menu text is visible
829 if (g_menuscreen_w - w < 320)
830 menu_darken_bg(d, d, w, 0);
837 // -------------- key config --------------
839 me_bind_action me_ctrl_actions[] =
841 { "UP ", 1 << DKEY_UP},
842 { "DOWN ", 1 << DKEY_DOWN },
843 { "LEFT ", 1 << DKEY_LEFT },
844 { "RIGHT ", 1 << DKEY_RIGHT },
845 { "TRIANGLE", 1 << DKEY_TRIANGLE },
846 { "CIRCLE ", 1 << DKEY_CIRCLE },
847 { "CROSS ", 1 << DKEY_CROSS },
848 { "SQUARE ", 1 << DKEY_SQUARE },
849 { "L1 ", 1 << DKEY_L1 },
850 { "R1 ", 1 << DKEY_R1 },
851 { "L2 ", 1 << DKEY_L2 },
852 { "R2 ", 1 << DKEY_R2 },
853 { "L3 ", 1 << DKEY_L3 },
854 { "R3 ", 1 << DKEY_R3 },
855 { "START ", 1 << DKEY_START },
856 { "SELECT ", 1 << DKEY_SELECT },
860 me_bind_action emuctrl_actions[] =
862 { "Save State ", 1 << SACTION_SAVE_STATE },
863 { "Load State ", 1 << SACTION_LOAD_STATE },
864 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
865 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
866 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
867 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
868 { "Show/Hide FPS ", 1 << SACTION_TOGGLE_FPS },
869 #ifndef HAVE_PRE_ARMV7
870 { "Switch Renderer ", 1 << SACTION_SWITCH_DISPMODE },
872 { "Fast Forward ", 1 << SACTION_FAST_FORWARD },
873 #if MENU_SHOW_MINIMIZE
874 { "Minimize ", 1 << SACTION_MINIMIZE },
876 #if MENU_SHOW_FULLSCREEN
877 { "Toggle fullscreen", 1 << SACTION_TOGGLE_FULLSCREEN },
879 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
880 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
881 { "Gun A button ", 1 << SACTION_GUN_A },
882 { "Gun B button ", 1 << SACTION_GUN_B },
883 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
885 { "Volume Up ", 1 << SACTION_VOLUME_UP },
886 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
891 static char *mystrip(char *str)
896 for (i = 0; i < len; i++)
897 if (str[i] != ' ') break;
898 if (i > 0) memmove(str, str + i, len - i + 1);
901 for (i = len - 1; i >= 0; i--)
902 if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
908 static void get_line(char *d, size_t size, const char *s)
913 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
922 static void keys_write_all(FILE *f)
926 for (d = 0; d < IN_MAX_DEVS; d++)
928 const int *binds = in_get_dev_binds(d);
929 const char *name = in_get_dev_name(d, 0, 0);
932 if (binds == NULL || name == NULL)
935 fprintf(f, "binddev = %s\n", name);
936 in_get_config(d, IN_CFG_BIND_COUNT, &count);
938 for (k = 0; k < count; k++)
943 act[0] = act[31] = 0;
944 name = in_get_key_name(d, k);
946 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
947 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
948 mask = me_ctrl_actions[i].mask;
950 strncpy(act, me_ctrl_actions[i].name, 31);
951 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
954 mask = me_ctrl_actions[i].mask << 16;
956 strncpy(act, me_ctrl_actions[i].name, 31);
957 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
962 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
963 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
964 mask = emuctrl_actions[i].mask;
966 strncpy(act, emuctrl_actions[i].name, 31);
967 fprintf(f, "bind %s = %s\n", name, mystrip(act));
973 for (k = 0; k < array_size(in_adev); k++)
976 fprintf(f, "bind_analog = %d\n", k);
981 static int parse_bind_val(const char *val, int *type)
985 *type = IN_BINDTYPE_NONE;
989 if (strncasecmp(val, "player", 6) == 0)
991 int player, shift = 0;
992 player = atoi(val + 6) - 1;
994 if ((unsigned int)player > 1)
999 *type = IN_BINDTYPE_PLAYER12;
1000 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
1001 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
1002 return me_ctrl_actions[i].mask << shift;
1005 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
1006 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
1007 *type = IN_BINDTYPE_EMU;
1008 return emuctrl_actions[i].mask;
1015 static void keys_load_all(const char *cfg)
1017 char dev[256], key[128], *act;
1023 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
1026 // don't strip 'dev' because there are weird devices
1027 // with names with space at the end
1028 get_line(dev, sizeof(dev), p);
1030 dev_id = in_config_parse_dev(dev);
1032 printf("input: can't handle dev: %s\n", dev);
1036 in_unbind_all(dev_id, -1, -1);
1037 while ((p = strstr(p, "bind"))) {
1038 if (strncmp(p, "binddev = ", 10) == 0)
1041 if (strncmp(p, "bind_analog", 11) == 0) {
1042 ret = sscanf(p, "bind_analog = %d", &bind);
1045 printf("input: parse error: %16s..\n", p);
1048 if ((unsigned int)bind >= array_size(in_adev)) {
1049 printf("input: analog id %d out of range\n", bind);
1052 in_adev[bind] = dev_id;
1058 printf("input: parse error: %16s..\n", p);
1062 get_line(key, sizeof(key), p);
1063 act = strchr(key, '=');
1065 printf("parse failed: %16s..\n", p);
1073 bind = parse_bind_val(act, &bindtype);
1074 if (bind != -1 && bind != 0) {
1075 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
1076 in_config_bind_key(dev_id, key, bind, bindtype);
1079 lprintf("config: unhandled action \"%s\"\n", act);
1085 static int key_config_loop_wrap(int id, int keys)
1088 case MA_CTRL_PLAYER1:
1089 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
1091 case MA_CTRL_PLAYER2:
1092 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
1095 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
1103 static const char h_nubmode[] = "Maps nub-like analog controls to PSX ones better\n"
1104 "Might cause problems with real analog sticks";
1105 static const char *adevnames[IN_MAX_DEVS + 2];
1106 static int stick_sel[2];
1108 static menu_entry e_menu_keyconfig_analog[] =
1110 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
1111 mee_range (" X axis", 0, in_adev_axis[0][0], 0, 7),
1112 mee_range (" Y axis", 0, in_adev_axis[0][1], 0, 7),
1113 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[0], 1, h_nubmode),
1114 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
1115 mee_range (" X axis", 0, in_adev_axis[1][0], 0, 7),
1116 mee_range (" Y axis", 0, in_adev_axis[1][1], 0, 7),
1117 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[1], 1, h_nubmode),
1121 static int key_config_analog(int id, int keys)
1123 int i, d, count, sel = 0;
1124 int sel2dev_map[IN_MAX_DEVS];
1126 memset(adevnames, 0, sizeof(adevnames));
1127 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1128 memset(stick_sel, 0, sizeof(stick_sel));
1130 adevnames[0] = "None";
1132 for (d = 0; d < IN_MAX_DEVS; d++)
1134 const char *name = in_get_dev_name(d, 0, 1);
1139 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1143 if (in_adev[0] == d) stick_sel[0] = i;
1144 if (in_adev[1] == d) stick_sel[1] = i;
1146 adevnames[i++] = name;
1148 adevnames[i] = NULL;
1150 me_loop(e_menu_keyconfig_analog, &sel);
1152 in_adev[0] = sel2dev_map[stick_sel[0]];
1153 in_adev[1] = sel2dev_map[stick_sel[1]];
1158 static const char *mgn_dev_name(int id, int *offs)
1160 const char *name = NULL;
1163 if (id == MA_CTRL_DEV_FIRST)
1166 for (; it < IN_MAX_DEVS; it++) {
1167 name = in_get_dev_name(it, 1, 1);
1176 static const char *mgn_saveloadcfg(int id, int *offs)
1181 static int mh_savecfg(int id, int keys)
1183 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1184 menu_update_msg("config saved");
1186 menu_update_msg("failed to write config");
1191 static int mh_input_rescan(int id, int keys)
1193 //menu_sync_config();
1195 menu_update_msg("rescan complete.");
1200 static const char *men_in_type_sel[] = {
1201 "Standard (SCPH-1080)",
1202 "Analog (SCPH-1150)",
1206 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1207 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1208 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1210 static menu_entry e_menu_keyconfig[] =
1212 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1213 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1214 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
1215 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
1217 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1218 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
1219 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
1220 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
1221 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
1222 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1223 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1224 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1225 mee_handler ("Rescan devices:", mh_input_rescan),
1227 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
1228 mee_label_mk (MA_CTRL_DEV_NEXT, 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),
1237 static int menu_loop_keyconfig(int id, int keys)
1241 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1242 me_loop(e_menu_keyconfig, &sel);
1246 // ------------ gfx options menu ------------
1248 static const char *men_scaler[] = {
1249 "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL
1251 static const char *men_soft_filter[] = { "None",
1253 "scale2x", "eagle2x",
1256 static const char *men_dummy[] = { NULL };
1257 static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
1258 "int. 4:3 - uses integer if possible, else fractional";
1259 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1260 "using d-pad or move it using R+d-pad";
1261 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1262 static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
1264 static const char h_scanline_l[] = "Scanline brightness, 0-100%";
1267 static int menu_loop_cscaler(int id, int keys)
1271 g_scaler = SCALE_CUSTOM;
1273 plat_gvideo_open(Config.PsxType);
1277 menu_draw_begin(0, 1);
1278 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1279 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1280 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1283 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1284 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1285 if (inp & PBTN_UP) g_layer_y--;
1286 if (inp & PBTN_DOWN) g_layer_y++;
1287 if (inp & PBTN_LEFT) g_layer_x--;
1288 if (inp & PBTN_RIGHT) g_layer_x++;
1289 if (!(inp & PBTN_R)) {
1290 if (inp & PBTN_UP) g_layer_h += 2;
1291 if (inp & PBTN_DOWN) g_layer_h -= 2;
1292 if (inp & PBTN_LEFT) g_layer_w += 2;
1293 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1295 if (inp & (PBTN_MOK|PBTN_MBACK))
1298 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1299 if (g_layer_x < 0) g_layer_x = 0;
1300 if (g_layer_x > 640) g_layer_x = 640;
1301 if (g_layer_y < 0) g_layer_y = 0;
1302 if (g_layer_y > 420) g_layer_y = 420;
1303 if (g_layer_w < 160) g_layer_w = 160;
1304 if (g_layer_h < 60) g_layer_h = 60;
1305 if (g_layer_x + g_layer_w > 800)
1306 g_layer_w = 800 - g_layer_x;
1307 if (g_layer_y + g_layer_h > 480)
1308 g_layer_h = 480 - g_layer_y;
1310 plat_gvideo_open(Config.PsxType);
1314 plat_gvideo_close();
1319 static menu_entry e_menu_gfx_options[] =
1321 mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1322 mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1323 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1324 mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1325 mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1327 mee_onoff ("Scanlines", MA_OPT_SCANLINES, scanlines, 1),
1328 mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1330 mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1331 // mee_onoff ("Vsync", 0, vsync, 1),
1332 mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1336 static int menu_loop_gfx_options(int id, int keys)
1340 me_loop(e_menu_gfx_options, &sel);
1345 // ------------ bios/plugins ------------
1347 #ifdef BUILTIN_GPU_NEON
1349 static const char h_gpu_neon[] =
1350 "Configure built-in NEON GPU plugin";
1351 static const char h_gpu_neon_enhanced[] =
1352 "Renders in double resolution at the cost of lower performance\n"
1353 "(not available for high resolution games)";
1354 static const char h_gpu_neon_enhanced_hack[] =
1355 "Speed hack for above option (glitches some games)";
1356 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1358 static menu_entry e_menu_plugin_gpu_neon[] =
1360 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1361 mee_onoff_h ("Enhanced resolution (slow)", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1362 mee_onoff_h ("Enhanced res. speed hack", 0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1366 static int menu_loop_plugin_gpu_neon(int id, int keys)
1369 me_loop(e_menu_plugin_gpu_neon, &sel);
1375 static menu_entry e_menu_plugin_gpu_unai[] =
1377 //mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1378 //mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1379 //mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1380 //mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1381 mee_onoff ("Interlace", 0, pl_rearmed_cbs.gpu_unai.ilace_force, 1),
1382 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_unai.dithering, 1),
1383 mee_onoff ("Lighting", 0, pl_rearmed_cbs.gpu_unai.lighting, 1),
1384 mee_onoff ("Fast lighting", 0, pl_rearmed_cbs.gpu_unai.fast_lighting, 1),
1385 mee_onoff ("Blending", 0, pl_rearmed_cbs.gpu_unai.blending, 1),
1386 mee_onoff ("Pixel skip", 0, pl_rearmed_cbs.gpu_unai.pixel_skip, 1),
1390 static int menu_loop_plugin_gpu_unai(int id, int keys)
1393 me_loop(e_menu_plugin_gpu_unai, &sel);
1397 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1398 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1399 static const char h_gpu_1[] = "Capcom fighting games";
1400 static const char h_gpu_2[] = "Black screens in Lunar";
1401 static const char h_gpu_3[] = "Compatibility mode";
1402 static const char h_gpu_6[] = "Pandemonium 2";
1403 //static const char h_gpu_7[] = "Skip every second frame";
1404 static const char h_gpu_8[] = "Needed by Dark Forces";
1405 static const char h_gpu_9[] = "better g-colors, worse textures";
1406 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1408 static menu_entry e_menu_plugin_gpu_peops[] =
1410 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1411 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1412 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1413 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1414 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1415 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1416 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1417 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1418 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1419 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1423 static int menu_loop_plugin_gpu_peops(int id, int keys)
1426 me_loop(e_menu_plugin_gpu_peops, &sel);
1430 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1431 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1432 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1434 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1436 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1437 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1438 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1439 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1440 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1441 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1442 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1443 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1444 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1445 mee_label ("Fixes/hacks:"),
1446 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1447 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1448 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1449 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1450 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1451 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1452 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1453 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1454 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1455 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1456 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1460 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1463 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1467 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1468 static const char h_spu_volboost[] = "Large values cause distortion";
1469 static const char h_spu_tempo[] = "Slows down audio if emu is too slow\n"
1470 "This is inaccurate and breaks games";
1472 static menu_entry e_menu_plugin_spu[] =
1474 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1475 mee_onoff ("Reverb", 0, spu_config.iUseReverb, 1),
1476 mee_enum ("Interpolation", 0, spu_config.iUseInterpolation, men_spu_interp),
1477 //mee_onoff ("Adjust XA pitch", 0, spu_config.iXAPitch, 1),
1478 mee_onoff_h ("Adjust tempo", 0, spu_config.iTempo, 1, h_spu_tempo),
1482 static int menu_loop_plugin_spu(int id, int keys)
1485 me_loop(e_menu_plugin_spu, &sel);
1489 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1490 "savestates and can't be changed there. Must save\n"
1491 "config and reload the game for change to take effect";
1492 static const char h_plugin_gpu[] =
1493 #ifdef BUILTIN_GPU_NEON
1494 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1496 "gpu_peops is Pete's soft GPU, slow but accurate\n"
1497 "gpu_unai is GPU from PCSX4ALL, fast but glitchy\n"
1498 "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1499 "must save config and reload the game if changed";
1500 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1501 "must save config and reload the game if changed";
1502 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1503 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1504 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team GPU plugin";
1505 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1507 static menu_entry e_menu_plugin_options[] =
1509 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1510 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1511 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1512 #ifdef BUILTIN_GPU_NEON
1513 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1515 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1516 mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1517 mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1518 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1522 static menu_entry e_menu_main2[];
1524 static int menu_loop_plugin_options(int id, int keys)
1527 me_loop(e_menu_plugin_options, &sel);
1529 // sync BIOS/plugins
1530 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1531 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1532 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1533 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1538 // ------------ adv options menu ------------
1541 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1542 "(lower value - less work for the emu, may be faster)";
1543 static const char h_cfg_noch[] = "Disables game-specific compatibility hacks";
1544 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1545 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1546 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1548 static const char h_cfg_stalls[] = "Will cause some games to run too fast";
1550 static menu_entry e_menu_speed_hacks[] =
1553 mee_range_h ("PSX CPU clock, %%", 0, psx_clock, 1, 500, h_cfg_psxclk),
1554 mee_onoff_h ("Disable compat hacks", 0, new_dynarec_hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1555 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1556 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1557 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1559 mee_onoff_h ("Disable CPU/GTE stalls", 0, Config.DisableStalls, 1, h_cfg_stalls),
1563 static int menu_loop_speed_hacks(int id, int keys)
1566 me_loop(e_menu_speed_hacks, &sel);
1570 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1571 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1572 "(green: normal, red: fmod, blue: noise)";
1573 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1574 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1575 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1576 "(proper .cue/.bin dump is needed otherwise)";
1577 //static const char h_cfg_sio[] = "You should not need this, breaks games";
1578 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1579 static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix\n"
1580 "(timing hack, breaks other games)";
1582 static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1583 "(timing hack, breaks other games)";
1585 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1586 "Might be useful to overcome some dynarec bugs";
1588 static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1589 static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1591 static menu_entry e_menu_adv_options[] =
1593 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1594 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1595 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1596 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1597 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1598 //mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1599 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1600 mee_onoff_h ("ICache emulation", 0, Config.icache_emulation, 1, h_cfg_icache),
1602 mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1604 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
1606 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1608 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1612 static int menu_loop_adv_options(int id, int keys)
1615 me_loop(e_menu_adv_options, &sel);
1619 // ------------ options menu ------------
1621 static int mh_restore_defaults(int id, int keys)
1623 menu_set_defconfig();
1624 menu_update_msg("defaults restored");
1628 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1629 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1631 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1632 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1633 "loading state or both";
1635 static const char h_restore_def[] = "Switches back to default / recommended\n"
1637 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1639 static menu_entry e_menu_options[] =
1641 // mee_range ("Save slot", 0, state_slot, 0, 9),
1642 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1643 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1644 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1645 mee_enum ("Region", 0, region, men_region),
1646 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1648 mee_onoff ("Use C64x DSP for sound", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1650 mee_onoff ("Threaded SPU", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1652 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1653 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1654 mee_handler ("[Advanced]", menu_loop_adv_options),
1655 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1656 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1657 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1661 static int menu_loop_options(int id, int keys)
1665 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1666 me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1667 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1669 me_loop(e_menu_options, &sel);
1674 // ------------ debug menu ------------
1676 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1678 int w = min(g_menuscreen_w, 1024);
1679 int h = min(g_menuscreen_h, 512);
1680 u16 *d = g_menuscreen_ptr;
1681 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1685 gpuf->ulFreezeVersion = 1;
1686 if (GPU_freeze != NULL)
1687 GPU_freeze(1, gpuf);
1689 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1690 bgr555_to_rgb565(d, s, w * 2);
1692 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1693 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1694 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1695 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1696 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1699 static void debug_menu_loop(void)
1701 int inp, df_x = 0, df_y = 0;
1704 gpuf = malloc(sizeof(*gpuf));
1710 menu_draw_begin(0, 1);
1711 draw_frame_debug(gpuf, df_x, df_y);
1714 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1715 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1716 if (inp & PBTN_MBACK) break;
1717 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1718 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1719 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x -= 2; }
1720 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1726 // --------- memcard manager ---------
1728 static void draw_mc_icon(int dx, int dy, const u16 *s)
1733 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1735 for (y = 0; y < 16; y++, s += 16) {
1736 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1737 for (x = 0; x < 16; x++) {
1739 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1740 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1746 static void draw_mc_bg(void)
1748 McdBlock *blocks1, *blocks2;
1752 blocks1 = malloc(15 * sizeof(blocks1[0]));
1753 blocks2 = malloc(15 * sizeof(blocks1[0]));
1754 if (blocks1 == NULL || blocks2 == NULL)
1757 for (i = 0; i < 15; i++) {
1758 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1759 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1762 menu_draw_begin(1, 1);
1764 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1766 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1770 maxicons = g_menuscreen_h / 32;
1773 row2 = g_menuscreen_w / 2;
1774 for (i = 0; i < maxicons; i++) {
1775 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1776 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1778 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1779 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1782 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1790 static void handle_memcard_sel(void)
1792 strcpy(Config.Mcd1, "none");
1793 if (memcard1_sel != 0)
1794 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1795 strcpy(Config.Mcd2, "none");
1796 if (memcard2_sel != 0)
1797 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1798 LoadMcds(Config.Mcd1, Config.Mcd2);
1802 static menu_entry e_memcard_options[] =
1804 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1805 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1809 static int menu_loop_memcards(int id, int keys)
1815 memcard1_sel = memcard2_sel = 0;
1816 p = strrchr(Config.Mcd1, '/');
1818 for (i = 0; memcards[i] != NULL; i++)
1819 if (strcmp(p + 1, memcards[i]) == 0)
1820 { memcard1_sel = i; break; }
1821 p = strrchr(Config.Mcd2, '/');
1823 for (i = 0; memcards[i] != NULL; i++)
1824 if (strcmp(p + 1, memcards[i]) == 0)
1825 { memcard2_sel = i; break; }
1827 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1829 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1834 // ------------ cheats menu ------------
1836 static void draw_cheatlist(int sel)
1838 int max_cnt, start, i, pos, active;
1840 max_cnt = g_menuscreen_h / me_sfont_h;
1841 start = max_cnt / 2 - sel;
1843 menu_draw_begin(1, 1);
1845 for (i = 0; i < NumCheats; i++) {
1847 if (pos < 0) continue;
1848 if (pos >= max_cnt) break;
1849 active = Cheats[i].Enabled;
1850 smalltext_out16(14, pos * me_sfont_h,
1851 active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1852 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1853 Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1857 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1859 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1863 static void menu_loop_cheats(void)
1865 static int menu_sel = 0;
1870 draw_cheatlist(menu_sel);
1871 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1872 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1873 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1874 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1875 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1876 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1877 if (inp & PBTN_MOK) { // action
1878 if (menu_sel < NumCheats)
1879 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1882 if (inp & PBTN_MBACK)
1887 // --------- main menu help ----------
1889 static void menu_bios_warn(void)
1892 static const char msg[] =
1893 "You don't seem to have copied any BIOS\n"
1895 MENU_BIOS_PATH "\n\n"
1897 "While many games work fine with fake\n"
1898 "(HLE) BIOS, others (like MGS and FF8)\n"
1899 "require BIOS to work.\n"
1900 "After copying the file, you'll also need\n"
1901 "to select it in the emu's menu:\n"
1902 "options->[BIOS/Plugins]\n\n"
1903 "The file is usually named SCPH1001.BIN,\n"
1904 "but other not compressed files can be\n"
1906 "Press %s or %s to continue";
1907 char tmp_msg[sizeof(msg) + 64];
1909 snprintf(tmp_msg, sizeof(tmp_msg), msg,
1910 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1913 draw_menu_message(tmp_msg, NULL);
1915 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
1916 if (inp & (PBTN_MBACK|PBTN_MOK))
1921 // ------------ main menu ------------
1923 static menu_entry e_menu_main[];
1925 static void draw_frame_main(void)
1934 if (CdromId[0] != 0) {
1935 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1936 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1937 Config.HLE ? "HLE" : "BIOS");
1938 smalltext_out16(4, 1, buff, 0x105f);
1942 capacity = plat_target_bat_capacity_get();
1944 tmp = localtime(<ime);
1945 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1946 if (capacity >= 0) {
1947 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1952 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1956 static void draw_frame_credits(void)
1958 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1961 static const char credits_text[] =
1963 "(C) 1999-2003 PCSX Team\n"
1964 "(C) 2005-2009 PCSX-df Team\n"
1965 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1966 "ARM recompiler (C) 2009-2011 Ari64\n"
1967 #ifdef BUILTIN_GPU_NEON
1968 "ARM NEON GPU (c) 2011-2012 Exophase\n"
1970 "PEOpS GPU and SPU by Pete Bernert\n"
1971 " and the P.E.Op.S. team\n"
1972 "PCSX4ALL plugin by PCSX4ALL team\n"
1973 " Chui, Franxis, Unai\n\n"
1974 "integration, optimization and\n"
1975 " frontend (C) 2010-2015 notaz\n";
1977 static int reset_game(void)
1980 if (bios_sel == 0 && !Config.HLE)
1986 if (CheckCdrom() != -1) {
1992 static int reload_plugins(const char *cdimg)
1998 set_cd_image(cdimg);
2000 pcnt_hook_plugins();
2002 if (OpenPlugins() == -1) {
2003 menu_update_msg("failed to open plugins");
2006 plugin_call_rearmed_cbs();
2008 cdrIsoMultidiskCount = 1;
2010 CdromLabel[0] = '\0';
2015 static int run_bios(void)
2021 if (reload_plugins(NULL) != 0)
2029 static int run_exe(void)
2031 const char *exts[] = { "exe", NULL };
2034 fname = menu_loop_romsel(last_selected_fname,
2035 sizeof(last_selected_fname), exts, NULL);
2040 if (reload_plugins(NULL) != 0)
2044 if (Load(fname) != 0) {
2045 menu_update_msg("exe load failed, bad file?");
2054 static int run_cd_image(const char *fname)
2056 int autoload_state = g_autostateld_opt;
2059 reload_plugins(fname);
2061 // always autodetect, menu_sync_config will override as needed
2064 if (CheckCdrom() == -1) {
2065 // Only check the CD if we are starting the console with a CD
2067 menu_update_msg("unsupported/invalid CD image");
2073 // Read main executable directly from CDRom and start it
2074 if (LoadCdrom() == -1) {
2076 menu_update_msg("failed to load CD image");
2083 if (autoload_state) {
2084 unsigned int newest = 0;
2085 int time, slot, newest_slot = -1;
2087 for (slot = 0; slot < 10; slot++) {
2088 if (emu_check_save_file(slot, &time)) {
2089 if ((unsigned int)time > newest) {
2096 if (newest_slot >= 0) {
2097 lprintf("autoload slot %d\n", newest_slot);
2098 emu_load_state(newest_slot);
2101 lprintf("no save to autoload.\n");
2108 static int romsel_run(void)
2110 int prev_gpu, prev_spu;
2113 fname = menu_loop_romsel(last_selected_fname,
2114 sizeof(last_selected_fname), filter_exts,
2115 optional_cdimg_filter);
2119 printf("selected file: %s\n", fname);
2121 new_dynarec_clear_full();
2123 if (run_cd_image(fname) != 0)
2126 prev_gpu = gpu_plugsel;
2127 prev_spu = spu_plugsel;
2128 if (menu_load_config(1) != 0)
2129 menu_load_config(0);
2131 // check for plugin changes, have to repeat
2132 // loading if game config changed plugins to reload them
2133 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2134 printf("plugin change detected, reloading plugins..\n");
2135 if (run_cd_image(fname) != 0)
2139 strcpy(last_selected_fname, fname);
2140 menu_do_last_cd_img(0);
2144 static int swap_cd_image(void)
2148 fname = menu_loop_romsel(last_selected_fname,
2149 sizeof(last_selected_fname), filter_exts,
2150 optional_cdimg_filter);
2154 printf("selected file: %s\n", fname);
2157 CdromLabel[0] = '\0';
2159 set_cd_image(fname);
2160 if (ReloadCdromPlugin() < 0) {
2161 menu_update_msg("failed to load cdr plugin");
2164 if (CDR_open() < 0) {
2165 menu_update_msg("failed to open cdr plugin");
2169 SetCdOpenCaseTime(time(NULL) + 2);
2172 strcpy(last_selected_fname, fname);
2176 static int swap_cd_multidisk(void)
2178 cdrIsoMultidiskSelect++;
2180 CdromLabel[0] = '\0';
2183 if (CDR_open() < 0) {
2184 menu_update_msg("failed to open cdr plugin");
2188 SetCdOpenCaseTime(time(NULL) + 2);
2194 static void load_pcsx_cht(void)
2196 static const char *exts[] = { "cht", NULL };
2200 fname = menu_loop_romsel(last_selected_fname,
2201 sizeof(last_selected_fname), exts, NULL);
2205 printf("selected cheat file: %s\n", fname);
2208 if (NumCheats == 0 && NumCodes == 0)
2209 menu_update_msg("failed to load cheats");
2211 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2212 menu_update_msg(msg);
2214 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2217 static int main_menu_handler(int id, int keys)
2221 case MA_MAIN_RESUME_GAME:
2225 case MA_MAIN_SAVE_STATE:
2227 return menu_loop_savestate(0);
2229 case MA_MAIN_LOAD_STATE:
2231 return menu_loop_savestate(1);
2233 case MA_MAIN_RESET_GAME:
2234 if (ready_to_go && reset_game() == 0)
2237 case MA_MAIN_LOAD_ROM:
2238 if (romsel_run() == 0)
2241 case MA_MAIN_SWAP_CD:
2242 if (swap_cd_image() == 0)
2245 case MA_MAIN_SWAP_CD_MULTI:
2246 if (swap_cd_multidisk() == 0)
2249 case MA_MAIN_RUN_BIOS:
2250 if (run_bios() == 0)
2253 case MA_MAIN_RUN_EXE:
2257 case MA_MAIN_CHEATS:
2260 case MA_MAIN_LOAD_CHEATS:
2263 case MA_MAIN_CREDITS:
2264 draw_menu_message(credits_text, draw_frame_credits);
2265 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2268 emu_core_ask_exit();
2271 lprintf("%s: something unknown selected\n", __FUNCTION__);
2278 static menu_entry e_menu_main2[] =
2280 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2281 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2282 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2283 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2284 mee_handler ("Memcard manager", menu_loop_memcards),
2285 mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS, main_menu_handler),
2289 static int main_menu2_handler(int id, int keys)
2293 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2294 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2295 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2296 me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2298 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2301 static const char h_extra[] = "Change CD, manage memcards..\n";
2303 static menu_entry e_menu_main[] =
2307 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2308 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2309 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2310 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2311 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2312 mee_handler ("Options", menu_loop_options),
2313 mee_handler ("Controls", menu_loop_keyconfig),
2314 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
2315 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2316 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2317 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2321 // ----------------------------
2323 static void menu_leave_emu(void);
2325 void menu_loop(void)
2327 static int warned_about_bios = 0;
2332 if (config_save_counter == 0) {
2334 if (bioses[1] != NULL) {
2335 // autoselect BIOS to make user's life easier
2336 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2339 else if (!warned_about_bios) {
2341 warned_about_bios = 1;
2345 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2346 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2347 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2348 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2349 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2351 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2354 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2355 } while (!ready_to_go && !g_emu_want_quit);
2357 /* wait until menu, ok, back is released */
2358 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2361 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2366 static int qsort_strcmp(const void *p1, const void *p2)
2368 char * const *s1 = (char * const *)p1;
2369 char * const *s2 = (char * const *)p2;
2370 return strcasecmp(*s1, *s2);
2373 static void scan_bios_plugins(void)
2375 char fname[MAXPATHLEN];
2377 int bios_i, gpu_i, spu_i, mc_i;
2382 gpu_plugins[0] = "builtin_gpu";
2383 spu_plugins[0] = "builtin_spu";
2384 memcards[0] = "(none)";
2385 bios_i = gpu_i = spu_i = mc_i = 1;
2387 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2388 dir = opendir(fname);
2390 perror("scan_bios_plugins bios opendir");
2405 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2408 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2409 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2410 printf("bad BIOS file: %s\n", ent->d_name);
2414 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2415 bioses[bios_i++] = strdup(ent->d_name);
2419 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2425 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2426 dir = opendir(fname);
2428 perror("scan_bios_plugins plugins opendir");
2442 p = strstr(ent->d_name, ".so");
2446 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2447 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2449 fprintf(stderr, "%s\n", dlerror());
2453 // now what do we have here?
2454 tmp = dlsym(h, "GPUinit");
2457 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2458 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2462 tmp = dlsym(h, "SPUinit");
2465 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2466 spu_plugins[spu_i++] = strdup(ent->d_name);
2470 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2477 dir = opendir("." MEMCARD_DIR);
2479 perror("scan_bios_plugins memcards opendir");
2494 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2497 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2498 if (stat(fname, &st) != 0) {
2499 printf("bad memcard file: %s\n", ent->d_name);
2503 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2504 memcards[mc_i++] = strdup(ent->d_name);
2508 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2512 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2517 void menu_init(void)
2519 char buff[MAXPATHLEN];
2522 cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2524 scan_bios_plugins();
2527 menu_set_defconfig();
2528 menu_load_config(0);
2529 menu_do_last_cd_img(1);
2534 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2535 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2536 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2537 fprintf(stderr, "OOM\n");
2541 emu_make_path(buff, "skin/background.png", sizeof(buff));
2542 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2544 i = plat_target.cpu_clock_set != NULL
2545 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2546 me_enable(e_menu_gfx_options, MA_OPT_CPU_CLOCKS, i);
2548 i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2549 e_menu_gfx_options[i].data = plat_target.vout_methods;
2550 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2551 plat_target.vout_methods != NULL);
2553 i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2554 e_menu_gfx_options[i].data = plat_target.hwfilters;
2555 me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2556 plat_target.hwfilters != NULL);
2558 me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2559 plat_target.gamma_set != NULL);
2561 #ifdef HAVE_PRE_ARMV7
2562 me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2564 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2565 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2566 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2567 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2568 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2569 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2570 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2573 void menu_notify_mode_change(int w, int h, int bpp)
2577 last_vout_bpp = bpp;
2580 static void menu_leave_emu(void)
2582 if (GPU_close != NULL) {
2583 int ret = GPU_close();
2585 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2588 plat_video_menu_enter(ready_to_go);
2590 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2591 if (pl_vout_buf != NULL && ready_to_go) {
2592 int x = max(0, g_menuscreen_w - last_vout_w);
2593 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2594 int w = min(g_menuscreen_w, last_vout_w);
2595 int h = min(g_menuscreen_h, last_vout_h);
2596 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2597 char *s = pl_vout_buf;
2599 if (last_vout_bpp == 16) {
2600 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2601 menu_darken_bg(d, s, w, 0);
2604 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2605 rgb888_to_rgb565(d, s, w * 3);
2606 menu_darken_bg(d, d, w, 0);
2612 cpu_clock = plat_target_cpu_clock_get();
2615 void menu_prepare_emu(void)
2617 R3000Acpu *prev_cpu = psxCpu;
2619 plat_video_menu_leave();
2622 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2626 if (psxCpu != prev_cpu) {
2627 prev_cpu->Shutdown();
2629 // note that this does not really reset, just clears drc caches
2633 psxCpu->ApplyConfig();
2635 // core doesn't care about Config.Cdda changes,
2636 // so handle them manually here
2642 plat_target_cpu_clock_set(cpu_clock);
2644 // push config to GPU plugin
2645 plugin_call_rearmed_cbs();
2647 if (GPU_open != NULL) {
2648 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2650 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2656 void menu_update_msg(const char *msg)
2658 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2659 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2661 menu_error_time = plat_get_ticks_ms();
2662 lprintf("msg: %s\n", menu_error_msg);
2665 void menu_finish(void)
2667 if (cpu_clock_st > 0)
2668 plat_target_cpu_clock_set(cpu_clock_st);