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/dfsound/spu_config.h"
40 #include "psemu_plugin_defs.h"
41 #include "arm_features.h"
44 #define REARMED_BIRTHDAY_TIME 1293306830 /* 25 Dec 2010 */
46 #define array_size(x) (sizeof(x) / sizeof(x[0]))
57 MA_MAIN_SWAP_CD_MULTI,
88 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;
94 static int frameskip = 1; // 0 - auto, 1 - off
95 static char last_selected_fname[MAXPATHLEN];
96 static int config_save_counter, region, in_type_sel1, in_type_sel2;
98 static int memcard1_sel = -1, memcard2_sel = -1;
99 extern int g_autostateld_opt;
100 static int menu_iopts[8];
101 int g_opts, g_scaler, g_gamma = 100;
102 int scanlines, scanline_level = 20;
103 int soft_scaling, analog_deadzone; // for Caanoo
106 #ifndef HAVE_PRE_ARMV7
107 #define DEFAULT_PSX_CLOCK (10000 / CYCLE_MULT_DEFAULT)
108 #define DEFAULT_PSX_CLOCK_S "57"
110 #define DEFAULT_PSX_CLOCK 50
111 #define DEFAULT_PSX_CLOCK_S "50"
114 static const char *bioses[32];
115 static const char *gpu_plugins[16];
116 static const char *spu_plugins[16];
117 static const char *memcards[32];
118 static int bios_sel, gpu_plugsel, spu_plugsel;
120 #ifndef UI_FEATURES_H
121 #define MENU_BIOS_PATH "bios/"
122 #define MENU_SHOW_VARSCALER 0
123 #define MENU_SHOW_VOUTMODE 1
124 #define MENU_SHOW_SCALER2 0
125 #define MENU_SHOW_NUBS_BTNS 0
126 #define MENU_SHOW_VIBRATION 0
127 #define MENU_SHOW_DEADZONE 0
128 #define MENU_SHOW_MINIMIZE 0
129 #define MENU_SHOW_FULLSCREEN 1
130 #define MENU_SHOW_VOLUME 0
133 static int min(int x, int y) { return x < y ? x : y; }
134 static int max(int x, int y) { return x > y ? x : y; }
136 void emu_make_path(char *buff, const char *end, int size)
140 end_len = strlen(end);
141 pos = plat_get_root_dir(buff, size);
142 strncpy(buff + pos, end, size - pos);
144 if (pos + end_len > size - 1)
145 printf("Warning: path truncated: %s\n", buff);
148 static int emu_check_save_file(int slot, int *time)
150 char fname[MAXPATHLEN];
154 ret = emu_check_state(slot);
155 if (ret != 0 || time == NULL)
156 return ret == 0 ? 1 : 0;
158 ret = get_state_filename(fname, sizeof(fname), slot);
162 ret = stat(fname, &status);
166 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
167 return 1; // probably bad rtc like on some Caanoos
169 *time = status.st_mtime;
174 static int emu_save_load_game(int load, int unused)
179 ret = emu_load_state(state_slot);
181 // reflect hle/bios mode from savestate
184 else if (bios_sel == 0 && bioses[1] != NULL)
185 // XXX: maybe find the right bios instead
189 ret = emu_save_state(state_slot);
194 static void rm_namelist_entry(struct dirent **namelist,
195 int count, const char *name)
199 for (i = 1; i < count; i++) {
200 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
203 if (strcmp(name, namelist[i]->d_name) == 0) {
211 static int optional_cdimg_filter(struct dirent **namelist, int count,
215 char buf[256], buf2[256];
216 int i, d, ret, good_cue;
223 for (i = 1; i < count; i++) {
224 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
227 ext = strrchr(namelist[i]->d_name, '.');
229 // should not happen but whatever
236 // first find .cue files and remove files they reference
237 if (strcasecmp(ext, "cue") == 0)
239 snprintf(buf, sizeof(buf), "%s/%s", basedir,
240 namelist[i]->d_name);
250 while (fgets(buf, sizeof(buf), f)) {
251 ret = sscanf(buf, " FILE \"%256[^\"]\"", buf2);
253 ret = sscanf(buf, " FILE %256s", buf2);
257 p = strrchr(buf2, '/');
259 p = strrchr(buf2, '\\');
265 snprintf(buf, sizeof(buf), "%s/%s", basedir, p);
266 ret = stat64(buf, &statf);
268 rm_namelist_entry(namelist, count, p);
281 p = strcasestr(namelist[i]->d_name, "track");
283 ret = strtoul(p + 5, NULL, 10);
293 for (i = d = 1; i < count; i++)
294 if (namelist[i] != NULL)
295 namelist[d++] = namelist[i];
300 // propagate menu settings to the emu vars
301 static void menu_sync_config(void)
303 static int allow_abs_only_old;
308 Config.PsxType = region - 1;
310 Config.cycle_multiplier = 10000 / psx_clock;
312 switch (in_type_sel1) {
313 case 1: in_type[0] = PSE_PAD_TYPE_ANALOGPAD; break;
314 case 2: in_type[0] = PSE_PAD_TYPE_NEGCON; break;
315 case 3: in_type[0] = PSE_PAD_TYPE_NONE; break;
316 default: in_type[0] = PSE_PAD_TYPE_STANDARD;
318 switch (in_type_sel2) {
319 case 1: in_type[1] = PSE_PAD_TYPE_ANALOGPAD; break;
320 case 2: in_type[1] = PSE_PAD_TYPE_NEGCON; break;
321 case 3: in_type[1] = PSE_PAD_TYPE_NONE; break;
322 default: in_type[1] = PSE_PAD_TYPE_STANDARD;
324 if (in_evdev_allow_abs_only != allow_abs_only_old) {
326 allow_abs_only_old = in_evdev_allow_abs_only;
329 spu_config.iVolume = 768 + 128 * volume_boost;
330 pl_rearmed_cbs.frameskip = frameskip - 1;
331 pl_timing_prepare(Config.PsxType);
334 static void menu_set_defconfig(void)
336 emu_set_default_config();
339 g_scaler = SCALE_4_3;
342 frameskip = 1; // 1 - off
343 analog_deadzone = 50;
348 plat_target.vout_fullscreen = 0;
349 psx_clock = DEFAULT_PSX_CLOCK;
352 in_type_sel1 = in_type_sel2 = 0;
353 in_evdev_allow_abs_only = 0;
358 #define CE_CONFIG_STR(val) \
359 { #val, 0, Config.val }
361 #define CE_CONFIG_VAL(val) \
362 { #val, sizeof(Config.val), &Config.val }
364 #define CE_STR(val) \
367 #define CE_INTVAL(val) \
368 { #val, sizeof(val), &val }
370 #define CE_INTVAL_N(name, val) \
371 { name, sizeof(val), &val }
373 #define CE_INTVAL_P(val) \
374 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
376 // 'versioned' var, used when defaults change
377 #define CE_CONFIG_STR_V(val, ver) \
378 { #val #ver, 0, Config.val }
380 #define CE_INTVAL_V(val, ver) \
381 { #val #ver, sizeof(val), &val }
383 #define CE_INTVAL_PV(val, ver) \
384 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
386 static const struct {
392 CE_CONFIG_STR_V(Gpu, 3),
394 // CE_CONFIG_STR(Cdr),
398 CE_CONFIG_VAL(Debug),
399 CE_CONFIG_VAL(PsxOut),
400 CE_CONFIG_VAL(icache_emulation),
401 CE_CONFIG_VAL(DisableStalls),
403 CE_CONFIG_VAL(GpuListWalking),
404 CE_CONFIG_VAL(PreciseExceptions),
406 CE_INTVAL_V(g_scaler, 3),
408 CE_INTVAL(g_layer_x),
409 CE_INTVAL(g_layer_y),
410 CE_INTVAL(g_layer_w),
411 CE_INTVAL(g_layer_h),
412 CE_INTVAL(soft_filter),
413 CE_INTVAL(scanlines),
414 CE_INTVAL(scanline_level),
415 CE_INTVAL(plat_target.vout_method),
416 CE_INTVAL(plat_target.hwfilter),
417 CE_INTVAL(plat_target.vout_fullscreen),
418 CE_INTVAL(state_slot),
419 CE_INTVAL(cpu_clock),
421 CE_INTVAL(in_type_sel1),
422 CE_INTVAL(in_type_sel2),
423 CE_INTVAL(analog_deadzone),
424 CE_INTVAL(memcard1_sel),
425 CE_INTVAL(memcard2_sel),
426 CE_INTVAL(g_autostateld_opt),
427 CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
428 CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
429 CE_INTVAL_V(frameskip, 4),
430 CE_INTVAL_P(gpu_peops.iUseDither),
431 CE_INTVAL_P(gpu_peops.dwActFixes),
432 CE_INTVAL_P(gpu_unai.lineskip),
433 CE_INTVAL_P(gpu_unai.abe_hack),
434 CE_INTVAL_P(gpu_unai.no_light),
435 CE_INTVAL_P(gpu_unai.no_blend),
437 CE_INTVAL_P(gpu_senquack.ilace_force),
438 CE_INTVAL_P(gpu_senquack.pixel_skip),
439 CE_INTVAL_P(gpu_senquack.lighting),
440 CE_INTVAL_P(gpu_senquack.fast_lighting),
441 CE_INTVAL_P(gpu_senquack.blending),
442 CE_INTVAL_P(gpu_senquack.dithering),
443 CE_INTVAL_P(gpu_senquack.scale_hires),
445 CE_INTVAL_P(gpu_neon.allow_interlace),
446 CE_INTVAL_P(gpu_neon.enhancement_enable),
447 CE_INTVAL_P(gpu_neon.enhancement_no_main),
448 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
449 CE_INTVAL_P(gpu_peopsgl.iFilterType),
450 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
451 CE_INTVAL_P(gpu_peopsgl.iUseMask),
452 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
453 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
454 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
455 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
456 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
457 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
458 CE_INTVAL_P(screen_centering_type),
459 CE_INTVAL_P(screen_centering_x),
460 CE_INTVAL_P(screen_centering_y),
461 CE_INTVAL(spu_config.iUseReverb),
462 CE_INTVAL(spu_config.iXAPitch),
463 CE_INTVAL(spu_config.iUseInterpolation),
464 CE_INTVAL(spu_config.iTempo),
465 CE_INTVAL(spu_config.iUseThread),
466 CE_INTVAL(config_save_counter),
467 CE_INTVAL(in_evdev_allow_abs_only),
468 CE_INTVAL(volume_boost),
469 CE_INTVAL(psx_clock),
470 CE_INTVAL(new_dynarec_hacks),
471 CE_INTVAL(in_enable_vibration),
474 static char *get_cd_label(void)
476 static char trimlabel[33];
479 strncpy(trimlabel, CdromLabel, 32);
481 for (j = 31; j >= 0; j--)
482 if (trimlabel[j] == ' ')
488 static void make_cfg_fname(char *buf, size_t size, int is_game)
491 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
493 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
496 static void keys_write_all(FILE *f);
497 static char *mystrip(char *str);
499 static int menu_write_config(int is_game)
501 char cfgfile[MAXPATHLEN];
505 config_save_counter++;
507 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
508 f = fopen(cfgfile, "w");
510 printf("menu_write_config: failed to open: %s\n", cfgfile);
514 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
515 fprintf(f, "%s = ", config_data[i].name);
516 switch (config_data[i].len) {
518 fprintf(f, "%s\n", (char *)config_data[i].val);
521 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
524 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
527 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
530 printf("menu_write_config: unhandled len %d for %s\n",
531 (int)config_data[i].len, config_data[i].name);
542 static int menu_do_last_cd_img(int is_get)
544 static const char *defaults[] = { "/media", "/mnt/sd", "/mnt" };
550 snprintf(path, sizeof(path), "." PCSX_DOT_DIR "lastcdimg.txt");
551 f = fopen(path, is_get ? "r" : "w");
558 ret = fread(last_selected_fname, 1, sizeof(last_selected_fname) - 1, f);
559 last_selected_fname[ret] = 0;
560 mystrip(last_selected_fname);
563 fprintf(f, "%s\n", last_selected_fname);
568 for (i = 0; last_selected_fname[0] == 0
569 || stat64(last_selected_fname, &st) != 0; i++)
571 if (i >= ARRAY_SIZE(defaults))
573 strcpy(last_selected_fname, defaults[i]);
580 static void parse_str_val(char *cval, const char *src)
583 strncpy(cval, src, MAXPATHLEN);
584 cval[MAXPATHLEN - 1] = 0;
585 tmp = strchr(cval, '\n');
587 tmp = strchr(cval, '\r');
592 static void keys_load_all(const char *cfg);
594 int menu_load_config(int is_game)
596 char cfgfile[MAXPATHLEN];
602 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
603 f = fopen(cfgfile, "r");
605 printf("menu_load_config: failed to open: %s\n", cfgfile);
609 fseek(f, 0, SEEK_END);
612 printf("bad size %ld: %s\n", size, cfgfile);
616 cfg = malloc(size + 1);
620 fseek(f, 0, SEEK_SET);
621 if (fread(cfg, 1, size, f) != size) {
622 printf("failed to read: %s\n", cfgfile);
627 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
631 tmp = strstr(cfg, config_data[i].name);
634 tmp += strlen(config_data[i].name);
635 if (strncmp(tmp, " = ", 3) != 0)
639 if (config_data[i].len == 0) {
640 parse_str_val(config_data[i].val, tmp);
645 val = strtoul(tmp, &tmp2, 16);
646 if (tmp2 == NULL || tmp == tmp2)
647 continue; // parse failed
649 switch (config_data[i].len) {
651 *(u8 *)config_data[i].val = val;
654 *(u16 *)config_data[i].val = val;
657 *(u32 *)config_data[i].val = val;
660 printf("menu_load_config: unhandled len %d for %s\n",
661 (int)config_data[i].len, config_data[i].name);
667 char *tmp = strstr(cfg, "lastcdimg = ");
670 parse_str_val(last_selected_fname, tmp);
685 for (i = bios_sel = 0; bioses[i] != NULL; i++)
686 if (strcmp(Config.Bios, bioses[i]) == 0)
687 { bios_sel = i; break; }
689 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
690 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
691 { gpu_plugsel = i; break; }
693 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
694 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
695 { spu_plugsel = i; break; }
697 // memcard selections
698 char mcd1_old[sizeof(Config.Mcd1)];
699 char mcd2_old[sizeof(Config.Mcd2)];
700 strcpy(mcd1_old, Config.Mcd1);
701 strcpy(mcd2_old, Config.Mcd2);
703 if ((unsigned int)memcard1_sel < ARRAY_SIZE(memcards)) {
704 if (memcard1_sel == 0)
705 strcpy(Config.Mcd1, "none");
706 else if (memcards[memcard1_sel] != NULL)
707 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s",
708 MEMCARD_DIR, memcards[memcard1_sel]);
710 if ((unsigned int)memcard2_sel < ARRAY_SIZE(memcards)) {
711 if (memcard2_sel == 0)
712 strcpy(Config.Mcd2, "none");
713 else if (memcards[memcard2_sel] != NULL)
714 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s",
715 MEMCARD_DIR, memcards[memcard2_sel]);
717 if (strcmp(mcd1_old, Config.Mcd1) || strcmp(mcd2_old, Config.Mcd2))
718 LoadMcds(Config.Mcd1, Config.Mcd2);
723 static const char *filter_exts[] = {
724 "bin", "img", "mdf", "iso", "cue", "z",
728 "bz", "znx", "pbp", "cbn", NULL
731 // rrrr rggg gggb bbbb
732 static unsigned short fname2color(const char *fname)
734 static const char *other_exts[] = {
735 "ccd", "toc", "mds", "sub", "table", "index", "sbi"
737 const char *ext = strrchr(fname, '.');
743 for (i = 0; filter_exts[i] != NULL; i++)
744 if (strcasecmp(ext, filter_exts[i]) == 0)
746 for (i = 0; i < array_size(other_exts); i++)
747 if (strcasecmp(ext, other_exts[i]) == 0)
752 static void draw_savestate_bg(int slot);
754 #define MENU_ALIGN_LEFT
755 #ifndef HAVE_PRE_ARMV7 // assume hires device
761 #include "libpicofe/menu.c"
763 // a bit of black magic here
764 static void draw_savestate_bg(int slot)
766 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
768 char fname[MAXPATHLEN];
775 ret = get_state_filename(fname, sizeof(fname), slot);
779 f = gzopen(fname, "rb");
783 if ((ret = (int)gzseek(f, 0x29933d, SEEK_SET)) != 0x29933d) {
784 fprintf(stderr, "gzseek failed: %d\n", ret);
789 gpu = malloc(sizeof(*gpu));
795 ret = gzread(f, gpu, sizeof(*gpu));
797 if (ret != sizeof(*gpu)) {
798 fprintf(stderr, "gzread failed\n");
802 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
804 if (gpu->ulStatus & 0x800000)
805 goto out; // disabled
807 x = gpu->ulControl[5] & 0x3ff;
808 y = (gpu->ulControl[5] >> 10) & 0x1ff;
809 w = psx_widths[(gpu->ulStatus >> 16) & 7];
810 tmp = gpu->ulControl[7];
811 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
812 if (gpu->ulStatus & 0x80000) // doubleheight
814 if (h <= 0 || h > 512)
820 s = (u16 *)gpu->psxVRam + y * 1024 + x;
822 x = max(0, g_menuscreen_w - w) & ~3;
823 y = max(0, g_menuscreen_h / 2 - h / 2);
824 w = min(g_menuscreen_w, w);
825 h = min(g_menuscreen_h, h);
826 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
828 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
829 if (gpu->ulStatus & 0x200000)
830 bgr888_to_rgb565(d, s, w * 3);
832 bgr555_to_rgb565(d, s, w * 2);
834 // darken this so that menu text is visible
835 if (g_menuscreen_w - w < 320)
836 menu_darken_bg(d, d, w, 0);
843 // -------------- key config --------------
845 me_bind_action me_ctrl_actions[] =
847 { "UP ", 1 << DKEY_UP},
848 { "DOWN ", 1 << DKEY_DOWN },
849 { "LEFT ", 1 << DKEY_LEFT },
850 { "RIGHT ", 1 << DKEY_RIGHT },
851 { "TRIANGLE", 1 << DKEY_TRIANGLE },
852 { "CIRCLE ", 1 << DKEY_CIRCLE },
853 { "CROSS ", 1 << DKEY_CROSS },
854 { "SQUARE ", 1 << DKEY_SQUARE },
855 { "L1 ", 1 << DKEY_L1 },
856 { "R1 ", 1 << DKEY_R1 },
857 { "L2 ", 1 << DKEY_L2 },
858 { "R2 ", 1 << DKEY_R2 },
859 { "L3 ", 1 << DKEY_L3 },
860 { "R3 ", 1 << DKEY_R3 },
861 { "START ", 1 << DKEY_START },
862 { "SELECT ", 1 << DKEY_SELECT },
866 me_bind_action emuctrl_actions[] =
868 { "Save State ", 1 << SACTION_SAVE_STATE },
869 { "Load State ", 1 << SACTION_LOAD_STATE },
870 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
871 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
872 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
873 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
874 { "Show/Hide FPS ", 1 << SACTION_TOGGLE_FPS },
875 #ifndef HAVE_PRE_ARMV7
876 { "Switch Renderer ", 1 << SACTION_SWITCH_DISPMODE },
878 { "Fast Forward ", 1 << SACTION_FAST_FORWARD },
879 #if MENU_SHOW_MINIMIZE
880 { "Minimize ", 1 << SACTION_MINIMIZE },
882 #if MENU_SHOW_FULLSCREEN
883 { "Toggle fullscreen", 1 << SACTION_TOGGLE_FULLSCREEN },
885 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
886 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
887 { "Gun A button ", 1 << SACTION_GUN_A },
888 { "Gun B button ", 1 << SACTION_GUN_B },
889 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
891 { "Volume Up ", 1 << SACTION_VOLUME_UP },
892 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
897 static char *mystrip(char *str)
902 for (i = 0; i < len; i++)
903 if (str[i] != ' ') break;
904 if (i > 0) memmove(str, str + i, len - i + 1);
907 for (i = len - 1; i >= 0; i--)
908 if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
914 static void get_line(char *d, size_t size, const char *s)
919 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
928 static void keys_write_all(FILE *f)
932 for (d = 0; d < IN_MAX_DEVS; d++)
934 const int *binds = in_get_dev_binds(d);
935 const char *name = in_get_dev_name(d, 0, 0);
938 if (binds == NULL || name == NULL)
941 fprintf(f, "binddev = %s\n", name);
942 in_get_config(d, IN_CFG_BIND_COUNT, &count);
944 for (k = 0; k < count; k++)
949 act[0] = act[31] = 0;
950 name = in_get_key_name(d, k);
952 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
953 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
954 mask = me_ctrl_actions[i].mask;
956 strncpy(act, me_ctrl_actions[i].name, 31);
957 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
960 mask = me_ctrl_actions[i].mask << 16;
962 strncpy(act, me_ctrl_actions[i].name, 31);
963 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
968 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
969 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
970 mask = emuctrl_actions[i].mask;
972 strncpy(act, emuctrl_actions[i].name, 31);
973 fprintf(f, "bind %s = %s\n", name, mystrip(act));
979 for (k = 0; k < array_size(in_adev); k++)
982 fprintf(f, "bind_analog = %d\n", k);
987 static int parse_bind_val(const char *val, int *type)
991 *type = IN_BINDTYPE_NONE;
995 if (strncasecmp(val, "player", 6) == 0)
997 int player, shift = 0;
998 player = atoi(val + 6) - 1;
1000 if ((unsigned int)player > 1)
1005 *type = IN_BINDTYPE_PLAYER12;
1006 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
1007 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
1008 return me_ctrl_actions[i].mask << shift;
1011 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
1012 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
1013 *type = IN_BINDTYPE_EMU;
1014 return emuctrl_actions[i].mask;
1021 static void keys_load_all(const char *cfg)
1023 char dev[256], key[128], *act;
1029 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
1032 // don't strip 'dev' because there are weird devices
1033 // with names with space at the end
1034 get_line(dev, sizeof(dev), p);
1036 dev_id = in_config_parse_dev(dev);
1038 printf("input: can't handle dev: %s\n", dev);
1042 in_unbind_all(dev_id, -1, -1);
1043 while ((p = strstr(p, "bind"))) {
1044 if (strncmp(p, "binddev = ", 10) == 0)
1047 if (strncmp(p, "bind_analog", 11) == 0) {
1048 ret = sscanf(p, "bind_analog = %d", &bind);
1051 printf("input: parse error: %16s..\n", p);
1054 if ((unsigned int)bind >= array_size(in_adev)) {
1055 printf("input: analog id %d out of range\n", bind);
1058 in_adev[bind] = dev_id;
1064 printf("input: parse error: %16s..\n", p);
1068 get_line(key, sizeof(key), p);
1069 act = strchr(key, '=');
1071 printf("parse failed: %16s..\n", p);
1079 bind = parse_bind_val(act, &bindtype);
1080 if (bind != -1 && bind != 0) {
1081 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
1082 in_config_bind_key(dev_id, key, bind, bindtype);
1085 lprintf("config: unhandled action \"%s\"\n", act);
1091 static int key_config_loop_wrap(int id, int keys)
1094 case MA_CTRL_PLAYER1:
1095 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
1097 case MA_CTRL_PLAYER2:
1098 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
1101 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
1109 static const char h_nubmode[] = "Maps nub-like analog controls to PSX ones better\n"
1110 "Might cause problems with real analog sticks";
1111 static const char *adevnames[IN_MAX_DEVS + 2];
1112 static int stick_sel[2];
1114 static menu_entry e_menu_keyconfig_analog[] =
1116 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
1117 mee_range (" X axis", 0, in_adev_axis[0][0], 0, 7),
1118 mee_range (" Y axis", 0, in_adev_axis[0][1], 0, 7),
1119 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[0], 1, h_nubmode),
1120 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
1121 mee_range (" X axis", 0, in_adev_axis[1][0], 0, 7),
1122 mee_range (" Y axis", 0, in_adev_axis[1][1], 0, 7),
1123 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[1], 1, h_nubmode),
1127 static int key_config_analog(int id, int keys)
1129 int i, d, count, sel = 0;
1130 int sel2dev_map[IN_MAX_DEVS];
1132 memset(adevnames, 0, sizeof(adevnames));
1133 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1134 memset(stick_sel, 0, sizeof(stick_sel));
1136 adevnames[0] = "None";
1138 for (d = 0; d < IN_MAX_DEVS; d++)
1140 const char *name = in_get_dev_name(d, 0, 1);
1145 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1149 if (in_adev[0] == d) stick_sel[0] = i;
1150 if (in_adev[1] == d) stick_sel[1] = i;
1152 adevnames[i++] = name;
1154 adevnames[i] = NULL;
1156 me_loop(e_menu_keyconfig_analog, &sel);
1158 in_adev[0] = sel2dev_map[stick_sel[0]];
1159 in_adev[1] = sel2dev_map[stick_sel[1]];
1164 static const char *mgn_dev_name(int id, int *offs)
1166 const char *name = NULL;
1169 if (id == MA_CTRL_DEV_FIRST)
1172 for (; it < IN_MAX_DEVS; it++) {
1173 name = in_get_dev_name(it, 1, 1);
1182 static const char *mgn_saveloadcfg(int id, int *offs)
1187 static int mh_savecfg(int id, int keys)
1189 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1190 menu_update_msg("config saved");
1192 menu_update_msg("failed to write config");
1197 static int mh_input_rescan(int id, int keys)
1199 //menu_sync_config();
1201 menu_update_msg("rescan complete.");
1206 static const char *men_in_type_sel[] = {
1207 "Standard (SCPH-1080)",
1208 "Analog (SCPH-1150)",
1213 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1214 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1215 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1217 static menu_entry e_menu_keyconfig[] =
1219 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1220 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1221 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
1222 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
1224 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1225 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
1226 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
1227 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
1228 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
1229 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1230 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1231 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1232 mee_handler ("Rescan devices:", mh_input_rescan),
1234 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
1235 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1236 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1237 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1238 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1239 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1240 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1244 static int menu_loop_keyconfig(int id, int keys)
1248 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1249 me_loop(e_menu_keyconfig, &sel);
1253 // ------------ gfx options menu ------------
1255 static const char *men_scaler[] = {
1256 "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL
1258 static const char *men_soft_filter[] = { "None",
1260 "scale2x", "eagle2x",
1263 static const char *men_dummy[] = { NULL };
1264 static const char *men_centering[] = { "Auto", "Ingame", "Force", NULL };
1265 static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
1266 "int. 4:3 - uses integer if possible, else fractional";
1267 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1268 "using d-pad or move it using R+d-pad";
1269 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1270 static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
1272 static const char h_scanline_l[] = "Scanline brightness, 0-100%";
1275 static int menu_loop_cscaler(int id, int keys)
1279 g_scaler = SCALE_CUSTOM;
1281 plat_gvideo_open(Config.PsxType);
1285 menu_draw_begin(0, 1);
1286 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1287 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1288 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1291 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1292 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1293 if (inp & PBTN_UP) g_layer_y--;
1294 if (inp & PBTN_DOWN) g_layer_y++;
1295 if (inp & PBTN_LEFT) g_layer_x--;
1296 if (inp & PBTN_RIGHT) g_layer_x++;
1297 if (!(inp & PBTN_R)) {
1298 if (inp & PBTN_UP) g_layer_h += 2;
1299 if (inp & PBTN_DOWN) g_layer_h -= 2;
1300 if (inp & PBTN_LEFT) g_layer_w += 2;
1301 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1303 if (inp & (PBTN_MOK|PBTN_MBACK))
1306 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1307 if (g_layer_x < 0) g_layer_x = 0;
1308 if (g_layer_x > 640) g_layer_x = 640;
1309 if (g_layer_y < 0) g_layer_y = 0;
1310 if (g_layer_y > 420) g_layer_y = 420;
1311 if (g_layer_w < 160) g_layer_w = 160;
1312 if (g_layer_h < 60) g_layer_h = 60;
1313 if (g_layer_x + g_layer_w > 800)
1314 g_layer_w = 800 - g_layer_x;
1315 if (g_layer_y + g_layer_h > 480)
1316 g_layer_h = 480 - g_layer_y;
1318 plat_gvideo_open(Config.PsxType);
1322 plat_gvideo_close();
1327 static menu_entry e_menu_gfx_options[] =
1329 mee_enum ("Screen centering", MA_OPT_CENTERING, pl_rearmed_cbs.screen_centering_type, men_centering),
1330 mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1331 mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1332 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1333 mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1334 mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1336 mee_onoff ("Scanlines", MA_OPT_SCANLINES, scanlines, 1),
1337 mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1339 mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1340 // mee_onoff ("Vsync", 0, vsync, 1),
1341 mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1345 static int menu_loop_gfx_options(int id, int keys)
1349 me_loop(e_menu_gfx_options, &sel);
1354 // ------------ bios/plugins ------------
1356 #ifdef BUILTIN_GPU_NEON
1358 static const char h_gpu_neon[] =
1359 "Configure built-in NEON GPU plugin";
1360 static const char h_gpu_neon_enhanced[] =
1361 "Renders in double resolution at the cost of lower performance\n"
1362 "(not available for high resolution games)";
1363 static const char h_gpu_neon_enhanced_hack[] =
1364 "Speed hack for above option (glitches some games)";
1365 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1367 static menu_entry e_menu_plugin_gpu_neon[] =
1369 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1370 mee_onoff_h ("Enhanced resolution", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1371 mee_onoff_h ("Enhanced res. speed hack", 0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1375 static int menu_loop_plugin_gpu_neon(int id, int keys)
1378 me_loop(e_menu_plugin_gpu_neon, &sel);
1384 static menu_entry e_menu_plugin_gpu_unai[] =
1386 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1387 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1388 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1389 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1393 static int menu_loop_plugin_gpu_unai(int id, int keys)
1396 me_loop(e_menu_plugin_gpu_unai, &sel);
1400 static menu_entry e_menu_plugin_gpu_senquack[] =
1403 mee_onoff ("Interlace", 0, pl_rearmed_cbs.gpu_senquack.ilace_force, 1),
1404 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_senquack.dithering, 1),
1405 mee_onoff ("Lighting", 0, pl_rearmed_cbs.gpu_senquack.lighting, 1),
1406 mee_onoff ("Fast lighting", 0, pl_rearmed_cbs.gpu_senquack.fast_lighting, 1),
1407 mee_onoff ("Blending", 0, pl_rearmed_cbs.gpu_senquack.blending, 1),
1408 mee_onoff ("Pixel skip", 0, pl_rearmed_cbs.gpu_senquack.pixel_skip, 1),
1413 static int menu_loop_plugin_gpu_senquack(int id, int keys)
1416 me_loop(e_menu_plugin_gpu_senquack, &sel);
1421 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1422 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1423 static const char h_gpu_1[] = "Capcom fighting games";
1424 static const char h_gpu_2[] = "Black screens in Lunar";
1425 static const char h_gpu_3[] = "Compatibility mode";
1426 static const char h_gpu_6[] = "Pandemonium 2";
1427 //static const char h_gpu_7[] = "Skip every second frame";
1428 static const char h_gpu_8[] = "Needed by Dark Forces";
1429 static const char h_gpu_9[] = "better g-colors, worse textures";
1430 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1432 static menu_entry e_menu_plugin_gpu_peops[] =
1434 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1435 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1436 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1437 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1438 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1439 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1440 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1441 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1442 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1443 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1447 static int menu_loop_plugin_gpu_peops(int id, int keys)
1450 me_loop(e_menu_plugin_gpu_peops, &sel);
1454 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1455 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1456 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1458 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1460 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1461 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1462 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1463 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1464 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1465 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1466 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1467 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1468 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1469 mee_label ("Fixes/hacks:"),
1470 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1471 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1472 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1473 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1474 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1475 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1476 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1477 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1478 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1479 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1480 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1484 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1487 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1491 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1492 static const char h_spu_volboost[] = "Large values cause distortion";
1493 static const char h_spu_tempo[] = "Slows down audio if emu is too slow\n"
1494 "This is inaccurate and breaks games";
1496 static menu_entry e_menu_plugin_spu[] =
1498 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1499 mee_onoff ("Reverb", 0, spu_config.iUseReverb, 1),
1500 mee_enum ("Interpolation", 0, spu_config.iUseInterpolation, men_spu_interp),
1501 //mee_onoff ("Adjust XA pitch", 0, spu_config.iXAPitch, 1),
1502 mee_onoff_h ("Adjust tempo", 0, spu_config.iTempo, 1, h_spu_tempo),
1506 static int menu_loop_plugin_spu(int id, int keys)
1509 me_loop(e_menu_plugin_spu, &sel);
1513 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1514 "savestates and can't be changed there. Must save\n"
1515 "config and reload the game for change to take effect";
1516 static const char h_plugin_gpu[] =
1517 #ifdef BUILTIN_GPU_NEON
1518 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1520 "gpu_peops is Pete's soft GPU, slow but accurate\n"
1521 "gpu_unai is GPU from PCSX4ALL, fast but glitchy\n"
1522 "gpu_senquack is more accurate but slower\n"
1523 "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1524 "must save config and reload the game if changed";
1525 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1526 "must save config and reload the game if changed";
1527 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1528 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1529 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team GPU plugin";
1530 static const char h_gpu_senquack[] = "Configure Unai/PCSX4ALL Senquack plugin";
1531 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1533 static menu_entry e_menu_plugin_options[] =
1535 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1536 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1537 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1538 #ifdef BUILTIN_GPU_NEON
1539 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1541 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1542 mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1543 mee_handler_h ("Configure gpu_senquack GPU plugin", menu_loop_plugin_gpu_senquack, h_gpu_senquack),
1544 mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1545 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1549 static menu_entry e_menu_main2[];
1551 static int menu_loop_plugin_options(int id, int keys)
1554 me_loop(e_menu_plugin_options, &sel);
1556 // sync BIOS/plugins
1557 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1558 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1559 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1560 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1565 // ------------ adv options menu ------------
1568 static const char h_cfg_noch[] = "Disables game-specific compatibility hacks";
1569 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1570 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1571 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1573 static const char h_cfg_stalls[] = "Will cause some games to run too fast";
1575 static menu_entry e_menu_speed_hacks[] =
1578 mee_onoff_h ("Disable compat hacks", 0, new_dynarec_hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1579 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1580 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1581 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1583 mee_onoff_h ("Disable CPU/GTE stalls", 0, menu_iopts[0], 1, h_cfg_stalls),
1587 static int menu_loop_speed_hacks(int id, int keys)
1590 menu_iopts[0] = Config.DisableStalls;
1591 me_loop(e_menu_speed_hacks, &sel);
1592 Config.DisableStalls = menu_iopts[0];
1596 static const char *men_gpul[] = { "Auto", "Off", "On", NULL };
1598 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1599 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1600 "(green: normal, red: fmod, blue: noise)";
1601 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1602 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1603 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1604 "(proper .cue/.bin dump is needed otherwise)";
1606 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1607 "Might be useful to overcome some dynarec bugs";
1609 static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1610 static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1611 static const char h_cfg_exc[] = "Emulate some PSX's debug hw like breakpoints\n"
1612 "and exceptions (slow, interpreter only, keep off)";
1613 static const char h_cfg_gpul[] = "Try enabling this if the game misses some graphics\n"
1614 "causes a performance hit";
1615 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1616 "(adjust this if the game is too slow/too fast/hangs)";
1618 enum { AMO_XA, AMO_CDDA, AMO_IC, AMO_BP, AMO_CPU, AMO_GPUL };
1620 static menu_entry e_menu_adv_options[] =
1622 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1623 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1624 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1625 mee_onoff_h ("Disable XA Decoding", 0, menu_iopts[AMO_XA], 1, h_cfg_xa),
1626 mee_onoff_h ("Disable CD Audio", 0, menu_iopts[AMO_CDDA], 1, h_cfg_cdda),
1627 mee_onoff_h ("ICache emulation", 0, menu_iopts[AMO_IC], 1, h_cfg_icache),
1628 mee_onoff_h ("BP exception emulation", 0, menu_iopts[AMO_BP], 1, h_cfg_exc),
1629 mee_enum_h ("GPU l-list slow walking",0, menu_iopts[AMO_GPUL], men_gpul, h_cfg_gpul),
1630 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
1631 mee_onoff_h ("Disable dynarec (slow!)",0, menu_iopts[AMO_CPU], 1, h_cfg_nodrc),
1633 mee_range_h ("PSX CPU clock, %", 0, psx_clock, 1, 500, h_cfg_psxclk),
1634 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1638 static int menu_loop_adv_options(int id, int keys)
1645 { &Config.Xa, &menu_iopts[AMO_XA] },
1646 { &Config.Cdda, &menu_iopts[AMO_CDDA] },
1647 { &Config.icache_emulation, &menu_iopts[AMO_IC] },
1648 { &Config.PreciseExceptions, &menu_iopts[AMO_BP] },
1649 { &Config.Cpu, &menu_iopts[AMO_CPU] },
1652 for (i = 0; i < ARRAY_SIZE(opts); i++)
1653 *opts[i].mopt = *opts[i].opt;
1654 menu_iopts[AMO_GPUL] = Config.GpuListWalking + 1;
1656 me_loop(e_menu_adv_options, &sel);
1658 for (i = 0; i < ARRAY_SIZE(opts); i++)
1659 *opts[i].opt = *opts[i].mopt;
1660 Config.GpuListWalking = menu_iopts[AMO_GPUL] - 1;
1665 // ------------ options menu ------------
1667 static int mh_restore_defaults(int id, int keys)
1669 menu_set_defconfig();
1670 menu_update_msg("defaults restored");
1674 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1675 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1677 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1678 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1679 "loading state or both";
1681 static const char h_restore_def[] = "Switches back to default / recommended\n"
1683 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1685 static menu_entry e_menu_options[] =
1687 // mee_range ("Save slot", 0, state_slot, 0, 9),
1688 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1689 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1690 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1691 mee_enum ("Region", 0, region, men_region),
1692 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1694 mee_onoff ("Use C64x DSP for sound", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1696 mee_onoff ("Threaded SPU", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1698 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1699 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1700 mee_handler ("[Advanced]", menu_loop_adv_options),
1701 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1702 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1703 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1707 static int menu_loop_options(int id, int keys)
1711 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1712 me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1713 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1715 me_loop(e_menu_options, &sel);
1720 // ------------ debug menu ------------
1722 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1724 int w = min(g_menuscreen_w, 1024);
1725 int h = min(g_menuscreen_h, 512);
1726 u16 *d = g_menuscreen_ptr;
1727 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1731 gpuf->ulFreezeVersion = 1;
1732 if (GPU_freeze != NULL)
1733 GPU_freeze(1, gpuf);
1735 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1736 bgr555_to_rgb565(d, s, w * 2);
1738 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1739 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1740 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1741 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1742 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1745 static void debug_menu_loop(void)
1747 int inp, df_x = 0, df_y = 0;
1750 gpuf = malloc(sizeof(*gpuf));
1756 menu_draw_begin(0, 1);
1757 draw_frame_debug(gpuf, df_x, df_y);
1760 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1761 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1762 if (inp & PBTN_MBACK) break;
1763 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1764 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1765 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x -= 2; }
1766 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1772 // --------- memcard manager ---------
1774 static void draw_mc_icon(int dx, int dy, const u16 *s)
1779 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1781 for (y = 0; y < 16; y++, s += 16) {
1782 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1783 for (x = 0; x < 16; x++) {
1785 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1786 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1792 static void draw_mc_bg(void)
1794 McdBlock *blocks1, *blocks2;
1798 blocks1 = malloc(15 * sizeof(blocks1[0]));
1799 blocks2 = malloc(15 * sizeof(blocks1[0]));
1800 if (blocks1 == NULL || blocks2 == NULL)
1803 for (i = 0; i < 15; i++) {
1804 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1805 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1808 menu_draw_begin(1, 1);
1810 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1812 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1816 maxicons = g_menuscreen_h / 32;
1819 row2 = g_menuscreen_w / 2;
1820 for (i = 0; i < maxicons; i++) {
1821 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1822 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1824 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1825 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1828 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1836 static void handle_memcard_sel(void)
1838 strcpy(Config.Mcd1, "none");
1839 if (memcard1_sel != 0)
1840 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1841 strcpy(Config.Mcd2, "none");
1842 if (memcard2_sel != 0)
1843 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1844 LoadMcds(Config.Mcd1, Config.Mcd2);
1848 static menu_entry e_memcard_options[] =
1850 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1851 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1855 static int menu_loop_memcards(int id, int keys)
1861 memcard1_sel = memcard2_sel = 0;
1862 p = strrchr(Config.Mcd1, '/');
1864 for (i = 0; memcards[i] != NULL; i++)
1865 if (strcmp(p + 1, memcards[i]) == 0)
1866 { memcard1_sel = i; break; }
1867 p = strrchr(Config.Mcd2, '/');
1869 for (i = 0; memcards[i] != NULL; i++)
1870 if (strcmp(p + 1, memcards[i]) == 0)
1871 { memcard2_sel = i; break; }
1873 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1875 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1880 // ------------ cheats menu ------------
1882 static void draw_cheatlist(int sel)
1884 int max_cnt, start, i, pos, active;
1886 max_cnt = g_menuscreen_h / me_sfont_h;
1887 start = max_cnt / 2 - sel;
1889 menu_draw_begin(1, 1);
1891 for (i = 0; i < NumCheats; i++) {
1893 if (pos < 0) continue;
1894 if (pos >= max_cnt) break;
1895 active = Cheats[i].Enabled;
1896 smalltext_out16(14, pos * me_sfont_h,
1897 active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1898 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1899 Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1903 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1905 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1909 static void menu_loop_cheats(void)
1911 static int menu_sel = 0;
1916 draw_cheatlist(menu_sel);
1917 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1918 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1919 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1920 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1921 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1922 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1923 if (inp & PBTN_MOK) { // action
1924 if (menu_sel < NumCheats)
1925 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1928 if (inp & PBTN_MBACK)
1933 // --------- main menu help ----------
1935 static void menu_bios_warn(void)
1938 static const char msg[] =
1939 "You don't seem to have copied any BIOS\n"
1941 MENU_BIOS_PATH "\n\n"
1943 "While many games work fine with fake\n"
1944 "(HLE) BIOS, others (like MGS and FF8)\n"
1945 "require BIOS to work.\n"
1946 "After copying the file, you'll also need\n"
1947 "to select it in the emu's menu:\n"
1948 "options->[BIOS/Plugins]\n\n"
1949 "The file is usually named SCPH1001.BIN,\n"
1950 "but other not compressed files can be\n"
1952 "Press %s or %s to continue";
1953 char tmp_msg[sizeof(msg) + 64];
1955 snprintf(tmp_msg, sizeof(tmp_msg), msg,
1956 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1959 draw_menu_message(tmp_msg, NULL);
1961 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
1962 if (inp & (PBTN_MBACK|PBTN_MOK))
1967 // ------------ main menu ------------
1969 static menu_entry e_menu_main[];
1971 static void draw_frame_main(void)
1980 if (CdromId[0] != 0) {
1981 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1982 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1983 Config.HLE ? "HLE" : "BIOS");
1984 smalltext_out16(4, 1, buff, 0x105f);
1988 capacity = plat_target_bat_capacity_get();
1990 tmp = localtime(<ime);
1991 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1992 if (capacity >= 0) {
1993 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1998 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
2002 static void draw_frame_credits(void)
2004 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
2007 static const char credits_text[] =
2009 "(C) 1999-2003 PCSX Team\n"
2010 "(C) 2005-2009 PCSX-df Team\n"
2011 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
2012 "ARM recompiler (C) 2009-2011 Ari64\n"
2013 #ifdef BUILTIN_GPU_NEON
2014 "ARM NEON GPU (c) 2011-2012 Exophase\n"
2016 "PEOpS GPU and SPU by Pete Bernert\n"
2017 " and the P.E.Op.S. team\n"
2018 "PCSX4ALL plugin by PCSX4ALL team\n"
2019 " Chui, Franxis, Unai\n\n"
2020 "integration, optimization and\n"
2021 " frontend (C) 2010-2015 notaz\n";
2023 static int reset_game(void)
2026 if (bios_sel == 0 && !Config.HLE)
2033 if (LoadCdrom() == -1)
2039 static int reload_plugins(const char *cdimg)
2045 set_cd_image(cdimg);
2047 pcnt_hook_plugins();
2049 if (OpenPlugins() == -1) {
2050 menu_update_msg("failed to open plugins");
2053 plugin_call_rearmed_cbs();
2055 cdrIsoMultidiskCount = 1;
2057 CdromLabel[0] = '\0';
2062 static int run_bios(void)
2064 boolean origSlowBoot = Config.SlowBoot;
2070 if (reload_plugins(NULL) != 0)
2072 Config.SlowBoot = 1;
2074 Config.SlowBoot = origSlowBoot;
2080 static int run_exe(void)
2082 const char *exts[] = { "exe", NULL };
2085 fname = menu_loop_romsel(last_selected_fname,
2086 sizeof(last_selected_fname), exts, NULL);
2091 if (reload_plugins(NULL) != 0)
2095 if (Load(fname) != 0) {
2096 menu_update_msg("exe load failed, bad file?");
2105 static int run_cd_image(const char *fname)
2107 int autoload_state = g_autostateld_opt;
2110 reload_plugins(fname);
2112 // always autodetect, menu_sync_config will override as needed
2115 if (CheckCdrom() == -1) {
2116 // Only check the CD if we are starting the console with a CD
2118 menu_update_msg("unsupported/invalid CD image");
2124 // Read main executable directly from CDRom and start it
2125 if (LoadCdrom() == -1) {
2127 menu_update_msg("failed to load CD image");
2134 if (autoload_state) {
2135 unsigned int newest = 0;
2136 int time, slot, newest_slot = -1;
2138 for (slot = 0; slot < 10; slot++) {
2139 if (emu_check_save_file(slot, &time)) {
2140 if ((unsigned int)time > newest) {
2147 if (newest_slot >= 0) {
2148 lprintf("autoload slot %d\n", newest_slot);
2149 emu_load_state(newest_slot);
2152 lprintf("no save to autoload.\n");
2159 static int romsel_run(void)
2161 int prev_gpu, prev_spu;
2164 fname = menu_loop_romsel(last_selected_fname,
2165 sizeof(last_selected_fname), filter_exts,
2166 optional_cdimg_filter);
2170 printf("selected file: %s\n", fname);
2172 new_dynarec_clear_full();
2174 if (run_cd_image(fname) != 0)
2177 prev_gpu = gpu_plugsel;
2178 prev_spu = spu_plugsel;
2179 if (menu_load_config(1) != 0)
2180 menu_load_config(0);
2182 // check for plugin changes, have to repeat
2183 // loading if game config changed plugins to reload them
2184 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2185 printf("plugin change detected, reloading plugins..\n");
2186 if (run_cd_image(fname) != 0)
2190 strcpy(last_selected_fname, fname);
2191 menu_do_last_cd_img(0);
2195 static int swap_cd_image(void)
2199 fname = menu_loop_romsel(last_selected_fname,
2200 sizeof(last_selected_fname), filter_exts,
2201 optional_cdimg_filter);
2205 printf("selected file: %s\n", fname);
2208 CdromLabel[0] = '\0';
2210 set_cd_image(fname);
2211 if (ReloadCdromPlugin() < 0) {
2212 menu_update_msg("failed to load cdr plugin");
2215 if (CDR_open() < 0) {
2216 menu_update_msg("failed to open cdr plugin");
2220 SetCdOpenCaseTime(time(NULL) + 2);
2223 strcpy(last_selected_fname, fname);
2227 static int swap_cd_multidisk(void)
2229 cdrIsoMultidiskSelect++;
2231 CdromLabel[0] = '\0';
2234 if (CDR_open() < 0) {
2235 menu_update_msg("failed to open cdr plugin");
2239 SetCdOpenCaseTime(time(NULL) + 2);
2245 static void load_pcsx_cht(void)
2247 static const char *exts[] = { "cht", NULL };
2251 fname = menu_loop_romsel(last_selected_fname,
2252 sizeof(last_selected_fname), exts, NULL);
2256 printf("selected cheat file: %s\n", fname);
2259 if (NumCheats == 0 && NumCodes == 0)
2260 menu_update_msg("failed to load cheats");
2262 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2263 menu_update_msg(msg);
2265 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2268 static int main_menu_handler(int id, int keys)
2272 case MA_MAIN_RESUME_GAME:
2276 case MA_MAIN_SAVE_STATE:
2278 return menu_loop_savestate(0);
2280 case MA_MAIN_LOAD_STATE:
2282 return menu_loop_savestate(1);
2284 case MA_MAIN_RESET_GAME:
2285 if (ready_to_go && reset_game() == 0)
2288 case MA_MAIN_LOAD_ROM:
2289 if (romsel_run() == 0)
2292 case MA_MAIN_SWAP_CD:
2293 if (swap_cd_image() == 0)
2296 case MA_MAIN_SWAP_CD_MULTI:
2297 if (swap_cd_multidisk() == 0)
2300 case MA_MAIN_RUN_BIOS:
2301 if (run_bios() == 0)
2304 case MA_MAIN_RUN_EXE:
2308 case MA_MAIN_CHEATS:
2311 case MA_MAIN_LOAD_CHEATS:
2314 case MA_MAIN_CREDITS:
2315 draw_menu_message(credits_text, draw_frame_credits);
2316 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2319 emu_core_ask_exit();
2322 lprintf("%s: something unknown selected\n", __FUNCTION__);
2329 static menu_entry e_menu_main2[] =
2331 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2332 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2333 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2334 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2335 mee_handler ("Memcard manager", menu_loop_memcards),
2336 mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS, main_menu_handler),
2340 static int main_menu2_handler(int id, int keys)
2344 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2345 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2346 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2347 me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2349 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2352 static const char h_extra[] = "Change CD, manage memcards..\n";
2354 static menu_entry e_menu_main[] =
2358 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2359 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2360 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2361 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2362 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2363 mee_handler ("Options", menu_loop_options),
2364 mee_handler ("Controls", menu_loop_keyconfig),
2365 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
2366 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2367 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2368 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2372 // ----------------------------
2374 static void menu_leave_emu(void);
2376 void menu_loop(void)
2378 static int warned_about_bios = 0;
2383 if (config_save_counter == 0) {
2385 if (bioses[1] != NULL) {
2386 // autoselect BIOS to make user's life easier
2387 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2390 else if (!warned_about_bios) {
2392 warned_about_bios = 1;
2396 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2397 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2398 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2399 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2400 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2402 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2405 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2406 } while (!ready_to_go && !g_emu_want_quit);
2408 /* wait until menu, ok, back is released */
2409 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2412 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2417 static int qsort_strcmp(const void *p1, const void *p2)
2419 char * const *s1 = (char * const *)p1;
2420 char * const *s2 = (char * const *)p2;
2421 return strcasecmp(*s1, *s2);
2424 static void scan_bios_plugins(void)
2426 char fname[MAXPATHLEN];
2428 int bios_i, gpu_i, spu_i, mc_i;
2433 gpu_plugins[0] = "builtin_gpu";
2434 spu_plugins[0] = "builtin_spu";
2435 memcards[0] = "(none)";
2436 bios_i = gpu_i = spu_i = mc_i = 1;
2438 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2439 dir = opendir(fname);
2441 perror("scan_bios_plugins bios opendir");
2456 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2459 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2460 if (stat(fname, &st) != 0
2461 || (st.st_size != 512*1024 && st.st_size != 4*1024*1024)) {
2462 printf("bad BIOS file: %s\n", ent->d_name);
2466 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2467 bioses[bios_i++] = strdup(ent->d_name);
2471 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2477 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2478 dir = opendir(fname);
2480 perror("scan_bios_plugins plugins opendir");
2494 p = strstr(ent->d_name, ".so");
2498 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2499 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2501 fprintf(stderr, "%s\n", dlerror());
2505 // now what do we have here?
2506 tmp = dlsym(h, "GPUinit");
2509 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2510 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2514 tmp = dlsym(h, "SPUinit");
2517 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2518 spu_plugins[spu_i++] = strdup(ent->d_name);
2522 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2529 dir = opendir("." MEMCARD_DIR);
2531 perror("scan_bios_plugins memcards opendir");
2546 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2549 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2550 if (stat(fname, &st) != 0) {
2551 printf("bad memcard file: %s\n", ent->d_name);
2555 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2556 memcards[mc_i++] = strdup(ent->d_name);
2560 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2564 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2569 void menu_init(void)
2571 char buff[MAXPATHLEN];
2574 cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2576 scan_bios_plugins();
2579 menu_set_defconfig();
2580 menu_load_config(0);
2581 menu_do_last_cd_img(1);
2586 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2587 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2588 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2589 fprintf(stderr, "OOM\n");
2593 emu_make_path(buff, "skin/background.png", sizeof(buff));
2594 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2596 i = plat_target.cpu_clock_set != NULL
2597 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2598 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, i);
2600 i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2601 e_menu_gfx_options[i].data = plat_target.vout_methods;
2602 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2603 plat_target.vout_methods != NULL);
2605 i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2606 e_menu_gfx_options[i].data = plat_target.hwfilters;
2607 me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2608 plat_target.hwfilters != NULL);
2610 me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2611 plat_target.gamma_set != NULL);
2613 #ifdef HAVE_PRE_ARMV7
2614 me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2616 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2617 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2618 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2619 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2620 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2621 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2622 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2625 void menu_notify_mode_change(int w, int h, int bpp)
2629 last_vout_bpp = bpp;
2632 static void menu_leave_emu(void)
2634 if (GPU_close != NULL) {
2635 int ret = GPU_close();
2637 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2640 plat_video_menu_enter(ready_to_go);
2642 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2643 if (pl_vout_buf != NULL && ready_to_go) {
2644 int x = max(0, g_menuscreen_w - last_vout_w);
2645 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2646 int w = min(g_menuscreen_w, last_vout_w);
2647 int h = min(g_menuscreen_h, last_vout_h);
2648 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2649 char *s = pl_vout_buf;
2651 if (last_vout_bpp == 16) {
2652 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2653 menu_darken_bg(d, s, w, 0);
2656 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2657 rgb888_to_rgb565(d, s, w * 3);
2658 menu_darken_bg(d, d, w, 0);
2664 cpu_clock = plat_target_cpu_clock_get();
2667 void menu_prepare_emu(void)
2669 R3000Acpu *prev_cpu = psxCpu;
2671 plat_video_menu_leave();
2673 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
2674 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2678 if (psxCpu != prev_cpu) {
2679 prev_cpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
2680 prev_cpu->Shutdown();
2683 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
2687 psxCpu->ApplyConfig();
2689 // core doesn't care about Config.Cdda changes,
2690 // so handle them manually here
2695 plat_target_cpu_clock_set(cpu_clock);
2697 // push config to GPU plugin
2698 plugin_call_rearmed_cbs();
2700 if (GPU_open != NULL) {
2701 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2703 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2707 void menu_update_msg(const char *msg)
2709 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2710 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2712 menu_error_time = plat_get_ticks_ms();
2713 lprintf("msg: %s\n", menu_error_msg);
2716 void menu_finish(void)
2718 if (cpu_clock_st > 0)
2719 plat_target_cpu_clock_set(cpu_clock_st);