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/ppf.h"
44 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
45 #include "../plugins/dfsound/spu_config.h"
46 #include "psemu_plugin_defs.h"
47 #include "arm_features.h"
50 #define REARMED_BIRTHDAY_TIME 1293306830 /* 25 Dec 2010 */
52 #define array_size(x) (sizeof(x) / sizeof(x[0]))
63 MA_MAIN_SWAP_CD_MULTI,
94 MA_OPT_SCANLINE_LEVEL,
99 static int last_vout_w, last_vout_h, last_vout_bpp;
100 static int cpu_clock, cpu_clock_st, volume_boost;
101 static int frameskip = 1; // 0 - auto, 1 - off
102 static char last_selected_fname[MAXPATHLEN];
103 static int config_save_counter, region, in_type_sel1, in_type_sel2;
104 static int psx_clock;
105 static int memcard1_sel = -1, memcard2_sel = -1;
106 extern int g_autostateld_opt;
107 static int menu_iopts[8];
108 int g_opts, g_scaler, g_gamma = 100;
109 int scanlines, scanline_level = 20;
110 int soft_scaling, analog_deadzone; // for Caanoo
113 #ifndef HAVE_PRE_ARMV7
114 #define DEFAULT_PSX_CLOCK (10000 / CYCLE_MULT_DEFAULT)
115 #define DEFAULT_PSX_CLOCK_S "57"
117 #define DEFAULT_PSX_CLOCK 50
118 #define DEFAULT_PSX_CLOCK_S "50"
121 static const char *bioses[32];
122 static const char *gpu_plugins[16];
123 static const char *spu_plugins[16];
124 static const char *memcards[32];
125 static int bios_sel, gpu_plugsel, spu_plugsel;
127 #ifndef UI_FEATURES_H
128 #define MENU_BIOS_PATH "bios/"
129 #define MENU_SHOW_VARSCALER 0
130 #define MENU_SHOW_VOUTMODE 1
131 #define MENU_SHOW_SCALER2 0
132 #define MENU_SHOW_NUBS_BTNS 0
133 #define MENU_SHOW_VIBRATION 0
134 #define MENU_SHOW_DEADZONE 0
135 #define MENU_SHOW_MINIMIZE 0
136 #define MENU_SHOW_FULLSCREEN 1
137 #define MENU_SHOW_VOLUME 0
140 static int min(int x, int y) { return x < y ? x : y; }
141 static int max(int x, int y) { return x > y ? x : y; }
143 void emu_make_path(char *buff, const char *end, int size)
147 end_len = strlen(end);
148 pos = plat_get_root_dir(buff, size);
149 strncpy(buff + pos, end, size - pos);
151 if (pos + end_len > size - 1)
152 printf("Warning: path truncated: %s\n", buff);
155 static int emu_check_save_file(int slot, int *time)
157 char fname[MAXPATHLEN];
161 ret = emu_check_state(slot);
162 if (ret != 0 || time == NULL)
163 return ret == 0 ? 1 : 0;
165 ret = get_state_filename(fname, sizeof(fname), slot);
169 ret = stat(fname, &status);
173 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
174 return 1; // probably bad rtc like on some Caanoos
176 *time = status.st_mtime;
181 static int emu_save_load_game(int load, int unused)
186 ret = emu_load_state(state_slot);
188 // reflect hle/bios mode from savestate
191 else if (bios_sel == 0 && bioses[1] != NULL)
192 // XXX: maybe find the right bios instead
196 ret = emu_save_state(state_slot);
201 static void rm_namelist_entry(struct dirent **namelist,
202 int count, const char *name)
206 for (i = 1; i < count; i++) {
207 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
210 if (strcmp(name, namelist[i]->d_name) == 0) {
218 static int optional_cdimg_filter(struct dirent **namelist, int count,
222 char buf[256], buf2[257];
223 int i, d, ret, good_cue;
230 for (i = 1; i < count; i++) {
231 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
234 ext = strrchr(namelist[i]->d_name, '.');
236 // should not happen but whatever
243 // first find .cue files and remove files they reference
244 if (strcasecmp(ext, "cue") == 0)
246 snprintf(buf, sizeof(buf), "%s/%s", basedir,
247 namelist[i]->d_name);
257 while (fgets(buf, sizeof(buf), f)) {
258 ret = sscanf(buf, " FILE \"%256[^\"]\"", buf2);
260 ret = sscanf(buf, " FILE %256s", buf2);
264 p = strrchr(buf2, '/');
266 p = strrchr(buf2, '\\');
272 snprintf(buf, sizeof(buf), "%s/%s", basedir, p);
273 ret = STAT(buf, &statf);
275 rm_namelist_entry(namelist, count, p);
288 p = strcasestr(namelist[i]->d_name, "track");
290 ret = strtoul(p + 5, NULL, 10);
300 for (i = d = 1; i < count; i++)
301 if (namelist[i] != NULL)
302 namelist[d++] = namelist[i];
307 // propagate menu settings to the emu vars
308 static void menu_sync_config(void)
310 static int allow_abs_only_old;
315 Config.PsxType = region - 1;
317 Config.cycle_multiplier = 10000 / psx_clock;
319 switch (in_type_sel1) {
320 case 1: in_type[0] = PSE_PAD_TYPE_ANALOGPAD; break;
321 case 2: in_type[0] = PSE_PAD_TYPE_GUNCON; break;
322 case 3: in_type[0] = PSE_PAD_TYPE_GUN; break;
323 case 4: in_type[0] = PSE_PAD_TYPE_NONE; break;
324 default: in_type[0] = PSE_PAD_TYPE_STANDARD;
326 switch (in_type_sel2) {
327 case 1: in_type[1] = PSE_PAD_TYPE_ANALOGPAD; break;
328 case 2: in_type[1] = PSE_PAD_TYPE_GUNCON; break;
329 case 3: in_type[1] = PSE_PAD_TYPE_GUN; break;
330 case 4: in_type[1] = PSE_PAD_TYPE_NONE; break;
331 default: in_type[1] = PSE_PAD_TYPE_STANDARD;
333 if (in_evdev_allow_abs_only != allow_abs_only_old) {
335 allow_abs_only_old = in_evdev_allow_abs_only;
338 spu_config.iVolume = 768 + 128 * volume_boost;
339 pl_rearmed_cbs.frameskip = frameskip - 1;
340 pl_timing_prepare(Config.PsxType);
343 static void menu_set_defconfig(void)
345 emu_set_default_config();
348 g_scaler = SCALE_4_3;
351 frameskip = 1; // 1 - off
352 analog_deadzone = 50;
357 plat_target.vout_fullscreen = 0;
358 psx_clock = DEFAULT_PSX_CLOCK;
361 in_type_sel1 = in_type_sel2 = 0;
362 in_evdev_allow_abs_only = 0;
367 #define CE_CONFIG_STR(val) \
368 { #val, 0, Config.val }
370 #define CE_CONFIG_VAL(val) \
371 { #val, sizeof(Config.val), &Config.val }
373 #define CE_STR(val) \
376 #define CE_INTVAL(val) \
377 { #val, sizeof(val), &val }
379 #define CE_INTVAL_N(name, val) \
380 { name, sizeof(val), &val }
382 #define CE_INTVAL_P(val) \
383 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
385 // 'versioned' var, used when defaults change
386 #define CE_CONFIG_STR_V(val, ver) \
387 { #val #ver, 0, Config.val }
389 #define CE_INTVAL_V(val, ver) \
390 { #val #ver, sizeof(val), &val }
392 #define CE_INTVAL_PV(val, ver) \
393 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
395 static const struct {
401 CE_CONFIG_STR_V(Gpu, 3),
403 // CE_CONFIG_STR(Cdr),
407 CE_CONFIG_VAL(Debug),
408 CE_CONFIG_VAL(PsxOut),
409 CE_CONFIG_VAL(icache_emulation),
410 CE_CONFIG_VAL(DisableStalls),
412 CE_CONFIG_VAL(GpuListWalking),
413 CE_CONFIG_VAL(FractionalFramerate),
414 CE_CONFIG_VAL(PreciseExceptions),
415 CE_CONFIG_VAL(TurboCD),
417 CE_INTVAL_V(g_scaler, 3),
419 CE_INTVAL(g_layer_x),
420 CE_INTVAL(g_layer_y),
421 CE_INTVAL(g_layer_w),
422 CE_INTVAL(g_layer_h),
423 CE_INTVAL(soft_filter),
424 CE_INTVAL(scanlines),
425 CE_INTVAL(scanline_level),
426 CE_INTVAL(plat_target.vout_method),
427 CE_INTVAL(plat_target.hwfilter),
428 CE_INTVAL(plat_target.vout_fullscreen),
429 CE_INTVAL(state_slot),
430 CE_INTVAL(cpu_clock),
432 CE_INTVAL(in_type_sel1),
433 CE_INTVAL(in_type_sel2),
434 CE_INTVAL(analog_deadzone),
435 CE_INTVAL(memcard1_sel),
436 CE_INTVAL(memcard2_sel),
437 CE_INTVAL(g_autostateld_opt),
438 CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
439 CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
440 CE_INTVAL_V(frameskip, 4),
441 CE_INTVAL_P(gpu_peops.iUseDither),
442 CE_INTVAL_P(gpu_peops.dwActFixes),
443 CE_INTVAL_P(gpu_unai_old.lineskip),
444 CE_INTVAL_P(gpu_unai_old.abe_hack),
445 CE_INTVAL_P(gpu_unai_old.no_light),
446 CE_INTVAL_P(gpu_unai_old.no_blend),
447 CE_INTVAL_P(gpu_unai.ilace_force),
448 CE_INTVAL_P(gpu_unai.pixel_skip),
449 CE_INTVAL_P(gpu_unai.lighting),
450 CE_INTVAL_P(gpu_unai.fast_lighting),
451 CE_INTVAL_P(gpu_unai.blending),
452 CE_INTVAL_P(gpu_unai.dithering),
453 CE_INTVAL_P(gpu_unai.scale_hires),
454 CE_INTVAL_P(gpu_neon.allow_interlace),
455 CE_INTVAL_P(gpu_neon.enhancement_enable),
456 CE_INTVAL_P(gpu_neon.enhancement_no_main),
457 CE_INTVAL_P(gpu_neon.enhancement_tex_adj),
458 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
459 CE_INTVAL_P(gpu_peopsgl.iFilterType),
460 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
461 CE_INTVAL_P(gpu_peopsgl.iUseMask),
462 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
463 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
464 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
465 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
466 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
467 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
468 CE_INTVAL_P(screen_centering_type),
469 CE_INTVAL_P(screen_centering_x),
470 CE_INTVAL_P(screen_centering_y),
471 CE_INTVAL_P(show_overscan),
472 CE_INTVAL(spu_config.iUseReverb),
473 CE_INTVAL(spu_config.iXAPitch),
474 CE_INTVAL(spu_config.iUseInterpolation),
475 CE_INTVAL(spu_config.iTempo),
476 CE_INTVAL(spu_config.iUseThread),
477 CE_INTVAL(config_save_counter),
478 CE_INTVAL(in_evdev_allow_abs_only),
479 CE_INTVAL(volume_boost),
480 CE_INTVAL(psx_clock),
481 CE_INTVAL(new_dynarec_hacks),
482 CE_INTVAL(in_enable_vibration),
485 static char *get_cd_label(void)
487 static char trimlabel[33];
490 strncpy(trimlabel, CdromLabel, 32);
492 for (j = 31; j >= 0; j--)
493 if (trimlabel[j] == ' ')
499 static void make_cfg_fname(char *buf, size_t size, int is_game)
502 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
504 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
507 static void keys_write_all(FILE *f);
508 static char *mystrip(char *str);
510 static void write_u32_value(FILE *f, u32 v)
514 fprintf(f, "%x\n", v);
517 static int menu_write_config(int is_game)
519 char cfgfile[MAXPATHLEN];
523 config_save_counter++;
525 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
526 f = fopen(cfgfile, "w");
528 printf("menu_write_config: failed to open: %s\n", cfgfile);
532 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
533 fprintf(f, "%s = ", config_data[i].name);
534 switch (config_data[i].len) {
536 fprintf(f, "%s\n", (char *)config_data[i].val);
539 write_u32_value(f, *(u8 *)config_data[i].val);
542 write_u32_value(f, *(u16 *)config_data[i].val);
545 write_u32_value(f, *(u32 *)config_data[i].val);
548 printf("menu_write_config: unhandled len %d for %s\n",
549 (int)config_data[i].len, config_data[i].name);
560 static int menu_do_last_cd_img(int is_get)
562 static const char *defaults[] = { "/media", "/mnt/sd", "/mnt" };
568 snprintf(path, sizeof(path), "." PCSX_DOT_DIR "lastcdimg.txt");
569 f = fopen(path, is_get ? "r" : "w");
576 ret = fread(last_selected_fname, 1, sizeof(last_selected_fname) - 1, f);
577 last_selected_fname[ret] = 0;
578 mystrip(last_selected_fname);
581 fprintf(f, "%s\n", last_selected_fname);
586 for (i = 0; last_selected_fname[0] == 0
587 || STAT(last_selected_fname, &st) != 0; i++)
589 if (i >= ARRAY_SIZE(defaults))
591 strcpy(last_selected_fname, defaults[i]);
598 static void parse_str_val(char *cval, const char *src)
601 strncpy(cval, src, MAXPATHLEN);
602 cval[MAXPATHLEN - 1] = 0;
603 tmp = strchr(cval, '\n');
605 tmp = strchr(cval, '\r');
610 static void keys_load_all(const char *cfg);
612 int menu_load_config(int is_game)
614 char cfgfile[MAXPATHLEN];
620 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
621 f = fopen(cfgfile, "r");
623 printf("menu_load_config: failed to open: %s\n", cfgfile);
627 fseek(f, 0, SEEK_END);
630 printf("bad size %ld: %s\n", size, cfgfile);
634 cfg = malloc(size + 1);
638 fseek(f, 0, SEEK_SET);
639 if (fread(cfg, 1, size, f) != size) {
640 printf("failed to read: %s\n", cfgfile);
645 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
649 tmp = strstr(cfg, config_data[i].name);
652 tmp += strlen(config_data[i].name);
653 if (strncmp(tmp, " = ", 3) != 0)
657 if (config_data[i].len == 0) {
658 parse_str_val(config_data[i].val, tmp);
663 val = strtoul(tmp, &tmp2, 16);
664 if (tmp2 == NULL || tmp == tmp2)
665 continue; // parse failed
667 switch (config_data[i].len) {
669 *(u8 *)config_data[i].val = val;
672 *(u16 *)config_data[i].val = val;
675 *(u32 *)config_data[i].val = val;
678 printf("menu_load_config: unhandled len %d for %s\n",
679 (int)config_data[i].len, config_data[i].name);
685 char *tmp = strstr(cfg, "lastcdimg = ");
688 parse_str_val(last_selected_fname, tmp);
703 for (i = bios_sel = 0; bioses[i] != NULL; i++)
704 if (strcmp(Config.Bios, bioses[i]) == 0)
705 { bios_sel = i; break; }
707 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
708 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
709 { gpu_plugsel = i; break; }
711 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
712 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
713 { spu_plugsel = i; break; }
715 // memcard selections
716 char mcd1_old[sizeof(Config.Mcd1)];
717 char mcd2_old[sizeof(Config.Mcd2)];
718 strcpy(mcd1_old, Config.Mcd1);
719 strcpy(mcd2_old, Config.Mcd2);
721 if ((unsigned int)memcard1_sel < ARRAY_SIZE(memcards)) {
722 if (memcard1_sel == 0)
723 strcpy(Config.Mcd1, "none");
724 else if (memcards[memcard1_sel] != NULL)
725 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s",
726 MEMCARD_DIR, memcards[memcard1_sel]);
728 if ((unsigned int)memcard2_sel < ARRAY_SIZE(memcards)) {
729 if (memcard2_sel == 0)
730 strcpy(Config.Mcd2, "none");
731 else if (memcards[memcard2_sel] != NULL)
732 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s",
733 MEMCARD_DIR, memcards[memcard2_sel]);
735 if (strcmp(mcd1_old, Config.Mcd1) || strcmp(mcd2_old, Config.Mcd2))
736 LoadMcds(Config.Mcd1, Config.Mcd2);
741 static const char *filter_exts[] = {
742 "bin", "img", "mdf", "iso", "cue", "z",
746 "bz", "znx", "pbp", "cbn", "ppf", NULL
749 // rrrr rggg gggb bbbb
750 static unsigned short fname2color(const char *fname)
752 static const char *other_exts[] = {
753 "ccd", "toc", "mds", "sub", "table", "index", "sbi"
755 const char *ext = strrchr(fname, '.');
761 for (i = 0; filter_exts[i] != NULL; i++)
762 if (strcasecmp(ext, filter_exts[i]) == 0)
764 for (i = 0; i < array_size(other_exts); i++)
765 if (strcasecmp(ext, other_exts[i]) == 0)
770 static void draw_savestate_bg(int slot);
772 #define MENU_ALIGN_LEFT
773 #ifndef HAVE_PRE_ARMV7 // assume hires device
779 #include "libpicofe/menu.c"
781 // a bit of black magic here
782 static void draw_savestate_bg(int slot)
784 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
786 char fname[MAXPATHLEN];
793 ret = get_state_filename(fname, sizeof(fname), slot);
797 f = gzopen(fname, "rb");
801 if ((ret = (int)gzseek(f, 0x29933d, SEEK_SET)) != 0x29933d) {
802 fprintf(stderr, "gzseek failed: %d\n", ret);
807 gpu = malloc(sizeof(*gpu));
813 ret = gzread(f, gpu, sizeof(*gpu));
815 if (ret != sizeof(*gpu)) {
816 fprintf(stderr, "gzread failed\n");
820 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
822 if (gpu->ulStatus & 0x800000)
823 goto out; // disabled
825 x = gpu->ulControl[5] & 0x3ff;
826 y = (gpu->ulControl[5] >> 10) & 0x1ff;
827 w = psx_widths[(gpu->ulStatus >> 16) & 7];
828 tmp = gpu->ulControl[7];
829 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
830 if (gpu->ulStatus & 0x80000) // doubleheight
832 if (h <= 0 || h > 512)
838 s = (u16 *)gpu->psxVRam + y * 1024 + x;
840 x = max(0, g_menuscreen_w - w) & ~3;
841 y = max(0, g_menuscreen_h / 2 - h / 2);
842 w = min(g_menuscreen_w, w);
843 h = min(g_menuscreen_h, h);
844 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
846 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
847 if (gpu->ulStatus & 0x200000)
848 bgr888_to_rgb565(d, s, w * 3);
850 bgr555_to_rgb565(d, s, w * 2);
852 // darken this so that menu text is visible
853 if (g_menuscreen_w - w < 320)
854 menu_darken_bg(d, d, w, 0);
861 // -------------- key config --------------
863 me_bind_action me_ctrl_actions[] =
865 { "UP ", 1 << DKEY_UP},
866 { "DOWN ", 1 << DKEY_DOWN },
867 { "LEFT ", 1 << DKEY_LEFT },
868 { "RIGHT ", 1 << DKEY_RIGHT },
869 { "TRIANGLE", 1 << DKEY_TRIANGLE },
870 { "CIRCLE ", 1 << DKEY_CIRCLE },
871 { "CROSS ", 1 << DKEY_CROSS },
872 { "SQUARE ", 1 << DKEY_SQUARE },
873 { "L1 ", 1 << DKEY_L1 },
874 { "R1 ", 1 << DKEY_R1 },
875 { "L2 ", 1 << DKEY_L2 },
876 { "R2 ", 1 << DKEY_R2 },
877 { "L3 ", 1 << DKEY_L3 },
878 { "R3 ", 1 << DKEY_R3 },
879 { "START ", 1 << DKEY_START },
880 { "SELECT ", 1 << DKEY_SELECT },
884 me_bind_action emuctrl_actions[] =
886 { "Save State ", 1 << SACTION_SAVE_STATE },
887 { "Load State ", 1 << SACTION_LOAD_STATE },
888 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
889 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
890 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
891 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
892 { "Show/Hide FPS ", 1 << SACTION_TOGGLE_FPS },
893 #ifndef HAVE_PRE_ARMV7
894 { "Switch Renderer ", 1 << SACTION_SWITCH_DISPMODE },
896 { "Fast Forward ", 1 << SACTION_FAST_FORWARD },
897 #if MENU_SHOW_MINIMIZE
898 { "Minimize ", 1 << SACTION_MINIMIZE },
900 #if MENU_SHOW_FULLSCREEN
901 { "Toggle fullscreen", 1 << SACTION_TOGGLE_FULLSCREEN },
903 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
904 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
905 { "Gun A button ", 1 << SACTION_GUN_A },
906 { "Gun B button ", 1 << SACTION_GUN_B },
907 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
909 { "Volume Up ", 1 << SACTION_VOLUME_UP },
910 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
912 { "Analog toggle ", 1 << SACTION_ANALOG_TOGGLE },
916 static char *mystrip(char *str)
921 for (i = 0; i < len; i++)
922 if (str[i] != ' ') break;
923 if (i > 0) memmove(str, str + i, len - i + 1);
926 for (i = len - 1; i >= 0; i--)
927 if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
933 static void get_line(char *d, size_t size, const char *s)
938 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
947 static void keys_write_all(FILE *f)
951 for (d = 0; d < IN_MAX_DEVS; d++)
953 const int *binds = in_get_dev_binds(d);
954 const char *name = in_get_dev_name(d, 0, 0);
957 if (binds == NULL || name == NULL)
960 fprintf(f, "binddev = %s\n", name);
961 in_get_config(d, IN_CFG_BIND_COUNT, &count);
963 for (k = 0; k < count; k++)
968 act[0] = act[31] = 0;
969 name = in_get_key_name(d, k);
971 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
972 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
973 mask = me_ctrl_actions[i].mask;
975 strncpy(act, me_ctrl_actions[i].name, 31);
976 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
979 mask = me_ctrl_actions[i].mask << 16;
981 strncpy(act, me_ctrl_actions[i].name, 31);
982 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
987 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
988 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
989 mask = emuctrl_actions[i].mask;
991 strncpy(act, emuctrl_actions[i].name, 31);
992 fprintf(f, "bind %s = %s\n", name, mystrip(act));
998 for (k = 0; k < array_size(in_adev); k++)
1000 if (in_adev[k] == d)
1001 fprintf(f, "bind_analog = %d\n", k);
1006 static int parse_bind_val(const char *val, int *type)
1010 *type = IN_BINDTYPE_NONE;
1014 if (strncasecmp(val, "player", 6) == 0)
1016 int player, shift = 0;
1017 player = atoi(val + 6) - 1;
1019 if ((unsigned int)player > 1)
1024 *type = IN_BINDTYPE_PLAYER12;
1025 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
1026 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
1027 return me_ctrl_actions[i].mask << shift;
1030 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
1031 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
1032 *type = IN_BINDTYPE_EMU;
1033 return emuctrl_actions[i].mask;
1040 static void keys_load_all(const char *cfg)
1042 char dev[256], key[128], *act;
1048 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
1051 // don't strip 'dev' because there are weird devices
1052 // with names with space at the end
1053 get_line(dev, sizeof(dev), p);
1055 dev_id = in_config_parse_dev(dev);
1057 printf("input: can't handle dev: %s\n", dev);
1061 in_unbind_all(dev_id, -1, -1);
1062 while ((p = strstr(p, "bind"))) {
1063 if (strncmp(p, "binddev = ", 10) == 0)
1066 if (strncmp(p, "bind_analog", 11) == 0) {
1067 ret = sscanf(p, "bind_analog = %d", &bind);
1070 printf("input: parse error: %16s..\n", p);
1073 if ((unsigned int)bind >= array_size(in_adev)) {
1074 printf("input: analog id %d out of range\n", bind);
1077 in_adev[bind] = dev_id;
1083 printf("input: parse error: %16s..\n", p);
1087 get_line(key, sizeof(key), p);
1088 act = strchr(key, '=');
1090 printf("parse failed: %16s..\n", p);
1098 bind = parse_bind_val(act, &bindtype);
1099 if (bind != -1 && bind != 0) {
1100 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
1101 in_config_bind_key(dev_id, key, bind, bindtype);
1104 lprintf("config: unhandled action \"%s\"\n", act);
1110 static int key_config_loop_wrap(int id, int keys)
1113 case MA_CTRL_PLAYER1:
1114 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
1116 case MA_CTRL_PLAYER2:
1117 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
1120 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
1128 static const char h_nubmode[] = "Maps nub-like analog controls to PSX ones better\n"
1129 "Might cause problems with real analog sticks";
1130 static const char *adevnames[IN_MAX_DEVS + 2];
1131 static int stick_sel[2];
1133 static menu_entry e_menu_keyconfig_analog[] =
1135 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
1136 mee_range (" X axis", 0, in_adev_axis[0][0], 0, 7),
1137 mee_range (" Y axis", 0, in_adev_axis[0][1], 0, 7),
1138 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[0], 1, h_nubmode),
1139 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
1140 mee_range (" X axis", 0, in_adev_axis[1][0], 0, 7),
1141 mee_range (" Y axis", 0, in_adev_axis[1][1], 0, 7),
1142 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[1], 1, h_nubmode),
1146 static int key_config_analog(int id, int keys)
1148 int i, d, count, sel = 0;
1149 int sel2dev_map[IN_MAX_DEVS];
1151 memset(adevnames, 0, sizeof(adevnames));
1152 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1153 memset(stick_sel, 0, sizeof(stick_sel));
1155 adevnames[0] = "None";
1157 for (d = 0; d < IN_MAX_DEVS; d++)
1159 const char *name = in_get_dev_name(d, 0, 1);
1164 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1168 if (in_adev[0] == d) stick_sel[0] = i;
1169 if (in_adev[1] == d) stick_sel[1] = i;
1171 adevnames[i++] = name;
1173 adevnames[i] = NULL;
1175 me_loop(e_menu_keyconfig_analog, &sel);
1177 in_adev[0] = sel2dev_map[stick_sel[0]];
1178 in_adev[1] = sel2dev_map[stick_sel[1]];
1183 static const char *mgn_dev_name(int id, int *offs)
1185 const char *name = NULL;
1188 if (id == MA_CTRL_DEV_FIRST)
1191 for (; it < IN_MAX_DEVS; it++) {
1192 name = in_get_dev_name(it, 1, 1);
1201 static const char *mgn_saveloadcfg(int id, int *offs)
1206 static int mh_savecfg(int id, int keys)
1208 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1209 menu_update_msg("config saved");
1211 menu_update_msg("failed to write config");
1216 static int mh_input_rescan(int id, int keys)
1218 //menu_sync_config();
1220 menu_update_msg("rescan complete.");
1225 static const char *men_in_type_sel[] = {
1226 "Standard (SCPH-1080)",
1227 "Analog (SCPH-1150)",
1233 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1234 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1235 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1237 static menu_entry e_menu_keyconfig[] =
1239 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1240 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1241 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
1242 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
1244 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1245 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
1246 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
1247 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
1248 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
1249 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1250 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1251 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1252 mee_handler ("Rescan devices:", mh_input_rescan),
1254 mee_label_mk (MA_CTRL_DEV_FIRST, 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),
1257 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1258 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1259 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1260 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1264 static int menu_loop_keyconfig(int id, int keys)
1268 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1269 me_loop(e_menu_keyconfig, &sel);
1273 // ------------ gfx options menu ------------
1275 static const char *men_scaler[] = {
1276 "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL
1278 static const char *men_soft_filter[] = { "None",
1280 "scale2x", "eagle2x",
1283 static const char *men_dummy[] = { NULL };
1284 static const char *men_centering[] = { "Auto", "Ingame", "Borderless", "Force", NULL };
1285 static const char *men_overscan[] = { "OFF", "Auto", "Hack", NULL };
1286 static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
1287 "int. 4:3 - uses integer if possible, else fractional";
1288 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1289 "using d-pad or move it using R+d-pad";
1290 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1291 static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
1293 static const char *men_scanlines[] = { "OFF", "1", "2", "3", NULL };
1294 static const char h_scanline_l[] = "Scanline brightness, 0-100%";
1297 static int menu_loop_cscaler(int id, int keys)
1299 void *saved_layer = NULL;
1300 size_t saved_layer_size = 0;
1301 int was_layer_clipped = 0;
1307 g_scaler = SCALE_CUSTOM;
1308 saved_layer_size = last_vout_w * last_vout_h * last_vout_bpp / 8;
1309 saved_layer = malloc(saved_layer_size);
1311 memcpy(saved_layer, pl_vout_buf, saved_layer_size);
1313 plat_gvideo_open(Config.PsxType);
1315 menu_draw_begin(0, 1);
1316 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1321 if (saved_layer && last_vout_bpp == 16) {
1322 int top_x = max(0, -g_layer_x * last_vout_w / 800) + 1;
1323 int top_y = max(0, -g_layer_y * last_vout_h / 480) + 1;
1325 memcpy(pl_vout_buf, saved_layer, saved_layer_size);
1326 snprintf(text, sizeof(text), "%d,%d %dx%d",
1327 g_layer_x, g_layer_y, g_layer_w, g_layer_h);
1328 basic_text_out16_nf(pl_vout_buf, last_vout_w,
1329 top_x, top_y, text);
1330 basic_text_out16_nf(pl_vout_buf, last_vout_w, 2,
1331 last_vout_h - 20, "d-pad: resize, R+d-pad: move");
1332 pl_vout_buf = plat_gvideo_flip();
1335 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1336 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1337 if (inp & PBTN_UP) g_layer_y--;
1338 if (inp & PBTN_DOWN) g_layer_y++;
1339 if (inp & PBTN_LEFT) g_layer_x--;
1340 if (inp & PBTN_RIGHT) g_layer_x++;
1341 if (!(inp & PBTN_R)) {
1342 if (inp & PBTN_UP) g_layer_h += 2;
1343 if (inp & PBTN_DOWN) g_layer_h -= 2;
1344 if (inp & PBTN_LEFT) g_layer_w += 2;
1345 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1347 if (inp & (PBTN_MOK|PBTN_MBACK))
1350 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1351 int layer_clipped = 0;
1352 g_layer_x = max(-320, min(g_layer_x, 640));
1353 g_layer_y = max(-240, min(g_layer_y, 400));
1354 g_layer_w = max(160, g_layer_w);
1355 g_layer_h = max( 60, g_layer_h);
1356 if (g_layer_x < 0 || g_layer_x + g_layer_w > 800)
1358 if (g_layer_w > 800+400)
1359 g_layer_w = 800+400;
1360 if (g_layer_y < 0 || g_layer_y + g_layer_h > 480)
1362 if (g_layer_h > 480+360)
1363 g_layer_h = 480+360;
1365 plat_gvideo_open(Config.PsxType);
1366 if (layer_clipped || was_layer_clipped)
1367 pl_vout_buf = plat_gvideo_set_mode(&last_vout_w,
1368 &last_vout_h, &last_vout_bpp);
1369 was_layer_clipped = layer_clipped;
1373 plat_gvideo_close();
1379 static menu_entry e_menu_gfx_options[] =
1381 mee_enum ("Screen centering", MA_OPT_CENTERING, pl_rearmed_cbs.screen_centering_type, men_centering),
1382 mee_enum ("Show overscan", MA_OPT_OVERSCAN, pl_rearmed_cbs.show_overscan, men_overscan),
1383 mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1384 mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1385 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1386 mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1387 mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1389 mee_enum ("Scanlines", MA_OPT_SCANLINES, scanlines, men_scanlines),
1390 mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1392 mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1393 // mee_onoff ("Vsync", 0, vsync, 1),
1394 mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1398 static int menu_loop_gfx_options(int id, int keys)
1402 me_loop(e_menu_gfx_options, &sel);
1407 // ------------ bios/plugins ------------
1409 #ifdef BUILTIN_GPU_NEON
1411 static const char h_gpu_neon[] =
1412 "Configure built-in NEON GPU plugin";
1413 static const char h_gpu_neon_enhanced[] =
1414 "Renders in double resolution at the cost of lower performance\n"
1415 "(not available for high resolution games)";
1416 static const char h_gpu_neon_enhanced_hack[] =
1417 "Speed hack for above option (glitches some games)";
1418 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1420 static menu_entry e_menu_plugin_gpu_neon[] =
1422 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1423 mee_onoff_h ("Enhanced resolution", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1424 mee_onoff_h ("Enhanced res. speed hack", 0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1425 mee_onoff ("Enh. res. texture adjust", 0, pl_rearmed_cbs.gpu_neon.enhancement_tex_adj, 1),
1429 static int menu_loop_plugin_gpu_neon(int id, int keys)
1432 me_loop(e_menu_plugin_gpu_neon, &sel);
1438 static menu_entry e_menu_plugin_gpu_unai_old[] =
1440 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai_old.lineskip, 1),
1441 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai_old.abe_hack, 1),
1442 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai_old.no_light, 1),
1443 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai_old.no_blend, 1),
1447 static int menu_loop_plugin_gpu_unai_old(int id, int keys)
1450 me_loop(e_menu_plugin_gpu_unai_old, &sel);
1454 static menu_entry e_menu_plugin_gpu_unai[] =
1456 mee_onoff ("Interlace", 0, pl_rearmed_cbs.gpu_unai.ilace_force, 1),
1457 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_unai.dithering, 1),
1458 mee_onoff ("Lighting", 0, pl_rearmed_cbs.gpu_unai.lighting, 1),
1459 mee_onoff ("Fast lighting", 0, pl_rearmed_cbs.gpu_unai.fast_lighting, 1),
1460 mee_onoff ("Blending", 0, pl_rearmed_cbs.gpu_unai.blending, 1),
1461 mee_onoff ("Pixel skip", 0, pl_rearmed_cbs.gpu_unai.pixel_skip, 1),
1465 static int menu_loop_plugin_gpu_unai(int id, int keys)
1468 me_loop(e_menu_plugin_gpu_unai, &sel);
1473 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1474 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1475 static const char h_gpu_1[] = "Capcom fighting games";
1476 static const char h_gpu_2[] = "Black screens in Lunar";
1477 static const char h_gpu_3[] = "Compatibility mode";
1478 static const char h_gpu_6[] = "Pandemonium 2";
1479 //static const char h_gpu_7[] = "Skip every second frame";
1480 static const char h_gpu_8[] = "Needed by Dark Forces";
1481 static const char h_gpu_9[] = "better g-colors, worse textures";
1482 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1484 static menu_entry e_menu_plugin_gpu_peops[] =
1486 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1487 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1488 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1489 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1490 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1491 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1492 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1493 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1494 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1495 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1499 static int menu_loop_plugin_gpu_peops(int id, int keys)
1502 me_loop(e_menu_plugin_gpu_peops, &sel);
1506 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1507 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1508 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1510 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1512 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1513 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1514 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1515 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1516 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1517 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1518 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1519 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1520 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1521 mee_label ("Fixes/hacks:"),
1522 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1523 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1524 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1525 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1526 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1527 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1528 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1529 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1530 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1531 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1532 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1536 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1539 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1543 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1544 static const char h_spu_volboost[] = "Large values cause distortion";
1545 static const char h_spu_tempo[] = "Slows down audio if emu is too slow\n"
1546 "This is inaccurate and breaks games";
1548 static menu_entry e_menu_plugin_spu[] =
1550 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1551 mee_onoff ("Reverb", 0, spu_config.iUseReverb, 1),
1552 mee_enum ("Interpolation", 0, spu_config.iUseInterpolation, men_spu_interp),
1553 //mee_onoff ("Adjust XA pitch", 0, spu_config.iXAPitch, 1),
1554 mee_onoff_h ("Adjust tempo", 0, spu_config.iTempo, 1, h_spu_tempo),
1558 static int menu_loop_plugin_spu(int id, int keys)
1561 me_loop(e_menu_plugin_spu, &sel);
1565 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1566 "savestates and can't be changed there. Must save\n"
1567 "config and reload the game for change to take effect";
1568 static const char h_plugin_gpu[] =
1569 #ifdef BUILTIN_GPU_NEON
1570 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1572 "gpu_peops is Pete's soft GPU, slow but accurate\n"
1573 "gpu_unai_old is from old PCSX4ALL, fast but glitchy\n"
1574 "gpu_unai is newer, more accurate but slower\n"
1575 "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1576 "must save config and reload the game if changed";
1577 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1578 "must save config and reload the game if changed";
1579 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1580 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1581 static const char h_gpu_unai_old[] = "Configure Unai/PCSX4ALL Team GPU plugin (old)";
1582 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team plugin (new)";
1583 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1585 static menu_entry e_menu_plugin_options[] =
1587 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1588 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1589 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1590 #ifdef BUILTIN_GPU_NEON
1591 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1593 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1594 mee_handler_h ("Configure gpu_unai_old GPU plugin", menu_loop_plugin_gpu_unai_old, h_gpu_unai_old),
1595 mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1596 mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1597 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1601 static menu_entry e_menu_main2[];
1603 static int menu_loop_plugin_options(int id, int keys)
1606 me_loop(e_menu_plugin_options, &sel);
1608 // sync BIOS/plugins
1609 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1610 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1611 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1612 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1617 // ------------ adv options menu ------------
1620 static const char h_cfg_noch[] = "Disables game-specific compatibility hacks";
1621 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1622 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1623 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1625 static const char h_cfg_stalls[] = "Will cause some games to run too fast";
1627 static menu_entry e_menu_speed_hacks[] =
1630 mee_onoff_h ("Disable compat hacks", 0, new_dynarec_hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1631 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1632 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1633 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1635 mee_onoff_h ("Disable CPU/GTE stalls", 0, menu_iopts[0], 1, h_cfg_stalls),
1639 static int menu_loop_speed_hacks(int id, int keys)
1642 menu_iopts[0] = Config.DisableStalls;
1643 me_loop(e_menu_speed_hacks, &sel);
1644 Config.DisableStalls = menu_iopts[0];
1648 static const char *men_autooo[] = { "Auto", "Off", "On", NULL };
1650 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1651 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1652 "(green: normal, red: fmod, blue: noise)";
1653 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1654 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1655 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1656 "(proper .cue/.bin dump is needed otherwise)";
1658 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1659 "Might be useful to overcome some dynarec bugs";
1661 static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1662 static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1663 static const char h_cfg_exc[] = "Emulate some PSX's debug hw like breakpoints\n"
1664 "and exceptions (slow, interpreter only, keep off)";
1665 static const char h_cfg_gpul[] = "Try enabling this if the game misses some graphics\n"
1666 "causes a performance hit";
1667 static const char h_cfg_ffps[] = "Instead of 50/60fps for PAL/NTSC use ~49.75/59.81\n"
1668 "Closer to real hw but doesn't match modern displays.";
1669 static const char h_cfg_tcd[] = "Greatly reduce CD load times. Breaks some games.";
1670 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1671 "(adjust this if the game is too slow/too fast/hangs)";
1673 enum { AMO_XA, AMO_CDDA, AMO_IC, AMO_BP, AMO_CPU, AMO_GPUL, AMO_FFPS, AMO_TCD };
1675 static menu_entry e_menu_adv_options[] =
1677 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1678 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1679 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1680 mee_onoff_h ("Disable XA Decoding", 0, menu_iopts[AMO_XA], 1, h_cfg_xa),
1681 mee_onoff_h ("Disable CD Audio", 0, menu_iopts[AMO_CDDA], 1, h_cfg_cdda),
1682 mee_onoff_h ("ICache emulation", 0, menu_iopts[AMO_IC], 1, h_cfg_icache),
1683 mee_onoff_h ("BP exception emulation", 0, menu_iopts[AMO_BP], 1, h_cfg_exc),
1684 mee_enum_h ("GPU l-list slow walking",0, menu_iopts[AMO_GPUL], men_autooo, h_cfg_gpul),
1685 mee_enum_h ("Fractional framerate", 0, menu_iopts[AMO_FFPS], men_autooo, h_cfg_ffps),
1686 mee_onoff_h ("Turbo CD-ROM ", 0, menu_iopts[AMO_TCD], 1, h_cfg_tcd),
1687 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
1688 mee_onoff_h ("Disable dynarec (slow!)",0, menu_iopts[AMO_CPU], 1, h_cfg_nodrc),
1690 mee_range_h ("PSX CPU clock, %", 0, psx_clock, 1, 500, h_cfg_psxclk),
1691 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1695 static int menu_loop_adv_options(int id, int keys)
1702 { &Config.Xa, &menu_iopts[AMO_XA] },
1703 { &Config.Cdda, &menu_iopts[AMO_CDDA] },
1704 { &Config.icache_emulation, &menu_iopts[AMO_IC] },
1705 { &Config.PreciseExceptions, &menu_iopts[AMO_BP] },
1706 { &Config.Cpu, &menu_iopts[AMO_CPU] },
1707 { &Config.TurboCD, &menu_iopts[AMO_TCD] },
1710 for (i = 0; i < ARRAY_SIZE(opts); i++)
1711 *opts[i].mopt = *opts[i].opt;
1712 menu_iopts[AMO_GPUL] = Config.GpuListWalking + 1;
1713 menu_iopts[AMO_FFPS] = Config.FractionalFramerate + 1;
1715 me_loop(e_menu_adv_options, &sel);
1717 for (i = 0; i < ARRAY_SIZE(opts); i++)
1718 *opts[i].opt = *opts[i].mopt;
1719 Config.GpuListWalking = menu_iopts[AMO_GPUL] - 1;
1720 Config.FractionalFramerate = menu_iopts[AMO_FFPS] - 1;
1725 // ------------ options menu ------------
1727 static int mh_restore_defaults(int id, int keys)
1729 menu_set_defconfig();
1730 menu_update_msg("defaults restored");
1734 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1735 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1737 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1738 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1739 "loading state or both";
1741 static const char h_restore_def[] = "Switches back to default / recommended\n"
1743 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1744 static const char h_sputhr[] = "Warning: has some known bugs\n";
1746 static menu_entry e_menu_options[] =
1748 // mee_range ("Save slot", 0, state_slot, 0, 9),
1749 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1750 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1751 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1752 mee_enum ("Region", 0, region, men_region),
1753 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1755 mee_onoff_h ("Use C64x DSP for sound", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1757 mee_onoff_h ("Threaded SPU", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1759 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1760 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1761 mee_handler ("[Advanced]", menu_loop_adv_options),
1762 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1763 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1764 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1768 static int menu_loop_options(int id, int keys)
1772 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1773 me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1774 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1776 me_loop(e_menu_options, &sel);
1781 // ------------ debug menu ------------
1783 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1785 int w = min(g_menuscreen_w, 1024);
1786 int h = min(g_menuscreen_h, 512);
1787 u16 *d = g_menuscreen_ptr;
1788 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1792 gpuf->ulFreezeVersion = 1;
1793 if (GPU_freeze != NULL)
1794 GPU_freeze(1, gpuf);
1796 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1797 bgr555_to_rgb565(d, s, w * 2);
1799 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1800 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1801 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1802 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1803 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1806 static void debug_menu_loop(void)
1808 int inp, df_x = 0, df_y = 0;
1811 gpuf = malloc(sizeof(*gpuf));
1817 menu_draw_begin(0, 1);
1818 draw_frame_debug(gpuf, df_x, df_y);
1821 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1822 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1823 if (inp & PBTN_MBACK) break;
1824 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1825 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1826 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x -= 2; }
1827 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1833 // --------- memcard manager ---------
1835 static void draw_mc_icon(int dx, int dy, const u16 *s)
1840 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1842 for (y = 0; y < 16; y++, s += 16) {
1843 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1844 for (x = 0; x < 16; x++) {
1846 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1847 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1853 static void draw_mc_bg(void)
1855 McdBlock *blocks1, *blocks2;
1859 blocks1 = malloc(15 * sizeof(blocks1[0]));
1860 blocks2 = malloc(15 * sizeof(blocks1[0]));
1861 if (blocks1 == NULL || blocks2 == NULL)
1864 for (i = 0; i < 15; i++) {
1865 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1866 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1869 menu_draw_begin(1, 1);
1871 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1873 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1877 maxicons = g_menuscreen_h / 32;
1880 row2 = g_menuscreen_w / 2;
1881 for (i = 0; i < maxicons; i++) {
1882 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1883 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1885 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1886 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1889 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1897 static void handle_memcard_sel(void)
1899 strcpy(Config.Mcd1, "none");
1900 if (memcard1_sel != 0)
1901 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1902 strcpy(Config.Mcd2, "none");
1903 if (memcard2_sel != 0)
1904 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1905 LoadMcds(Config.Mcd1, Config.Mcd2);
1909 static menu_entry e_memcard_options[] =
1911 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1912 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1916 static int menu_loop_memcards(int id, int keys)
1922 memcard1_sel = memcard2_sel = 0;
1923 p = strrchr(Config.Mcd1, '/');
1925 for (i = 0; memcards[i] != NULL; i++)
1926 if (strcmp(p + 1, memcards[i]) == 0)
1927 { memcard1_sel = i; break; }
1928 p = strrchr(Config.Mcd2, '/');
1930 for (i = 0; memcards[i] != NULL; i++)
1931 if (strcmp(p + 1, memcards[i]) == 0)
1932 { memcard2_sel = i; break; }
1934 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1936 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1941 // ------------ cheats menu ------------
1943 static void draw_cheatlist(int sel)
1945 int max_cnt, start, i, pos, active;
1947 max_cnt = g_menuscreen_h / me_sfont_h;
1948 start = max_cnt / 2 - sel;
1950 menu_draw_begin(1, 1);
1952 for (i = 0; i < NumCheats; i++) {
1954 if (pos < 0) continue;
1955 if (pos >= max_cnt) break;
1956 active = Cheats[i].Enabled;
1957 smalltext_out16(14, pos * me_sfont_h,
1958 active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1959 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1960 Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1964 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1966 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1970 static void menu_loop_cheats(void)
1972 static int menu_sel = 0;
1977 draw_cheatlist(menu_sel);
1978 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1979 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1980 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1981 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1982 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1983 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1984 if (inp & PBTN_MOK) { // action
1985 if (menu_sel < NumCheats)
1986 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1989 if (inp & PBTN_MBACK)
1994 // --------- main menu help ----------
1996 static void menu_bios_warn(void)
1999 static const char msg[] =
2000 "You don't seem to have copied any BIOS\n"
2002 MENU_BIOS_PATH "\n\n"
2004 "While many games work fine with fake\n"
2005 "(HLE) BIOS, others (like MGS and FF8)\n"
2006 "require BIOS to work.\n"
2007 "After copying the file, you'll also need\n"
2008 "to select it in the emu's menu:\n"
2009 "options->[BIOS/Plugins]\n\n"
2010 "The file is usually named SCPH1001.BIN,\n"
2011 "but other not compressed files can be\n"
2013 "Press %s or %s to continue";
2014 char tmp_msg[sizeof(msg) + 64];
2016 snprintf(tmp_msg, sizeof(tmp_msg), msg,
2017 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
2020 draw_menu_message(tmp_msg, NULL);
2022 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2023 if (inp & (PBTN_MBACK|PBTN_MOK))
2028 // ------------ main menu ------------
2030 static menu_entry e_menu_main[];
2032 static void draw_frame_main(void)
2041 if (CdromId[0] != 0) {
2042 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
2043 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
2044 Config.HLE ? "HLE" : "BIOS");
2045 smalltext_out16(4, 1, buff, 0x105f);
2049 capacity = plat_target_bat_capacity_get();
2051 tmp = localtime(<ime);
2052 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
2053 if (capacity >= 0) {
2054 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
2059 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
2063 static void draw_frame_credits(void)
2065 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
2068 static const char credits_text[] =
2070 "(C) 1999-2003 PCSX Team\n"
2071 "(C) 2005-2009 PCSX-df Team\n"
2072 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
2073 "ARM recompiler (C) 2009-2011 Ari64\n"
2074 #ifdef BUILTIN_GPU_NEON
2075 "ARM NEON GPU (c) 2011-2012 Exophase\n"
2077 "PEOpS GPU and SPU by Pete Bernert\n"
2078 " and the P.E.Op.S. team\n"
2079 "PCSX4ALL plugin by PCSX4ALL team\n"
2080 " Chui, Franxis, Unai\n\n"
2081 "integration, optimization and\n"
2082 " frontend (C) 2010-2015 notaz\n";
2084 static int reset_game(void)
2090 if (LoadCdrom() == -1)
2096 static int reload_plugins(const char *cdimg)
2102 set_cd_image(cdimg);
2104 pcnt_hook_plugins();
2106 if (OpenPlugins() == -1) {
2107 menu_update_msg("failed to open plugins");
2110 plugin_call_rearmed_cbs();
2112 cdrIsoMultidiskCount = 1;
2114 CdromLabel[0] = '\0';
2119 static int run_bios(void)
2121 boolean origSlowBoot = Config.SlowBoot;
2127 if (reload_plugins(NULL) != 0)
2129 Config.SlowBoot = 1;
2131 Config.SlowBoot = origSlowBoot;
2137 static int run_exe(void)
2139 const char *exts[] = { "exe", NULL };
2142 fname = menu_loop_romsel(last_selected_fname,
2143 sizeof(last_selected_fname), exts, NULL);
2148 if (reload_plugins(NULL) != 0)
2152 if (Load(fname) != 0) {
2153 menu_update_msg("exe load failed, bad file?");
2162 static int run_cd_image(const char *fname)
2164 int autoload_state = g_autostateld_opt;
2165 size_t fname_len = strlen(fname);
2166 const char *ppfname = NULL;
2169 // simle ppf handling, like game.chd.ppf
2170 if (4 < fname_len && fname_len < sizeof(fname2)
2171 && strcasecmp(fname + fname_len - 4, ".ppf") == 0) {
2172 memcpy(fname2, fname, fname_len - 4);
2173 fname2[fname_len - 4] = 0;
2179 reload_plugins(fname);
2181 // always autodetect, menu_sync_config will override as needed
2184 if (CheckCdrom() == -1) {
2185 // Only check the CD if we are starting the console with a CD
2187 menu_update_msg("unsupported/invalid CD image");
2191 BuildPPFCache(ppfname);
2195 // Read main executable directly from CDRom and start it
2196 if (LoadCdrom() == -1) {
2198 menu_update_msg("failed to load CD image");
2205 if (autoload_state) {
2206 unsigned int newest = 0;
2207 int time = 0, slot, newest_slot = -1;
2209 for (slot = 0; slot < 10; slot++) {
2210 if (emu_check_save_file(slot, &time)) {
2211 if ((unsigned int)time > newest) {
2218 if (newest_slot >= 0) {
2219 lprintf("autoload slot %d\n", newest_slot);
2220 emu_load_state(newest_slot);
2223 lprintf("no save to autoload.\n");
2230 static int romsel_run(void)
2232 int prev_gpu, prev_spu;
2235 fname = menu_loop_romsel(last_selected_fname,
2236 sizeof(last_selected_fname), filter_exts,
2237 optional_cdimg_filter);
2241 printf("selected file: %s\n", fname);
2243 new_dynarec_clear_full();
2245 if (run_cd_image(fname) != 0)
2248 prev_gpu = gpu_plugsel;
2249 prev_spu = spu_plugsel;
2250 if (menu_load_config(1) != 0)
2251 menu_load_config(0);
2253 // check for plugin changes, have to repeat
2254 // loading if game config changed plugins to reload them
2255 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2256 printf("plugin change detected, reloading plugins..\n");
2257 if (run_cd_image(fname) != 0)
2261 strcpy(last_selected_fname, fname);
2262 menu_do_last_cd_img(0);
2266 static int swap_cd_image(void)
2270 fname = menu_loop_romsel(last_selected_fname,
2271 sizeof(last_selected_fname), filter_exts,
2272 optional_cdimg_filter);
2276 printf("selected file: %s\n", fname);
2279 CdromLabel[0] = '\0';
2281 set_cd_image(fname);
2282 if (ReloadCdromPlugin() < 0) {
2283 menu_update_msg("failed to load cdr plugin");
2286 if (CDR_open() < 0) {
2287 menu_update_msg("failed to open cdr plugin");
2291 SetCdOpenCaseTime(time(NULL) + 2);
2294 strcpy(last_selected_fname, fname);
2298 static int swap_cd_multidisk(void)
2300 cdrIsoMultidiskSelect++;
2302 CdromLabel[0] = '\0';
2305 if (CDR_open() < 0) {
2306 menu_update_msg("failed to open cdr plugin");
2310 SetCdOpenCaseTime(time(NULL) + 2);
2316 static void load_pcsx_cht(void)
2318 static const char *exts[] = { "cht", NULL };
2322 fname = menu_loop_romsel(last_selected_fname,
2323 sizeof(last_selected_fname), exts, NULL);
2327 printf("selected cheat file: %s\n", fname);
2330 if (NumCheats == 0 && NumCodes == 0)
2331 menu_update_msg("failed to load cheats");
2333 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2334 menu_update_msg(msg);
2336 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2339 static int main_menu_handler(int id, int keys)
2343 case MA_MAIN_RESUME_GAME:
2347 case MA_MAIN_SAVE_STATE:
2349 return menu_loop_savestate(0);
2351 case MA_MAIN_LOAD_STATE:
2353 return menu_loop_savestate(1);
2355 case MA_MAIN_RESET_GAME:
2356 if (ready_to_go && reset_game() == 0)
2359 case MA_MAIN_LOAD_ROM:
2360 if (romsel_run() == 0)
2363 case MA_MAIN_SWAP_CD:
2364 if (swap_cd_image() == 0)
2367 case MA_MAIN_SWAP_CD_MULTI:
2368 if (swap_cd_multidisk() == 0)
2371 case MA_MAIN_RUN_BIOS:
2372 if (run_bios() == 0)
2375 case MA_MAIN_RUN_EXE:
2379 case MA_MAIN_CHEATS:
2382 case MA_MAIN_LOAD_CHEATS:
2385 case MA_MAIN_CREDITS:
2386 draw_menu_message(credits_text, draw_frame_credits);
2387 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2390 emu_core_ask_exit();
2393 lprintf("%s: something unknown selected\n", __FUNCTION__);
2400 static menu_entry e_menu_main2[] =
2402 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2403 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2404 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2405 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2406 mee_handler ("Memcard manager", menu_loop_memcards),
2407 mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS, main_menu_handler),
2411 static int main_menu2_handler(int id, int keys)
2415 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2416 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2417 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2418 me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2420 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2423 static const char h_extra[] = "Change CD, manage memcards..\n";
2425 static menu_entry e_menu_main[] =
2429 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2430 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2431 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2432 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2433 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2434 mee_handler ("Options", menu_loop_options),
2435 mee_handler ("Controls", menu_loop_keyconfig),
2436 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
2437 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2438 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2439 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2443 // ----------------------------
2445 static void menu_leave_emu(void);
2447 void menu_loop(void)
2449 static int warned_about_bios = 0;
2454 if (config_save_counter == 0) {
2456 if (bioses[1] != NULL) {
2457 // autoselect BIOS to make user's life easier
2458 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2461 else if (!warned_about_bios) {
2463 warned_about_bios = 1;
2467 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2468 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2469 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2470 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2471 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2473 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2476 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2477 } while (!ready_to_go && !g_emu_want_quit);
2479 /* wait until menu, ok, back is released */
2480 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2483 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2488 static int qsort_strcmp(const void *p1, const void *p2)
2490 char * const *s1 = (char * const *)p1;
2491 char * const *s2 = (char * const *)p2;
2492 return strcasecmp(*s1, *s2);
2495 static void scan_bios_plugins(void)
2497 char fname[MAXPATHLEN];
2499 int bios_i, gpu_i, spu_i, mc_i;
2504 gpu_plugins[0] = "builtin_gpu";
2505 spu_plugins[0] = "builtin_spu";
2506 memcards[0] = "(none)";
2507 bios_i = gpu_i = spu_i = mc_i = 1;
2509 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2510 dir = opendir(fname);
2512 perror("scan_bios_plugins bios opendir");
2527 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2530 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2531 if (stat(fname, &st) != 0
2532 || (st.st_size != 512*1024 && st.st_size != 4*1024*1024)) {
2533 printf("bad BIOS file: %s\n", ent->d_name);
2537 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2538 bioses[bios_i++] = strdup(ent->d_name);
2542 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2548 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2549 dir = opendir(fname);
2551 perror("scan_bios_plugins plugins opendir");
2565 p = strstr(ent->d_name, ".so");
2569 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2570 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2572 fprintf(stderr, "%s\n", dlerror());
2576 // now what do we have here?
2577 tmp = dlsym(h, "GPUinit");
2580 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2581 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2585 tmp = dlsym(h, "SPUinit");
2588 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2589 spu_plugins[spu_i++] = strdup(ent->d_name);
2593 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2600 dir = opendir("." MEMCARD_DIR);
2602 perror("scan_bios_plugins memcards opendir");
2617 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2620 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2621 if (stat(fname, &st) != 0) {
2622 printf("bad memcard file: %s\n", ent->d_name);
2626 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2627 memcards[mc_i++] = strdup(ent->d_name);
2631 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2635 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2640 void menu_init(void)
2642 char buff[MAXPATHLEN];
2645 cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2647 scan_bios_plugins();
2650 menu_set_defconfig();
2651 menu_load_config(0);
2652 menu_do_last_cd_img(1);
2657 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2658 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2659 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2660 fprintf(stderr, "OOM\n");
2664 emu_make_path(buff, "skin/background.png", sizeof(buff));
2665 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2667 i = plat_target.cpu_clock_set != NULL
2668 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2669 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, i);
2671 i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2672 e_menu_gfx_options[i].data = plat_target.vout_methods;
2673 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2674 plat_target.vout_methods != NULL);
2676 i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2677 e_menu_gfx_options[i].data = plat_target.hwfilters;
2678 me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2679 plat_target.hwfilters != NULL);
2681 me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2682 plat_target.gamma_set != NULL);
2684 #ifdef HAVE_PRE_ARMV7
2685 me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2687 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2688 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2689 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2690 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2691 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2692 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2693 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2696 void menu_notify_mode_change(int w, int h, int bpp)
2700 last_vout_bpp = bpp;
2703 static void menu_leave_emu(void)
2705 if (GPU_close != NULL) {
2706 int ret = GPU_close();
2708 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2711 plat_video_menu_enter(ready_to_go);
2713 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2714 if (pl_vout_buf != NULL && ready_to_go) {
2715 int x = max(0, g_menuscreen_w - last_vout_w);
2716 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2717 int w = min(g_menuscreen_w, last_vout_w);
2718 int h = min(g_menuscreen_h, last_vout_h);
2719 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2720 char *s = pl_vout_buf;
2722 if (last_vout_bpp == 16) {
2723 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2724 menu_darken_bg(d, s, w, 0);
2727 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2728 rgb888_to_rgb565(d, s, w * 3);
2729 menu_darken_bg(d, d, w, 0);
2735 cpu_clock = plat_target_cpu_clock_get();
2738 void menu_prepare_emu(void)
2740 R3000Acpu *prev_cpu = psxCpu;
2742 plat_video_menu_leave();
2744 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
2745 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2749 if (psxCpu != prev_cpu) {
2750 prev_cpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
2751 prev_cpu->Shutdown();
2754 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
2758 psxCpu->ApplyConfig();
2760 // core doesn't care about Config.Cdda changes,
2761 // so handle them manually here
2766 plat_target_cpu_clock_set(cpu_clock);
2768 // push config to GPU plugin
2769 plugin_call_rearmed_cbs();
2771 if (GPU_open != NULL) {
2772 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2774 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2778 void menu_update_msg(const char *msg)
2780 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2781 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2783 menu_error_time = plat_get_ticks_ms();
2784 lprintf("msg: %s\n", menu_error_msg);
2787 void menu_finish(void)
2789 if (cpu_clock_st > 0)
2790 plat_target_cpu_clock_set(cpu_clock_st);