2 * (C) GraÅžvydas "notaz" Ignotas, 2010-2015
4 * This work is licensed under the terms of any of these licenses
6 * - GNU GPL, version 2 or later.
7 * - GNU LGPL, version 2.1 or later.
8 * See the COPYING file in the top-level directory.
22 #include <sys/types.h>
31 #include "plugin_lib.h"
35 #include "libpicofe/plat.h"
36 #include "libpicofe/input.h"
37 #include "libpicofe/linux/in_evdev.h"
38 #include "libpicofe/plat.h"
39 #include "../libpcsxcore/misc.h"
40 #include "../libpcsxcore/cdrom.h"
41 #include "../libpcsxcore/cdriso.h"
42 #include "../libpcsxcore/cheat.h"
43 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
44 #include "../plugins/dfsound/spu_config.h"
45 #include "psemu_plugin_defs.h"
46 #include "arm_features.h"
49 #define REARMED_BIRTHDAY_TIME 1293306830 /* 25 Dec 2010 */
51 #define array_size(x) (sizeof(x) / sizeof(x[0]))
62 MA_MAIN_SWAP_CD_MULTI,
93 MA_OPT_SCANLINE_LEVEL,
97 static int last_vout_w, last_vout_h, last_vout_bpp;
98 static int cpu_clock, cpu_clock_st, volume_boost;
99 static int frameskip = 1; // 0 - auto, 1 - off
100 static char last_selected_fname[MAXPATHLEN];
101 static int config_save_counter, region, in_type_sel1, in_type_sel2;
102 static int psx_clock;
103 static int memcard1_sel = -1, memcard2_sel = -1;
104 extern int g_autostateld_opt;
105 static int menu_iopts[8];
106 int g_opts, g_scaler, g_gamma = 100;
107 int scanlines, scanline_level = 20;
108 int soft_scaling, analog_deadzone; // for Caanoo
111 #ifndef HAVE_PRE_ARMV7
112 #define DEFAULT_PSX_CLOCK (10000 / CYCLE_MULT_DEFAULT)
113 #define DEFAULT_PSX_CLOCK_S "57"
115 #define DEFAULT_PSX_CLOCK 50
116 #define DEFAULT_PSX_CLOCK_S "50"
119 static const char *bioses[32];
120 static const char *gpu_plugins[16];
121 static const char *spu_plugins[16];
122 static const char *memcards[32];
123 static int bios_sel, gpu_plugsel, spu_plugsel;
125 #ifndef UI_FEATURES_H
126 #define MENU_BIOS_PATH "bios/"
127 #define MENU_SHOW_VARSCALER 0
128 #define MENU_SHOW_VOUTMODE 1
129 #define MENU_SHOW_SCALER2 0
130 #define MENU_SHOW_NUBS_BTNS 0
131 #define MENU_SHOW_VIBRATION 0
132 #define MENU_SHOW_DEADZONE 0
133 #define MENU_SHOW_MINIMIZE 0
134 #define MENU_SHOW_FULLSCREEN 1
135 #define MENU_SHOW_VOLUME 0
138 static int min(int x, int y) { return x < y ? x : y; }
139 static int max(int x, int y) { return x > y ? x : y; }
141 void emu_make_path(char *buff, const char *end, int size)
145 end_len = strlen(end);
146 pos = plat_get_root_dir(buff, size);
147 strncpy(buff + pos, end, size - pos);
149 if (pos + end_len > size - 1)
150 printf("Warning: path truncated: %s\n", buff);
153 static int emu_check_save_file(int slot, int *time)
155 char fname[MAXPATHLEN];
159 ret = emu_check_state(slot);
160 if (ret != 0 || time == NULL)
161 return ret == 0 ? 1 : 0;
163 ret = get_state_filename(fname, sizeof(fname), slot);
167 ret = stat(fname, &status);
171 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
172 return 1; // probably bad rtc like on some Caanoos
174 *time = status.st_mtime;
179 static int emu_save_load_game(int load, int unused)
184 ret = emu_load_state(state_slot);
186 // reflect hle/bios mode from savestate
189 else if (bios_sel == 0 && bioses[1] != NULL)
190 // XXX: maybe find the right bios instead
194 ret = emu_save_state(state_slot);
199 static void rm_namelist_entry(struct dirent **namelist,
200 int count, const char *name)
204 for (i = 1; i < count; i++) {
205 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
208 if (strcmp(name, namelist[i]->d_name) == 0) {
216 static int optional_cdimg_filter(struct dirent **namelist, int count,
220 char buf[256], buf2[257];
221 int i, d, ret, good_cue;
228 for (i = 1; i < count; i++) {
229 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
232 ext = strrchr(namelist[i]->d_name, '.');
234 // should not happen but whatever
241 // first find .cue files and remove files they reference
242 if (strcasecmp(ext, "cue") == 0)
244 snprintf(buf, sizeof(buf), "%s/%s", basedir,
245 namelist[i]->d_name);
255 while (fgets(buf, sizeof(buf), f)) {
256 ret = sscanf(buf, " FILE \"%256[^\"]\"", buf2);
258 ret = sscanf(buf, " FILE %256s", buf2);
262 p = strrchr(buf2, '/');
264 p = strrchr(buf2, '\\');
270 snprintf(buf, sizeof(buf), "%s/%s", basedir, p);
271 ret = STAT(buf, &statf);
273 rm_namelist_entry(namelist, count, p);
286 p = strcasestr(namelist[i]->d_name, "track");
288 ret = strtoul(p + 5, NULL, 10);
298 for (i = d = 1; i < count; i++)
299 if (namelist[i] != NULL)
300 namelist[d++] = namelist[i];
305 // propagate menu settings to the emu vars
306 static void menu_sync_config(void)
308 static int allow_abs_only_old;
313 Config.PsxType = region - 1;
315 Config.cycle_multiplier = 10000 / psx_clock;
317 switch (in_type_sel1) {
318 case 1: in_type[0] = PSE_PAD_TYPE_ANALOGPAD; break;
319 case 2: in_type[0] = PSE_PAD_TYPE_GUNCON; break;
320 case 3: in_type[0] = PSE_PAD_TYPE_GUN; break;
321 case 4: in_type[0] = PSE_PAD_TYPE_NONE; break;
322 default: in_type[0] = PSE_PAD_TYPE_STANDARD;
324 switch (in_type_sel2) {
325 case 1: in_type[1] = PSE_PAD_TYPE_ANALOGPAD; break;
326 case 2: in_type[1] = PSE_PAD_TYPE_GUNCON; break;
327 case 3: in_type[1] = PSE_PAD_TYPE_GUN; break;
328 case 4: in_type[1] = PSE_PAD_TYPE_NONE; break;
329 default: in_type[1] = PSE_PAD_TYPE_STANDARD;
331 if (in_evdev_allow_abs_only != allow_abs_only_old) {
333 allow_abs_only_old = in_evdev_allow_abs_only;
336 spu_config.iVolume = 768 + 128 * volume_boost;
337 pl_rearmed_cbs.frameskip = frameskip - 1;
338 pl_timing_prepare(Config.PsxType);
341 static void menu_set_defconfig(void)
343 emu_set_default_config();
346 g_scaler = SCALE_4_3;
349 frameskip = 1; // 1 - off
350 analog_deadzone = 50;
355 plat_target.vout_fullscreen = 0;
356 psx_clock = DEFAULT_PSX_CLOCK;
359 in_type_sel1 = in_type_sel2 = 0;
360 in_evdev_allow_abs_only = 0;
365 #define CE_CONFIG_STR(val) \
366 { #val, 0, Config.val }
368 #define CE_CONFIG_VAL(val) \
369 { #val, sizeof(Config.val), &Config.val }
371 #define CE_STR(val) \
374 #define CE_INTVAL(val) \
375 { #val, sizeof(val), &val }
377 #define CE_INTVAL_N(name, val) \
378 { name, sizeof(val), &val }
380 #define CE_INTVAL_P(val) \
381 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
383 // 'versioned' var, used when defaults change
384 #define CE_CONFIG_STR_V(val, ver) \
385 { #val #ver, 0, Config.val }
387 #define CE_INTVAL_V(val, ver) \
388 { #val #ver, sizeof(val), &val }
390 #define CE_INTVAL_PV(val, ver) \
391 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
393 static const struct {
399 CE_CONFIG_STR_V(Gpu, 3),
401 // CE_CONFIG_STR(Cdr),
405 CE_CONFIG_VAL(Debug),
406 CE_CONFIG_VAL(PsxOut),
407 CE_CONFIG_VAL(icache_emulation),
408 CE_CONFIG_VAL(DisableStalls),
410 CE_CONFIG_VAL(GpuListWalking),
411 CE_CONFIG_VAL(FractionalFramerate),
412 CE_CONFIG_VAL(PreciseExceptions),
414 CE_INTVAL_V(g_scaler, 3),
416 CE_INTVAL(g_layer_x),
417 CE_INTVAL(g_layer_y),
418 CE_INTVAL(g_layer_w),
419 CE_INTVAL(g_layer_h),
420 CE_INTVAL(soft_filter),
421 CE_INTVAL(scanlines),
422 CE_INTVAL(scanline_level),
423 CE_INTVAL(plat_target.vout_method),
424 CE_INTVAL(plat_target.hwfilter),
425 CE_INTVAL(plat_target.vout_fullscreen),
426 CE_INTVAL(state_slot),
427 CE_INTVAL(cpu_clock),
429 CE_INTVAL(in_type_sel1),
430 CE_INTVAL(in_type_sel2),
431 CE_INTVAL(analog_deadzone),
432 CE_INTVAL(memcard1_sel),
433 CE_INTVAL(memcard2_sel),
434 CE_INTVAL(g_autostateld_opt),
435 CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
436 CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
437 CE_INTVAL_V(frameskip, 4),
438 CE_INTVAL_P(gpu_peops.iUseDither),
439 CE_INTVAL_P(gpu_peops.dwActFixes),
440 CE_INTVAL_P(gpu_unai_old.lineskip),
441 CE_INTVAL_P(gpu_unai_old.abe_hack),
442 CE_INTVAL_P(gpu_unai_old.no_light),
443 CE_INTVAL_P(gpu_unai_old.no_blend),
444 CE_INTVAL_P(gpu_unai.ilace_force),
445 CE_INTVAL_P(gpu_unai.pixel_skip),
446 CE_INTVAL_P(gpu_unai.lighting),
447 CE_INTVAL_P(gpu_unai.fast_lighting),
448 CE_INTVAL_P(gpu_unai.blending),
449 CE_INTVAL_P(gpu_unai.dithering),
450 CE_INTVAL_P(gpu_unai.scale_hires),
451 CE_INTVAL_P(gpu_neon.allow_interlace),
452 CE_INTVAL_P(gpu_neon.enhancement_enable),
453 CE_INTVAL_P(gpu_neon.enhancement_no_main),
454 CE_INTVAL_P(gpu_neon.enhancement_tex_adj),
455 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
456 CE_INTVAL_P(gpu_peopsgl.iFilterType),
457 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
458 CE_INTVAL_P(gpu_peopsgl.iUseMask),
459 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
460 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
461 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
462 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
463 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
464 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
465 CE_INTVAL_P(screen_centering_type),
466 CE_INTVAL_P(screen_centering_x),
467 CE_INTVAL_P(screen_centering_y),
468 CE_INTVAL(spu_config.iUseReverb),
469 CE_INTVAL(spu_config.iXAPitch),
470 CE_INTVAL(spu_config.iUseInterpolation),
471 CE_INTVAL(spu_config.iTempo),
472 CE_INTVAL(spu_config.iUseThread),
473 CE_INTVAL(config_save_counter),
474 CE_INTVAL(in_evdev_allow_abs_only),
475 CE_INTVAL(volume_boost),
476 CE_INTVAL(psx_clock),
477 CE_INTVAL(new_dynarec_hacks),
478 CE_INTVAL(in_enable_vibration),
481 static char *get_cd_label(void)
483 static char trimlabel[33];
486 strncpy(trimlabel, CdromLabel, 32);
488 for (j = 31; j >= 0; j--)
489 if (trimlabel[j] == ' ')
495 static void make_cfg_fname(char *buf, size_t size, int is_game)
498 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
500 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
503 static void keys_write_all(FILE *f);
504 static char *mystrip(char *str);
506 static void write_u32_value(FILE *f, u32 v)
510 fprintf(f, "%x\n", v);
513 static int menu_write_config(int is_game)
515 char cfgfile[MAXPATHLEN];
519 config_save_counter++;
521 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
522 f = fopen(cfgfile, "w");
524 printf("menu_write_config: failed to open: %s\n", cfgfile);
528 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
529 fprintf(f, "%s = ", config_data[i].name);
530 switch (config_data[i].len) {
532 fprintf(f, "%s\n", (char *)config_data[i].val);
535 write_u32_value(f, *(u8 *)config_data[i].val);
538 write_u32_value(f, *(u16 *)config_data[i].val);
541 write_u32_value(f, *(u32 *)config_data[i].val);
544 printf("menu_write_config: unhandled len %d for %s\n",
545 (int)config_data[i].len, config_data[i].name);
556 static int menu_do_last_cd_img(int is_get)
558 static const char *defaults[] = { "/media", "/mnt/sd", "/mnt" };
564 snprintf(path, sizeof(path), "." PCSX_DOT_DIR "lastcdimg.txt");
565 f = fopen(path, is_get ? "r" : "w");
572 ret = fread(last_selected_fname, 1, sizeof(last_selected_fname) - 1, f);
573 last_selected_fname[ret] = 0;
574 mystrip(last_selected_fname);
577 fprintf(f, "%s\n", last_selected_fname);
582 for (i = 0; last_selected_fname[0] == 0
583 || STAT(last_selected_fname, &st) != 0; i++)
585 if (i >= ARRAY_SIZE(defaults))
587 strcpy(last_selected_fname, defaults[i]);
594 static void parse_str_val(char *cval, const char *src)
597 strncpy(cval, src, MAXPATHLEN);
598 cval[MAXPATHLEN - 1] = 0;
599 tmp = strchr(cval, '\n');
601 tmp = strchr(cval, '\r');
606 static void keys_load_all(const char *cfg);
608 int menu_load_config(int is_game)
610 char cfgfile[MAXPATHLEN];
616 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
617 f = fopen(cfgfile, "r");
619 printf("menu_load_config: failed to open: %s\n", cfgfile);
623 fseek(f, 0, SEEK_END);
626 printf("bad size %ld: %s\n", size, cfgfile);
630 cfg = malloc(size + 1);
634 fseek(f, 0, SEEK_SET);
635 if (fread(cfg, 1, size, f) != size) {
636 printf("failed to read: %s\n", cfgfile);
641 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
645 tmp = strstr(cfg, config_data[i].name);
648 tmp += strlen(config_data[i].name);
649 if (strncmp(tmp, " = ", 3) != 0)
653 if (config_data[i].len == 0) {
654 parse_str_val(config_data[i].val, tmp);
659 val = strtoul(tmp, &tmp2, 16);
660 if (tmp2 == NULL || tmp == tmp2)
661 continue; // parse failed
663 switch (config_data[i].len) {
665 *(u8 *)config_data[i].val = val;
668 *(u16 *)config_data[i].val = val;
671 *(u32 *)config_data[i].val = val;
674 printf("menu_load_config: unhandled len %d for %s\n",
675 (int)config_data[i].len, config_data[i].name);
681 char *tmp = strstr(cfg, "lastcdimg = ");
684 parse_str_val(last_selected_fname, tmp);
699 for (i = bios_sel = 0; bioses[i] != NULL; i++)
700 if (strcmp(Config.Bios, bioses[i]) == 0)
701 { bios_sel = i; break; }
703 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
704 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
705 { gpu_plugsel = i; break; }
707 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
708 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
709 { spu_plugsel = i; break; }
711 // memcard selections
712 char mcd1_old[sizeof(Config.Mcd1)];
713 char mcd2_old[sizeof(Config.Mcd2)];
714 strcpy(mcd1_old, Config.Mcd1);
715 strcpy(mcd2_old, Config.Mcd2);
717 if ((unsigned int)memcard1_sel < ARRAY_SIZE(memcards)) {
718 if (memcard1_sel == 0)
719 strcpy(Config.Mcd1, "none");
720 else if (memcards[memcard1_sel] != NULL)
721 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s",
722 MEMCARD_DIR, memcards[memcard1_sel]);
724 if ((unsigned int)memcard2_sel < ARRAY_SIZE(memcards)) {
725 if (memcard2_sel == 0)
726 strcpy(Config.Mcd2, "none");
727 else if (memcards[memcard2_sel] != NULL)
728 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s",
729 MEMCARD_DIR, memcards[memcard2_sel]);
731 if (strcmp(mcd1_old, Config.Mcd1) || strcmp(mcd2_old, Config.Mcd2))
732 LoadMcds(Config.Mcd1, Config.Mcd2);
737 static const char *filter_exts[] = {
738 "bin", "img", "mdf", "iso", "cue", "z",
742 "bz", "znx", "pbp", "cbn", NULL
745 // rrrr rggg gggb bbbb
746 static unsigned short fname2color(const char *fname)
748 static const char *other_exts[] = {
749 "ccd", "toc", "mds", "sub", "table", "index", "sbi"
751 const char *ext = strrchr(fname, '.');
757 for (i = 0; filter_exts[i] != NULL; i++)
758 if (strcasecmp(ext, filter_exts[i]) == 0)
760 for (i = 0; i < array_size(other_exts); i++)
761 if (strcasecmp(ext, other_exts[i]) == 0)
766 static void draw_savestate_bg(int slot);
768 #define MENU_ALIGN_LEFT
769 #ifndef HAVE_PRE_ARMV7 // assume hires device
775 #include "libpicofe/menu.c"
777 // a bit of black magic here
778 static void draw_savestate_bg(int slot)
780 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
782 char fname[MAXPATHLEN];
789 ret = get_state_filename(fname, sizeof(fname), slot);
793 f = gzopen(fname, "rb");
797 if ((ret = (int)gzseek(f, 0x29933d, SEEK_SET)) != 0x29933d) {
798 fprintf(stderr, "gzseek failed: %d\n", ret);
803 gpu = malloc(sizeof(*gpu));
809 ret = gzread(f, gpu, sizeof(*gpu));
811 if (ret != sizeof(*gpu)) {
812 fprintf(stderr, "gzread failed\n");
816 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
818 if (gpu->ulStatus & 0x800000)
819 goto out; // disabled
821 x = gpu->ulControl[5] & 0x3ff;
822 y = (gpu->ulControl[5] >> 10) & 0x1ff;
823 w = psx_widths[(gpu->ulStatus >> 16) & 7];
824 tmp = gpu->ulControl[7];
825 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
826 if (gpu->ulStatus & 0x80000) // doubleheight
828 if (h <= 0 || h > 512)
834 s = (u16 *)gpu->psxVRam + y * 1024 + x;
836 x = max(0, g_menuscreen_w - w) & ~3;
837 y = max(0, g_menuscreen_h / 2 - h / 2);
838 w = min(g_menuscreen_w, w);
839 h = min(g_menuscreen_h, h);
840 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
842 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
843 if (gpu->ulStatus & 0x200000)
844 bgr888_to_rgb565(d, s, w * 3);
846 bgr555_to_rgb565(d, s, w * 2);
848 // darken this so that menu text is visible
849 if (g_menuscreen_w - w < 320)
850 menu_darken_bg(d, d, w, 0);
857 // -------------- key config --------------
859 me_bind_action me_ctrl_actions[] =
861 { "UP ", 1 << DKEY_UP},
862 { "DOWN ", 1 << DKEY_DOWN },
863 { "LEFT ", 1 << DKEY_LEFT },
864 { "RIGHT ", 1 << DKEY_RIGHT },
865 { "TRIANGLE", 1 << DKEY_TRIANGLE },
866 { "CIRCLE ", 1 << DKEY_CIRCLE },
867 { "CROSS ", 1 << DKEY_CROSS },
868 { "SQUARE ", 1 << DKEY_SQUARE },
869 { "L1 ", 1 << DKEY_L1 },
870 { "R1 ", 1 << DKEY_R1 },
871 { "L2 ", 1 << DKEY_L2 },
872 { "R2 ", 1 << DKEY_R2 },
873 { "L3 ", 1 << DKEY_L3 },
874 { "R3 ", 1 << DKEY_R3 },
875 { "START ", 1 << DKEY_START },
876 { "SELECT ", 1 << DKEY_SELECT },
880 me_bind_action emuctrl_actions[] =
882 { "Save State ", 1 << SACTION_SAVE_STATE },
883 { "Load State ", 1 << SACTION_LOAD_STATE },
884 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
885 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
886 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
887 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
888 { "Show/Hide FPS ", 1 << SACTION_TOGGLE_FPS },
889 #ifndef HAVE_PRE_ARMV7
890 { "Switch Renderer ", 1 << SACTION_SWITCH_DISPMODE },
892 { "Fast Forward ", 1 << SACTION_FAST_FORWARD },
893 #if MENU_SHOW_MINIMIZE
894 { "Minimize ", 1 << SACTION_MINIMIZE },
896 #if MENU_SHOW_FULLSCREEN
897 { "Toggle fullscreen", 1 << SACTION_TOGGLE_FULLSCREEN },
899 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
900 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
901 { "Gun A button ", 1 << SACTION_GUN_A },
902 { "Gun B button ", 1 << SACTION_GUN_B },
903 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
905 { "Volume Up ", 1 << SACTION_VOLUME_UP },
906 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
908 { "Analog toggle ", 1 << SACTION_ANALOG_TOGGLE },
912 static char *mystrip(char *str)
917 for (i = 0; i < len; i++)
918 if (str[i] != ' ') break;
919 if (i > 0) memmove(str, str + i, len - i + 1);
922 for (i = len - 1; i >= 0; i--)
923 if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
929 static void get_line(char *d, size_t size, const char *s)
934 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
943 static void keys_write_all(FILE *f)
947 for (d = 0; d < IN_MAX_DEVS; d++)
949 const int *binds = in_get_dev_binds(d);
950 const char *name = in_get_dev_name(d, 0, 0);
953 if (binds == NULL || name == NULL)
956 fprintf(f, "binddev = %s\n", name);
957 in_get_config(d, IN_CFG_BIND_COUNT, &count);
959 for (k = 0; k < count; k++)
964 act[0] = act[31] = 0;
965 name = in_get_key_name(d, k);
967 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
968 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
969 mask = me_ctrl_actions[i].mask;
971 strncpy(act, me_ctrl_actions[i].name, 31);
972 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
975 mask = me_ctrl_actions[i].mask << 16;
977 strncpy(act, me_ctrl_actions[i].name, 31);
978 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
983 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
984 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
985 mask = emuctrl_actions[i].mask;
987 strncpy(act, emuctrl_actions[i].name, 31);
988 fprintf(f, "bind %s = %s\n", name, mystrip(act));
994 for (k = 0; k < array_size(in_adev); k++)
997 fprintf(f, "bind_analog = %d\n", k);
1002 static int parse_bind_val(const char *val, int *type)
1006 *type = IN_BINDTYPE_NONE;
1010 if (strncasecmp(val, "player", 6) == 0)
1012 int player, shift = 0;
1013 player = atoi(val + 6) - 1;
1015 if ((unsigned int)player > 1)
1020 *type = IN_BINDTYPE_PLAYER12;
1021 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
1022 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
1023 return me_ctrl_actions[i].mask << shift;
1026 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
1027 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
1028 *type = IN_BINDTYPE_EMU;
1029 return emuctrl_actions[i].mask;
1036 static void keys_load_all(const char *cfg)
1038 char dev[256], key[128], *act;
1044 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
1047 // don't strip 'dev' because there are weird devices
1048 // with names with space at the end
1049 get_line(dev, sizeof(dev), p);
1051 dev_id = in_config_parse_dev(dev);
1053 printf("input: can't handle dev: %s\n", dev);
1057 in_unbind_all(dev_id, -1, -1);
1058 while ((p = strstr(p, "bind"))) {
1059 if (strncmp(p, "binddev = ", 10) == 0)
1062 if (strncmp(p, "bind_analog", 11) == 0) {
1063 ret = sscanf(p, "bind_analog = %d", &bind);
1066 printf("input: parse error: %16s..\n", p);
1069 if ((unsigned int)bind >= array_size(in_adev)) {
1070 printf("input: analog id %d out of range\n", bind);
1073 in_adev[bind] = dev_id;
1079 printf("input: parse error: %16s..\n", p);
1083 get_line(key, sizeof(key), p);
1084 act = strchr(key, '=');
1086 printf("parse failed: %16s..\n", p);
1094 bind = parse_bind_val(act, &bindtype);
1095 if (bind != -1 && bind != 0) {
1096 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
1097 in_config_bind_key(dev_id, key, bind, bindtype);
1100 lprintf("config: unhandled action \"%s\"\n", act);
1106 static int key_config_loop_wrap(int id, int keys)
1109 case MA_CTRL_PLAYER1:
1110 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
1112 case MA_CTRL_PLAYER2:
1113 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
1116 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
1124 static const char h_nubmode[] = "Maps nub-like analog controls to PSX ones better\n"
1125 "Might cause problems with real analog sticks";
1126 static const char *adevnames[IN_MAX_DEVS + 2];
1127 static int stick_sel[2];
1129 static menu_entry e_menu_keyconfig_analog[] =
1131 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
1132 mee_range (" X axis", 0, in_adev_axis[0][0], 0, 7),
1133 mee_range (" Y axis", 0, in_adev_axis[0][1], 0, 7),
1134 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[0], 1, h_nubmode),
1135 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
1136 mee_range (" X axis", 0, in_adev_axis[1][0], 0, 7),
1137 mee_range (" Y axis", 0, in_adev_axis[1][1], 0, 7),
1138 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[1], 1, h_nubmode),
1142 static int key_config_analog(int id, int keys)
1144 int i, d, count, sel = 0;
1145 int sel2dev_map[IN_MAX_DEVS];
1147 memset(adevnames, 0, sizeof(adevnames));
1148 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1149 memset(stick_sel, 0, sizeof(stick_sel));
1151 adevnames[0] = "None";
1153 for (d = 0; d < IN_MAX_DEVS; d++)
1155 const char *name = in_get_dev_name(d, 0, 1);
1160 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1164 if (in_adev[0] == d) stick_sel[0] = i;
1165 if (in_adev[1] == d) stick_sel[1] = i;
1167 adevnames[i++] = name;
1169 adevnames[i] = NULL;
1171 me_loop(e_menu_keyconfig_analog, &sel);
1173 in_adev[0] = sel2dev_map[stick_sel[0]];
1174 in_adev[1] = sel2dev_map[stick_sel[1]];
1179 static const char *mgn_dev_name(int id, int *offs)
1181 const char *name = NULL;
1184 if (id == MA_CTRL_DEV_FIRST)
1187 for (; it < IN_MAX_DEVS; it++) {
1188 name = in_get_dev_name(it, 1, 1);
1197 static const char *mgn_saveloadcfg(int id, int *offs)
1202 static int mh_savecfg(int id, int keys)
1204 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1205 menu_update_msg("config saved");
1207 menu_update_msg("failed to write config");
1212 static int mh_input_rescan(int id, int keys)
1214 //menu_sync_config();
1216 menu_update_msg("rescan complete.");
1221 static const char *men_in_type_sel[] = {
1222 "Standard (SCPH-1080)",
1223 "Analog (SCPH-1150)",
1229 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1230 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1231 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1233 static menu_entry e_menu_keyconfig[] =
1235 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1236 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1237 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
1238 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
1240 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1241 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
1242 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
1243 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
1244 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
1245 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1246 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1247 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1248 mee_handler ("Rescan devices:", mh_input_rescan),
1250 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
1251 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1252 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1253 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1254 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1255 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1256 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1260 static int menu_loop_keyconfig(int id, int keys)
1264 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1265 me_loop(e_menu_keyconfig, &sel);
1269 // ------------ gfx options menu ------------
1271 static const char *men_scaler[] = {
1272 "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL
1274 static const char *men_soft_filter[] = { "None",
1276 "scale2x", "eagle2x",
1279 static const char *men_dummy[] = { NULL };
1280 static const char *men_centering[] = { "Auto", "Ingame", "Borderless", "Force", NULL };
1281 static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
1282 "int. 4:3 - uses integer if possible, else fractional";
1283 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1284 "using d-pad or move it using R+d-pad";
1285 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1286 static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
1288 static const char *men_scanlines[] = { "OFF", "1", "2", "3", NULL };
1289 static const char h_scanline_l[] = "Scanline brightness, 0-100%";
1292 static int menu_loop_cscaler(int id, int keys)
1294 void *saved_layer = NULL;
1295 size_t saved_layer_size = 0;
1296 int was_layer_clipped = 0;
1302 g_scaler = SCALE_CUSTOM;
1303 saved_layer_size = last_vout_w * last_vout_h * last_vout_bpp / 8;
1304 saved_layer = malloc(saved_layer_size);
1306 memcpy(saved_layer, pl_vout_buf, saved_layer_size);
1308 plat_gvideo_open(Config.PsxType);
1310 menu_draw_begin(0, 1);
1311 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1316 if (saved_layer && last_vout_bpp == 16) {
1317 int top_x = max(0, -g_layer_x * last_vout_w / 800) + 1;
1318 int top_y = max(0, -g_layer_y * last_vout_h / 480) + 1;
1320 memcpy(pl_vout_buf, saved_layer, saved_layer_size);
1321 snprintf(text, sizeof(text), "%d,%d %dx%d",
1322 g_layer_x, g_layer_y, g_layer_w, g_layer_h);
1323 basic_text_out16_nf(pl_vout_buf, last_vout_w,
1324 top_x, top_y, text);
1325 basic_text_out16_nf(pl_vout_buf, last_vout_w, 2,
1326 last_vout_h - 20, "d-pad: resize, R+d-pad: move");
1327 pl_vout_buf = plat_gvideo_flip();
1330 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1331 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1332 if (inp & PBTN_UP) g_layer_y--;
1333 if (inp & PBTN_DOWN) g_layer_y++;
1334 if (inp & PBTN_LEFT) g_layer_x--;
1335 if (inp & PBTN_RIGHT) g_layer_x++;
1336 if (!(inp & PBTN_R)) {
1337 if (inp & PBTN_UP) g_layer_h += 2;
1338 if (inp & PBTN_DOWN) g_layer_h -= 2;
1339 if (inp & PBTN_LEFT) g_layer_w += 2;
1340 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1342 if (inp & (PBTN_MOK|PBTN_MBACK))
1345 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1346 int layer_clipped = 0;
1347 g_layer_x = max(-320, min(g_layer_x, 640));
1348 g_layer_y = max(-240, min(g_layer_y, 400));
1349 g_layer_w = max(160, g_layer_w);
1350 g_layer_h = max( 60, g_layer_h);
1351 if (g_layer_x < 0 || g_layer_x + g_layer_w > 800)
1353 if (g_layer_w > 800+400)
1354 g_layer_w = 800+400;
1355 if (g_layer_y < 0 || g_layer_y + g_layer_h > 480)
1357 if (g_layer_h > 480+360)
1358 g_layer_h = 480+360;
1360 plat_gvideo_open(Config.PsxType);
1361 if (layer_clipped || was_layer_clipped)
1362 pl_vout_buf = plat_gvideo_set_mode(&last_vout_w,
1363 &last_vout_h, &last_vout_bpp);
1364 was_layer_clipped = layer_clipped;
1368 plat_gvideo_close();
1374 static menu_entry e_menu_gfx_options[] =
1376 mee_enum ("Screen centering", MA_OPT_CENTERING, pl_rearmed_cbs.screen_centering_type, men_centering),
1377 mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1378 mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1379 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1380 mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1381 mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1383 mee_enum ("Scanlines", MA_OPT_SCANLINES, scanlines, men_scanlines),
1384 mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1386 mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1387 // mee_onoff ("Vsync", 0, vsync, 1),
1388 mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1392 static int menu_loop_gfx_options(int id, int keys)
1396 me_loop(e_menu_gfx_options, &sel);
1401 // ------------ bios/plugins ------------
1403 #ifdef BUILTIN_GPU_NEON
1405 static const char h_gpu_neon[] =
1406 "Configure built-in NEON GPU plugin";
1407 static const char h_gpu_neon_enhanced[] =
1408 "Renders in double resolution at the cost of lower performance\n"
1409 "(not available for high resolution games)";
1410 static const char h_gpu_neon_enhanced_hack[] =
1411 "Speed hack for above option (glitches some games)";
1412 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1414 static menu_entry e_menu_plugin_gpu_neon[] =
1416 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1417 mee_onoff_h ("Enhanced resolution", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1418 mee_onoff_h ("Enhanced res. speed hack", 0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1419 mee_onoff ("Enh. res. texture adjust", 0, pl_rearmed_cbs.gpu_neon.enhancement_tex_adj, 1),
1423 static int menu_loop_plugin_gpu_neon(int id, int keys)
1426 me_loop(e_menu_plugin_gpu_neon, &sel);
1432 static menu_entry e_menu_plugin_gpu_unai_old[] =
1434 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai_old.lineskip, 1),
1435 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai_old.abe_hack, 1),
1436 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai_old.no_light, 1),
1437 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai_old.no_blend, 1),
1441 static int menu_loop_plugin_gpu_unai_old(int id, int keys)
1444 me_loop(e_menu_plugin_gpu_unai_old, &sel);
1448 static menu_entry e_menu_plugin_gpu_unai[] =
1450 mee_onoff ("Interlace", 0, pl_rearmed_cbs.gpu_unai.ilace_force, 1),
1451 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_unai.dithering, 1),
1452 mee_onoff ("Lighting", 0, pl_rearmed_cbs.gpu_unai.lighting, 1),
1453 mee_onoff ("Fast lighting", 0, pl_rearmed_cbs.gpu_unai.fast_lighting, 1),
1454 mee_onoff ("Blending", 0, pl_rearmed_cbs.gpu_unai.blending, 1),
1455 mee_onoff ("Pixel skip", 0, pl_rearmed_cbs.gpu_unai.pixel_skip, 1),
1459 static int menu_loop_plugin_gpu_unai(int id, int keys)
1462 me_loop(e_menu_plugin_gpu_unai, &sel);
1467 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1468 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1469 static const char h_gpu_1[] = "Capcom fighting games";
1470 static const char h_gpu_2[] = "Black screens in Lunar";
1471 static const char h_gpu_3[] = "Compatibility mode";
1472 static const char h_gpu_6[] = "Pandemonium 2";
1473 //static const char h_gpu_7[] = "Skip every second frame";
1474 static const char h_gpu_8[] = "Needed by Dark Forces";
1475 static const char h_gpu_9[] = "better g-colors, worse textures";
1476 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1478 static menu_entry e_menu_plugin_gpu_peops[] =
1480 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1481 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1482 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1483 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1484 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1485 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1486 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1487 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1488 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1489 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1493 static int menu_loop_plugin_gpu_peops(int id, int keys)
1496 me_loop(e_menu_plugin_gpu_peops, &sel);
1500 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1501 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1502 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1504 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1506 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1507 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1508 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1509 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1510 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1511 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1512 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1513 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1514 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1515 mee_label ("Fixes/hacks:"),
1516 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1517 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1518 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1519 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1520 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1521 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1522 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1523 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1524 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1525 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1526 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1530 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1533 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1537 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1538 static const char h_spu_volboost[] = "Large values cause distortion";
1539 static const char h_spu_tempo[] = "Slows down audio if emu is too slow\n"
1540 "This is inaccurate and breaks games";
1542 static menu_entry e_menu_plugin_spu[] =
1544 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1545 mee_onoff ("Reverb", 0, spu_config.iUseReverb, 1),
1546 mee_enum ("Interpolation", 0, spu_config.iUseInterpolation, men_spu_interp),
1547 //mee_onoff ("Adjust XA pitch", 0, spu_config.iXAPitch, 1),
1548 mee_onoff_h ("Adjust tempo", 0, spu_config.iTempo, 1, h_spu_tempo),
1552 static int menu_loop_plugin_spu(int id, int keys)
1555 me_loop(e_menu_plugin_spu, &sel);
1559 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1560 "savestates and can't be changed there. Must save\n"
1561 "config and reload the game for change to take effect";
1562 static const char h_plugin_gpu[] =
1563 #ifdef BUILTIN_GPU_NEON
1564 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1566 "gpu_peops is Pete's soft GPU, slow but accurate\n"
1567 "gpu_unai_old is from old PCSX4ALL, fast but glitchy\n"
1568 "gpu_unai is newer, more accurate but slower\n"
1569 "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1570 "must save config and reload the game if changed";
1571 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1572 "must save config and reload the game if changed";
1573 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1574 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1575 static const char h_gpu_unai_old[] = "Configure Unai/PCSX4ALL Team GPU plugin (old)";
1576 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team plugin (new)";
1577 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1579 static menu_entry e_menu_plugin_options[] =
1581 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1582 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1583 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1584 #ifdef BUILTIN_GPU_NEON
1585 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1587 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1588 mee_handler_h ("Configure gpu_unai_old GPU plugin", menu_loop_plugin_gpu_unai_old, h_gpu_unai_old),
1589 mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1590 mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1591 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1595 static menu_entry e_menu_main2[];
1597 static int menu_loop_plugin_options(int id, int keys)
1600 me_loop(e_menu_plugin_options, &sel);
1602 // sync BIOS/plugins
1603 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1604 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1605 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1606 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1611 // ------------ adv options menu ------------
1614 static const char h_cfg_noch[] = "Disables game-specific compatibility hacks";
1615 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1616 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1617 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1619 static const char h_cfg_stalls[] = "Will cause some games to run too fast";
1621 static menu_entry e_menu_speed_hacks[] =
1624 mee_onoff_h ("Disable compat hacks", 0, new_dynarec_hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1625 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1626 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1627 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1629 mee_onoff_h ("Disable CPU/GTE stalls", 0, menu_iopts[0], 1, h_cfg_stalls),
1633 static int menu_loop_speed_hacks(int id, int keys)
1636 menu_iopts[0] = Config.DisableStalls;
1637 me_loop(e_menu_speed_hacks, &sel);
1638 Config.DisableStalls = menu_iopts[0];
1642 static const char *men_autooo[] = { "Auto", "Off", "On", NULL };
1644 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1645 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1646 "(green: normal, red: fmod, blue: noise)";
1647 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1648 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1649 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1650 "(proper .cue/.bin dump is needed otherwise)";
1652 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1653 "Might be useful to overcome some dynarec bugs";
1655 static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1656 static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1657 static const char h_cfg_exc[] = "Emulate some PSX's debug hw like breakpoints\n"
1658 "and exceptions (slow, interpreter only, keep off)";
1659 static const char h_cfg_gpul[] = "Try enabling this if the game misses some graphics\n"
1660 "causes a performance hit";
1661 static const char h_cfg_ffps[] = "Instead of 50/60fps for PAL/NTSC use ~49.75/59.81\n"
1662 "Closer to real hw but doesn't match modern displays.";
1663 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1664 "(adjust this if the game is too slow/too fast/hangs)";
1666 enum { AMO_XA, AMO_CDDA, AMO_IC, AMO_BP, AMO_CPU, AMO_GPUL, AMO_FFPS };
1668 static menu_entry e_menu_adv_options[] =
1670 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1671 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1672 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1673 mee_onoff_h ("Disable XA Decoding", 0, menu_iopts[AMO_XA], 1, h_cfg_xa),
1674 mee_onoff_h ("Disable CD Audio", 0, menu_iopts[AMO_CDDA], 1, h_cfg_cdda),
1675 mee_onoff_h ("ICache emulation", 0, menu_iopts[AMO_IC], 1, h_cfg_icache),
1676 mee_onoff_h ("BP exception emulation", 0, menu_iopts[AMO_BP], 1, h_cfg_exc),
1677 mee_enum_h ("GPU l-list slow walking",0, menu_iopts[AMO_GPUL], men_autooo, h_cfg_gpul),
1678 mee_enum_h ("Fractional framerate", 0, menu_iopts[AMO_FFPS], men_autooo, h_cfg_ffps),
1679 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
1680 mee_onoff_h ("Disable dynarec (slow!)",0, menu_iopts[AMO_CPU], 1, h_cfg_nodrc),
1682 mee_range_h ("PSX CPU clock, %", 0, psx_clock, 1, 500, h_cfg_psxclk),
1683 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1687 static int menu_loop_adv_options(int id, int keys)
1694 { &Config.Xa, &menu_iopts[AMO_XA] },
1695 { &Config.Cdda, &menu_iopts[AMO_CDDA] },
1696 { &Config.icache_emulation, &menu_iopts[AMO_IC] },
1697 { &Config.PreciseExceptions, &menu_iopts[AMO_BP] },
1698 { &Config.Cpu, &menu_iopts[AMO_CPU] },
1701 for (i = 0; i < ARRAY_SIZE(opts); i++)
1702 *opts[i].mopt = *opts[i].opt;
1703 menu_iopts[AMO_GPUL] = Config.GpuListWalking + 1;
1704 menu_iopts[AMO_FFPS] = Config.FractionalFramerate + 1;
1706 me_loop(e_menu_adv_options, &sel);
1708 for (i = 0; i < ARRAY_SIZE(opts); i++)
1709 *opts[i].opt = *opts[i].mopt;
1710 Config.GpuListWalking = menu_iopts[AMO_GPUL] - 1;
1711 Config.FractionalFramerate = menu_iopts[AMO_FFPS] - 1;
1716 // ------------ options menu ------------
1718 static int mh_restore_defaults(int id, int keys)
1720 menu_set_defconfig();
1721 menu_update_msg("defaults restored");
1725 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1726 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1728 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1729 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1730 "loading state or both";
1732 static const char h_restore_def[] = "Switches back to default / recommended\n"
1734 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1735 static const char h_sputhr[] = "Warning: has some known bugs\n";
1737 static menu_entry e_menu_options[] =
1739 // mee_range ("Save slot", 0, state_slot, 0, 9),
1740 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1741 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1742 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1743 mee_enum ("Region", 0, region, men_region),
1744 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1746 mee_onoff_h ("Use C64x DSP for sound", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1748 mee_onoff_h ("Threaded SPU", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1750 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1751 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1752 mee_handler ("[Advanced]", menu_loop_adv_options),
1753 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1754 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1755 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1759 static int menu_loop_options(int id, int keys)
1763 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1764 me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1765 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1767 me_loop(e_menu_options, &sel);
1772 // ------------ debug menu ------------
1774 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1776 int w = min(g_menuscreen_w, 1024);
1777 int h = min(g_menuscreen_h, 512);
1778 u16 *d = g_menuscreen_ptr;
1779 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1783 gpuf->ulFreezeVersion = 1;
1784 if (GPU_freeze != NULL)
1785 GPU_freeze(1, gpuf);
1787 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1788 bgr555_to_rgb565(d, s, w * 2);
1790 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1791 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1792 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1793 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1794 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1797 static void debug_menu_loop(void)
1799 int inp, df_x = 0, df_y = 0;
1802 gpuf = malloc(sizeof(*gpuf));
1808 menu_draw_begin(0, 1);
1809 draw_frame_debug(gpuf, df_x, df_y);
1812 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1813 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1814 if (inp & PBTN_MBACK) break;
1815 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1816 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1817 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x -= 2; }
1818 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1824 // --------- memcard manager ---------
1826 static void draw_mc_icon(int dx, int dy, const u16 *s)
1831 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1833 for (y = 0; y < 16; y++, s += 16) {
1834 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1835 for (x = 0; x < 16; x++) {
1837 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1838 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1844 static void draw_mc_bg(void)
1846 McdBlock *blocks1, *blocks2;
1850 blocks1 = malloc(15 * sizeof(blocks1[0]));
1851 blocks2 = malloc(15 * sizeof(blocks1[0]));
1852 if (blocks1 == NULL || blocks2 == NULL)
1855 for (i = 0; i < 15; i++) {
1856 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1857 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1860 menu_draw_begin(1, 1);
1862 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1864 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1868 maxicons = g_menuscreen_h / 32;
1871 row2 = g_menuscreen_w / 2;
1872 for (i = 0; i < maxicons; i++) {
1873 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1874 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1876 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1877 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1880 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1888 static void handle_memcard_sel(void)
1890 strcpy(Config.Mcd1, "none");
1891 if (memcard1_sel != 0)
1892 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1893 strcpy(Config.Mcd2, "none");
1894 if (memcard2_sel != 0)
1895 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1896 LoadMcds(Config.Mcd1, Config.Mcd2);
1900 static menu_entry e_memcard_options[] =
1902 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1903 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1907 static int menu_loop_memcards(int id, int keys)
1913 memcard1_sel = memcard2_sel = 0;
1914 p = strrchr(Config.Mcd1, '/');
1916 for (i = 0; memcards[i] != NULL; i++)
1917 if (strcmp(p + 1, memcards[i]) == 0)
1918 { memcard1_sel = i; break; }
1919 p = strrchr(Config.Mcd2, '/');
1921 for (i = 0; memcards[i] != NULL; i++)
1922 if (strcmp(p + 1, memcards[i]) == 0)
1923 { memcard2_sel = i; break; }
1925 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1927 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1932 // ------------ cheats menu ------------
1934 static void draw_cheatlist(int sel)
1936 int max_cnt, start, i, pos, active;
1938 max_cnt = g_menuscreen_h / me_sfont_h;
1939 start = max_cnt / 2 - sel;
1941 menu_draw_begin(1, 1);
1943 for (i = 0; i < NumCheats; i++) {
1945 if (pos < 0) continue;
1946 if (pos >= max_cnt) break;
1947 active = Cheats[i].Enabled;
1948 smalltext_out16(14, pos * me_sfont_h,
1949 active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1950 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1951 Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1955 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1957 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1961 static void menu_loop_cheats(void)
1963 static int menu_sel = 0;
1968 draw_cheatlist(menu_sel);
1969 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1970 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1971 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1972 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1973 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1974 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1975 if (inp & PBTN_MOK) { // action
1976 if (menu_sel < NumCheats)
1977 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1980 if (inp & PBTN_MBACK)
1985 // --------- main menu help ----------
1987 static void menu_bios_warn(void)
1990 static const char msg[] =
1991 "You don't seem to have copied any BIOS\n"
1993 MENU_BIOS_PATH "\n\n"
1995 "While many games work fine with fake\n"
1996 "(HLE) BIOS, others (like MGS and FF8)\n"
1997 "require BIOS to work.\n"
1998 "After copying the file, you'll also need\n"
1999 "to select it in the emu's menu:\n"
2000 "options->[BIOS/Plugins]\n\n"
2001 "The file is usually named SCPH1001.BIN,\n"
2002 "but other not compressed files can be\n"
2004 "Press %s or %s to continue";
2005 char tmp_msg[sizeof(msg) + 64];
2007 snprintf(tmp_msg, sizeof(tmp_msg), msg,
2008 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
2011 draw_menu_message(tmp_msg, NULL);
2013 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2014 if (inp & (PBTN_MBACK|PBTN_MOK))
2019 // ------------ main menu ------------
2021 static menu_entry e_menu_main[];
2023 static void draw_frame_main(void)
2032 if (CdromId[0] != 0) {
2033 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
2034 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
2035 Config.HLE ? "HLE" : "BIOS");
2036 smalltext_out16(4, 1, buff, 0x105f);
2040 capacity = plat_target_bat_capacity_get();
2042 tmp = localtime(<ime);
2043 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
2044 if (capacity >= 0) {
2045 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
2050 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
2054 static void draw_frame_credits(void)
2056 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
2059 static const char credits_text[] =
2061 "(C) 1999-2003 PCSX Team\n"
2062 "(C) 2005-2009 PCSX-df Team\n"
2063 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
2064 "ARM recompiler (C) 2009-2011 Ari64\n"
2065 #ifdef BUILTIN_GPU_NEON
2066 "ARM NEON GPU (c) 2011-2012 Exophase\n"
2068 "PEOpS GPU and SPU by Pete Bernert\n"
2069 " and the P.E.Op.S. team\n"
2070 "PCSX4ALL plugin by PCSX4ALL team\n"
2071 " Chui, Franxis, Unai\n\n"
2072 "integration, optimization and\n"
2073 " frontend (C) 2010-2015 notaz\n";
2075 static int reset_game(void)
2081 if (LoadCdrom() == -1)
2087 static int reload_plugins(const char *cdimg)
2093 set_cd_image(cdimg);
2095 pcnt_hook_plugins();
2097 if (OpenPlugins() == -1) {
2098 menu_update_msg("failed to open plugins");
2101 plugin_call_rearmed_cbs();
2103 cdrIsoMultidiskCount = 1;
2105 CdromLabel[0] = '\0';
2110 static int run_bios(void)
2112 boolean origSlowBoot = Config.SlowBoot;
2118 if (reload_plugins(NULL) != 0)
2120 Config.SlowBoot = 1;
2122 Config.SlowBoot = origSlowBoot;
2128 static int run_exe(void)
2130 const char *exts[] = { "exe", NULL };
2133 fname = menu_loop_romsel(last_selected_fname,
2134 sizeof(last_selected_fname), exts, NULL);
2139 if (reload_plugins(NULL) != 0)
2143 if (Load(fname) != 0) {
2144 menu_update_msg("exe load failed, bad file?");
2153 static int run_cd_image(const char *fname)
2155 int autoload_state = g_autostateld_opt;
2158 reload_plugins(fname);
2160 // always autodetect, menu_sync_config will override as needed
2163 if (CheckCdrom() == -1) {
2164 // Only check the CD if we are starting the console with a CD
2166 menu_update_msg("unsupported/invalid CD image");
2172 // Read main executable directly from CDRom and start it
2173 if (LoadCdrom() == -1) {
2175 menu_update_msg("failed to load CD image");
2182 if (autoload_state) {
2183 unsigned int newest = 0;
2184 int time, slot, newest_slot = -1;
2186 for (slot = 0; slot < 10; slot++) {
2187 if (emu_check_save_file(slot, &time)) {
2188 if ((unsigned int)time > newest) {
2195 if (newest_slot >= 0) {
2196 lprintf("autoload slot %d\n", newest_slot);
2197 emu_load_state(newest_slot);
2200 lprintf("no save to autoload.\n");
2207 static int romsel_run(void)
2209 int prev_gpu, prev_spu;
2212 fname = menu_loop_romsel(last_selected_fname,
2213 sizeof(last_selected_fname), filter_exts,
2214 optional_cdimg_filter);
2218 printf("selected file: %s\n", fname);
2220 new_dynarec_clear_full();
2222 if (run_cd_image(fname) != 0)
2225 prev_gpu = gpu_plugsel;
2226 prev_spu = spu_plugsel;
2227 if (menu_load_config(1) != 0)
2228 menu_load_config(0);
2230 // check for plugin changes, have to repeat
2231 // loading if game config changed plugins to reload them
2232 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2233 printf("plugin change detected, reloading plugins..\n");
2234 if (run_cd_image(fname) != 0)
2238 strcpy(last_selected_fname, fname);
2239 menu_do_last_cd_img(0);
2243 static int swap_cd_image(void)
2247 fname = menu_loop_romsel(last_selected_fname,
2248 sizeof(last_selected_fname), filter_exts,
2249 optional_cdimg_filter);
2253 printf("selected file: %s\n", fname);
2256 CdromLabel[0] = '\0';
2258 set_cd_image(fname);
2259 if (ReloadCdromPlugin() < 0) {
2260 menu_update_msg("failed to load cdr plugin");
2263 if (CDR_open() < 0) {
2264 menu_update_msg("failed to open cdr plugin");
2268 SetCdOpenCaseTime(time(NULL) + 2);
2271 strcpy(last_selected_fname, fname);
2275 static int swap_cd_multidisk(void)
2277 cdrIsoMultidiskSelect++;
2279 CdromLabel[0] = '\0';
2282 if (CDR_open() < 0) {
2283 menu_update_msg("failed to open cdr plugin");
2287 SetCdOpenCaseTime(time(NULL) + 2);
2293 static void load_pcsx_cht(void)
2295 static const char *exts[] = { "cht", NULL };
2299 fname = menu_loop_romsel(last_selected_fname,
2300 sizeof(last_selected_fname), exts, NULL);
2304 printf("selected cheat file: %s\n", fname);
2307 if (NumCheats == 0 && NumCodes == 0)
2308 menu_update_msg("failed to load cheats");
2310 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2311 menu_update_msg(msg);
2313 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2316 static int main_menu_handler(int id, int keys)
2320 case MA_MAIN_RESUME_GAME:
2324 case MA_MAIN_SAVE_STATE:
2326 return menu_loop_savestate(0);
2328 case MA_MAIN_LOAD_STATE:
2330 return menu_loop_savestate(1);
2332 case MA_MAIN_RESET_GAME:
2333 if (ready_to_go && reset_game() == 0)
2336 case MA_MAIN_LOAD_ROM:
2337 if (romsel_run() == 0)
2340 case MA_MAIN_SWAP_CD:
2341 if (swap_cd_image() == 0)
2344 case MA_MAIN_SWAP_CD_MULTI:
2345 if (swap_cd_multidisk() == 0)
2348 case MA_MAIN_RUN_BIOS:
2349 if (run_bios() == 0)
2352 case MA_MAIN_RUN_EXE:
2356 case MA_MAIN_CHEATS:
2359 case MA_MAIN_LOAD_CHEATS:
2362 case MA_MAIN_CREDITS:
2363 draw_menu_message(credits_text, draw_frame_credits);
2364 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2367 emu_core_ask_exit();
2370 lprintf("%s: something unknown selected\n", __FUNCTION__);
2377 static menu_entry e_menu_main2[] =
2379 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2380 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2381 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2382 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2383 mee_handler ("Memcard manager", menu_loop_memcards),
2384 mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS, main_menu_handler),
2388 static int main_menu2_handler(int id, int keys)
2392 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2393 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2394 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2395 me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2397 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2400 static const char h_extra[] = "Change CD, manage memcards..\n";
2402 static menu_entry e_menu_main[] =
2406 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2407 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2408 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2409 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2410 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2411 mee_handler ("Options", menu_loop_options),
2412 mee_handler ("Controls", menu_loop_keyconfig),
2413 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
2414 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2415 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2416 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2420 // ----------------------------
2422 static void menu_leave_emu(void);
2424 void menu_loop(void)
2426 static int warned_about_bios = 0;
2431 if (config_save_counter == 0) {
2433 if (bioses[1] != NULL) {
2434 // autoselect BIOS to make user's life easier
2435 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2438 else if (!warned_about_bios) {
2440 warned_about_bios = 1;
2444 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2445 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2446 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2447 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2448 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2450 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2453 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2454 } while (!ready_to_go && !g_emu_want_quit);
2456 /* wait until menu, ok, back is released */
2457 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2460 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2465 static int qsort_strcmp(const void *p1, const void *p2)
2467 char * const *s1 = (char * const *)p1;
2468 char * const *s2 = (char * const *)p2;
2469 return strcasecmp(*s1, *s2);
2472 static void scan_bios_plugins(void)
2474 char fname[MAXPATHLEN];
2476 int bios_i, gpu_i, spu_i, mc_i;
2481 gpu_plugins[0] = "builtin_gpu";
2482 spu_plugins[0] = "builtin_spu";
2483 memcards[0] = "(none)";
2484 bios_i = gpu_i = spu_i = mc_i = 1;
2486 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2487 dir = opendir(fname);
2489 perror("scan_bios_plugins bios opendir");
2504 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2507 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2508 if (stat(fname, &st) != 0
2509 || (st.st_size != 512*1024 && st.st_size != 4*1024*1024)) {
2510 printf("bad BIOS file: %s\n", ent->d_name);
2514 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2515 bioses[bios_i++] = strdup(ent->d_name);
2519 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2525 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2526 dir = opendir(fname);
2528 perror("scan_bios_plugins plugins opendir");
2542 p = strstr(ent->d_name, ".so");
2546 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2547 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2549 fprintf(stderr, "%s\n", dlerror());
2553 // now what do we have here?
2554 tmp = dlsym(h, "GPUinit");
2557 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2558 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2562 tmp = dlsym(h, "SPUinit");
2565 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2566 spu_plugins[spu_i++] = strdup(ent->d_name);
2570 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2577 dir = opendir("." MEMCARD_DIR);
2579 perror("scan_bios_plugins memcards opendir");
2594 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2597 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2598 if (stat(fname, &st) != 0) {
2599 printf("bad memcard file: %s\n", ent->d_name);
2603 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2604 memcards[mc_i++] = strdup(ent->d_name);
2608 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2612 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2617 void menu_init(void)
2619 char buff[MAXPATHLEN];
2622 cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2624 scan_bios_plugins();
2627 menu_set_defconfig();
2628 menu_load_config(0);
2629 menu_do_last_cd_img(1);
2634 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2635 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2636 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2637 fprintf(stderr, "OOM\n");
2641 emu_make_path(buff, "skin/background.png", sizeof(buff));
2642 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2644 i = plat_target.cpu_clock_set != NULL
2645 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2646 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, i);
2648 i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2649 e_menu_gfx_options[i].data = plat_target.vout_methods;
2650 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2651 plat_target.vout_methods != NULL);
2653 i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2654 e_menu_gfx_options[i].data = plat_target.hwfilters;
2655 me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2656 plat_target.hwfilters != NULL);
2658 me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2659 plat_target.gamma_set != NULL);
2661 #ifdef HAVE_PRE_ARMV7
2662 me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2664 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2665 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2666 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2667 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2668 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2669 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2670 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2673 void menu_notify_mode_change(int w, int h, int bpp)
2677 last_vout_bpp = bpp;
2680 static void menu_leave_emu(void)
2682 if (GPU_close != NULL) {
2683 int ret = GPU_close();
2685 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2688 plat_video_menu_enter(ready_to_go);
2690 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2691 if (pl_vout_buf != NULL && ready_to_go) {
2692 int x = max(0, g_menuscreen_w - last_vout_w);
2693 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2694 int w = min(g_menuscreen_w, last_vout_w);
2695 int h = min(g_menuscreen_h, last_vout_h);
2696 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2697 char *s = pl_vout_buf;
2699 if (last_vout_bpp == 16) {
2700 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2701 menu_darken_bg(d, s, w, 0);
2704 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2705 rgb888_to_rgb565(d, s, w * 3);
2706 menu_darken_bg(d, d, w, 0);
2712 cpu_clock = plat_target_cpu_clock_get();
2715 void menu_prepare_emu(void)
2717 R3000Acpu *prev_cpu = psxCpu;
2719 plat_video_menu_leave();
2721 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
2722 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2726 if (psxCpu != prev_cpu) {
2727 prev_cpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
2728 prev_cpu->Shutdown();
2731 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
2735 psxCpu->ApplyConfig();
2737 // core doesn't care about Config.Cdda changes,
2738 // so handle them manually here
2743 plat_target_cpu_clock_set(cpu_clock);
2745 // push config to GPU plugin
2746 plugin_call_rearmed_cbs();
2748 if (GPU_open != NULL) {
2749 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2751 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2755 void menu_update_msg(const char *msg)
2757 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2758 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2760 menu_error_time = plat_get_ticks_ms();
2761 lprintf("msg: %s\n", menu_error_msg);
2764 void menu_finish(void)
2766 if (cpu_clock_st > 0)
2767 plat_target_cpu_clock_set(cpu_clock_st);