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.
19 #include <sys/types.h>
28 #include "plugin_lib.h"
32 #include "libpicofe/plat.h"
33 #include "libpicofe/input.h"
34 #include "libpicofe/linux/in_evdev.h"
35 #include "libpicofe/plat.h"
36 #include "../libpcsxcore/misc.h"
37 #include "../libpcsxcore/cdrom.h"
38 #include "../libpcsxcore/cdrom-async.h"
39 #include "../libpcsxcore/cdriso.h"
40 #include "../libpcsxcore/cheat.h"
41 #include "../libpcsxcore/ppf.h"
42 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
43 #include "../plugins/dfsound/spu_config.h"
44 #include "psemu_plugin_defs.h"
45 #include "compiler_features.h"
46 #include "arm_features.h"
49 #define REARMED_BIRTHDAY_TIME 1293306830 /* 25 Dec 2010 */
50 #if defined(__linux__) && (!defined(__SIZEOF_POINTER__) || __SIZEOF_POINTER__ == 4)
56 #define array_size(x) (sizeof(x) / sizeof(x[0]))
67 MA_MAIN_SWAP_CD_MULTI,
99 MA_OPT_SCANLINE_LEVEL,
105 static int last_vout_w, last_vout_h, last_vout_bpp;
106 static int cpu_clock, cpu_clock_st, volume_boost;
107 static int frameskip = 1; // 0 - auto, 1 - off
108 static char last_selected_fname[MAXPATHLEN];
109 static int config_save_counter, region, in_type_sel1, in_type_sel2;
110 static int psx_clock;
111 static int memcard1_sel = -1, memcard2_sel = -1;
112 static int cd_buf_count;
113 extern int g_autostateld_opt;
114 static int menu_iopts[8];
115 int g_opts, g_scaler, g_gamma = 100;
116 int scanlines, scanline_level = 20;
117 int soft_scaling, analog_deadzone; // for Caanoo
119 int in_evdev_allow_abs_only attr_weak; // FIXME
121 #ifndef HAVE_PRE_ARMV7
122 #define DEFAULT_PSX_CLOCK (10000 / CYCLE_MULT_DEFAULT)
123 #define DEFAULT_PSX_CLOCK_S "57"
125 #define DEFAULT_PSX_CLOCK 50
126 #define DEFAULT_PSX_CLOCK_S "50"
129 static const char *bioses[32];
130 static const char *gpu_plugins[16];
131 static const char *spu_plugins[16];
132 static const char *memcards[32];
133 static int bios_sel, gpu_plugsel, spu_plugsel;
135 #ifndef UI_FEATURES_H
136 #define MENU_SHOW_VOUTMODE 1
137 #define MENU_SHOW_SCALER2 0
138 #define MENU_SHOW_NUBS_BTNS 0
139 #define MENU_SHOW_VIBRATION 0
140 #define MENU_SHOW_DEADZONE 0
141 #define MENU_SHOW_MINIMIZE 0
142 #define MENU_SHOW_FULLSCREEN 1
143 #define MENU_SHOW_VOLUME 0
145 #ifndef MENU_SHOW_VARSCALER
146 #define MENU_SHOW_VARSCALER 0
148 #ifndef MENU_SHOW_VARSCALER_C
149 #define MENU_SHOW_VARSCALER_C 0
152 static int min(int x, int y) { return x < y ? x : y; }
153 static int max(int x, int y) { return x > y ? x : y; }
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),
416 CE_CONFIG_VAL(SlowBoot),
418 CE_INTVAL_V(g_scaler, 3),
420 CE_INTVAL(g_layer_x),
421 CE_INTVAL(g_layer_y),
422 CE_INTVAL(g_layer_w),
423 CE_INTVAL(g_layer_h),
424 CE_INTVAL(soft_filter),
425 CE_INTVAL(scanlines),
426 CE_INTVAL(scanline_level),
427 CE_INTVAL(plat_target.vout_method),
428 CE_INTVAL(plat_target.hwfilter),
429 CE_INTVAL(plat_target.vout_fullscreen),
430 CE_INTVAL(state_slot),
431 CE_INTVAL(cpu_clock),
433 CE_INTVAL(in_type_sel1),
434 CE_INTVAL(in_type_sel2),
435 CE_INTVAL(analog_deadzone),
436 CE_INTVAL(memcard1_sel),
437 CE_INTVAL(memcard2_sel),
438 CE_INTVAL(g_autostateld_opt),
439 CE_INTVAL(cd_buf_count),
440 CE_INTVAL_N("adev0_axis0", in_adev_axis[0][0]),
441 CE_INTVAL_N("adev0_axis1", in_adev_axis[0][1]),
442 CE_INTVAL_N("adev1_axis0", in_adev_axis[1][0]),
443 CE_INTVAL_N("adev1_axis1", in_adev_axis[1][1]),
444 CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
445 CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
446 CE_INTVAL_V(frameskip, 4),
447 CE_INTVAL_PV(dithering, 2),
448 CE_INTVAL_P(gpu_peops.dwActFixes),
449 CE_INTVAL_P(gpu_unai.old_renderer),
450 CE_INTVAL_P(gpu_unai.ilace_force),
451 CE_INTVAL_P(gpu_unai.lighting),
452 CE_INTVAL_P(gpu_unai.fast_lighting),
453 CE_INTVAL_P(gpu_unai.blending),
454 CE_INTVAL_P(gpu_unai.scale_hires),
455 CE_INTVAL_P(gpu_neon.allow_interlace),
456 CE_INTVAL_P(gpu_neon.enhancement_enable),
457 CE_INTVAL_P(gpu_neon.enhancement_no_main),
458 CE_INTVAL_PV(gpu_neon.enhancement_tex_adj, 2),
459 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
460 CE_INTVAL_P(gpu_peopsgl.iFilterType),
461 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
462 CE_INTVAL_P(gpu_peopsgl.iUseMask),
463 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
464 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
465 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
466 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
467 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
468 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
469 CE_INTVAL_P(screen_centering_type),
470 CE_INTVAL_P(screen_centering_x),
471 CE_INTVAL_P(screen_centering_y),
472 CE_INTVAL_P(screen_centering_h_adj),
473 CE_INTVAL_P(show_overscan),
474 CE_INTVAL(spu_config.iUseReverb),
475 CE_INTVAL(spu_config.iXAPitch),
476 CE_INTVAL(spu_config.iUseInterpolation),
477 CE_INTVAL(spu_config.iTempo),
478 CE_INTVAL(spu_config.iUseThread),
479 CE_INTVAL(config_save_counter),
480 CE_INTVAL(in_evdev_allow_abs_only),
481 CE_INTVAL(volume_boost),
482 CE_INTVAL(psx_clock),
483 CE_INTVAL(ndrc_g.hacks),
484 CE_INTVAL(in_enable_vibration),
487 static char *get_cd_label(void)
489 static char trimlabel[33];
492 strncpy(trimlabel, CdromLabel, 32);
494 for (j = 31; j >= 0; j--)
495 if (trimlabel[j] == ' ')
501 static void make_cfg_fname(char *buf, size_t size, int is_game)
505 snprintf(id_buf, sizeof(id_buf), "%.32s-%.9s.cfg",
506 get_cd_label(), CdromId);
507 emu_make_path(buf, size, CFG_DIR, id_buf);
510 emu_make_path(buf, size, PCSX_DOT_DIR, cfgfile_basename);
513 static void keys_write_all(FILE *f);
514 static char *mystrip(char *str);
516 static void write_u32_value(FILE *f, u32 v)
520 fprintf(f, "%x\n", v);
523 static int menu_write_config(int is_game)
525 char cfgfile[MAXPATHLEN];
529 config_save_counter++;
531 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
532 f = fopen(cfgfile, "w");
534 printf("menu_write_config: failed to open: %s\n", cfgfile);
538 cd_buf_count = cdra_get_buf_count();
540 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
541 fprintf(f, "%s = ", config_data[i].name);
542 switch (config_data[i].len) {
544 fprintf(f, "%s\n", (char *)config_data[i].val);
547 write_u32_value(f, *(u8 *)config_data[i].val);
550 write_u32_value(f, *(u16 *)config_data[i].val);
553 write_u32_value(f, *(u32 *)config_data[i].val);
556 printf("menu_write_config: unhandled len %d for %s\n",
557 (int)config_data[i].len, config_data[i].name);
568 static int menu_do_last_cd_img(int is_get)
570 static const char *defaults[] = { "/media", "/mnt/sd", "/mnt" };
576 emu_make_path(path, sizeof(path), PCSX_DOT_DIR, "lastcdimg.txt");
577 f = fopen(path, is_get ? "r" : "w");
584 ret = fread(last_selected_fname, 1, sizeof(last_selected_fname) - 1, f);
585 last_selected_fname[ret] = 0;
586 mystrip(last_selected_fname);
589 fprintf(f, "%s\n", last_selected_fname);
594 for (i = 0; last_selected_fname[0] == 0
595 || STAT(last_selected_fname, &st) != 0; i++)
597 if (i >= ARRAY_SIZE(defaults))
599 strcpy(last_selected_fname, defaults[i]);
606 static void parse_str_val(char *cval, const char *src)
609 strncpy(cval, src, MAXPATHLEN);
610 cval[MAXPATHLEN - 1] = 0;
611 tmp = strchr(cval, '\n');
613 tmp = strchr(cval, '\r');
618 static void keys_load_all(const char *cfg);
620 int menu_load_config(int is_game)
622 char cfgfile[MAXPATHLEN];
628 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
629 f = fopen(cfgfile, "r");
631 printf("menu_load_config: failed to open: %s\n", cfgfile);
635 fseek(f, 0, SEEK_END);
638 printf("bad size %ld: %s\n", size, cfgfile);
642 cfg = malloc(size + 1);
646 fseek(f, 0, SEEK_SET);
647 if (fread(cfg, 1, size, f) != size) {
648 printf("failed to read: %s\n", cfgfile);
653 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
657 tmp = strstr(cfg, config_data[i].name);
660 tmp += strlen(config_data[i].name);
661 if (strncmp(tmp, " = ", 3) != 0)
665 if (config_data[i].len == 0) {
666 parse_str_val(config_data[i].val, tmp);
671 val = strtoul(tmp, &tmp2, 16);
672 if (tmp2 == NULL || tmp == tmp2)
673 continue; // parse failed
675 switch (config_data[i].len) {
677 *(u8 *)config_data[i].val = val;
680 *(u16 *)config_data[i].val = val;
683 *(u32 *)config_data[i].val = val;
686 printf("menu_load_config: unhandled len %d for %s\n",
687 (int)config_data[i].len, config_data[i].name);
693 char *tmp = strstr(cfg, "lastcdimg = ");
696 parse_str_val(last_selected_fname, tmp);
701 cdra_set_buf_count(cd_buf_count);
712 for (i = bios_sel = 0; bioses[i] != NULL; i++)
713 if (strcmp(Config.Bios, bioses[i]) == 0)
714 { bios_sel = i; break; }
716 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
717 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
718 { gpu_plugsel = i; break; }
720 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
721 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
722 { spu_plugsel = i; break; }
724 // memcard selections
725 char mcd1_old[sizeof(Config.Mcd1)];
726 char mcd2_old[sizeof(Config.Mcd2)];
727 strcpy(mcd1_old, Config.Mcd1);
728 strcpy(mcd2_old, Config.Mcd2);
730 if ((unsigned int)memcard1_sel < ARRAY_SIZE(memcards)) {
731 if (memcard1_sel == 0)
732 strcpy(Config.Mcd1, "none");
733 else if (memcards[memcard1_sel] != NULL)
734 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s",
735 MEMCARD_DIR, memcards[memcard1_sel]);
737 if ((unsigned int)memcard2_sel < ARRAY_SIZE(memcards)) {
738 if (memcard2_sel == 0)
739 strcpy(Config.Mcd2, "none");
740 else if (memcards[memcard2_sel] != NULL)
741 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s",
742 MEMCARD_DIR, memcards[memcard2_sel]);
744 if (strcmp(mcd1_old, Config.Mcd1) || strcmp(mcd2_old, Config.Mcd2))
745 LoadMcds(Config.Mcd1, Config.Mcd2);
750 static const char *filter_exts[] = {
751 "bin", "img", "mdf", "iso", "cue", "z",
755 "bz", "znx", "pbp", "cbn", "ppf", NULL
758 // rrrr rggg gggb bbbb
759 static unsigned short fname2color(const char *fname)
761 static const char *other_exts[] = {
762 "ccd", "toc", "mds", "sub", "table", "index", "sbi"
764 const char *ext = strrchr(fname, '.');
770 for (i = 0; filter_exts[i] != NULL; i++)
771 if (strcasecmp(ext, filter_exts[i]) == 0)
773 for (i = 0; i < array_size(other_exts); i++)
774 if (strcasecmp(ext, other_exts[i]) == 0)
779 static void draw_savestate_bg(int slot);
781 #define MENU_ALIGN_LEFT
782 #ifndef HAVE_PRE_ARMV7 // assume hires device
788 #include "libpicofe/menu.c"
790 // a bit of black magic here
791 static void draw_savestate_bg(int slot)
793 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
795 char fname[MAXPATHLEN];
802 ret = get_state_filename(fname, sizeof(fname), slot);
806 f = gzopen(fname, "rb");
810 if ((ret = (int)gzseek(f, 0x29933d, SEEK_SET)) != 0x29933d) {
811 fprintf(stderr, "gzseek failed: %d\n", ret);
816 gpu = malloc(sizeof(*gpu));
822 ret = gzread(f, gpu, sizeof(*gpu));
824 if (ret != sizeof(*gpu)) {
825 fprintf(stderr, "gzread failed\n");
829 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
831 if (gpu->ulStatus & 0x800000)
832 goto out; // disabled
834 x = gpu->ulControl[5] & 0x3ff;
835 y = (gpu->ulControl[5] >> 10) & 0x1ff;
836 w = psx_widths[(gpu->ulStatus >> 16) & 7];
837 tmp = gpu->ulControl[7];
838 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
839 if (gpu->ulStatus & 0x80000) // doubleheight
841 if (h <= 0 || h > 512)
847 s = (u16 *)gpu->psxVRam + y * 1024 + x;
849 x = max(0, g_menuscreen_w - w) & ~3;
850 y = max(0, g_menuscreen_h / 2 - h / 2);
851 w = min(g_menuscreen_w, w);
852 h = min(g_menuscreen_h, h);
853 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
855 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
856 if (gpu->ulStatus & 0x200000)
857 bgr888_to_rgb565(d, s, w * 3);
859 bgr555_to_rgb565(d, s, w * 2);
861 // darken this so that menu text is visible
862 if (g_menuscreen_w - w < 320)
863 menu_darken_bg(d, d, w, 0);
870 // -------------- key config --------------
872 me_bind_action me_ctrl_actions[] =
874 { "UP ", 1 << DKEY_UP},
875 { "DOWN ", 1 << DKEY_DOWN },
876 { "LEFT ", 1 << DKEY_LEFT },
877 { "RIGHT ", 1 << DKEY_RIGHT },
878 { "TRIANGLE", 1 << DKEY_TRIANGLE },
879 { "CIRCLE ", 1 << DKEY_CIRCLE },
880 { "CROSS ", 1 << DKEY_CROSS },
881 { "SQUARE ", 1 << DKEY_SQUARE },
882 { "L1 ", 1 << DKEY_L1 },
883 { "R1 ", 1 << DKEY_R1 },
884 { "L2 ", 1 << DKEY_L2 },
885 { "R2 ", 1 << DKEY_R2 },
886 { "L3 ", 1 << DKEY_L3 },
887 { "R3 ", 1 << DKEY_R3 },
888 { "START ", 1 << DKEY_START },
889 { "SELECT ", 1 << DKEY_SELECT },
893 me_bind_action emuctrl_actions[] =
895 { "Save State ", 1 << SACTION_SAVE_STATE },
896 { "Load State ", 1 << SACTION_LOAD_STATE },
897 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
898 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
899 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
900 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
901 { "Show/Hide FPS ", 1 << SACTION_TOGGLE_FPS },
902 #ifndef HAVE_PRE_ARMV7
903 { "Switch Renderer ", 1 << SACTION_SWITCH_DISPMODE },
905 { "Fast Forward ", 1 << SACTION_FAST_FORWARD },
906 #if MENU_SHOW_MINIMIZE
907 { "Minimize ", 1 << SACTION_MINIMIZE },
909 #if MENU_SHOW_FULLSCREEN
910 { "Toggle fullscreen", 1 << SACTION_TOGGLE_FULLSCREEN },
912 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
913 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
914 { "Gun A button ", 1 << SACTION_GUN_A },
915 { "Gun B button ", 1 << SACTION_GUN_B },
916 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
918 { "Volume Up ", 1 << SACTION_VOLUME_UP },
919 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
921 { "Analog toggle ", 1 << SACTION_ANALOG_TOGGLE },
925 static char *mystrip(char *str)
930 for (i = 0; i < len; i++)
931 if (str[i] != ' ') break;
932 if (i > 0) memmove(str, str + i, len - i + 1);
935 for (i = len - 1; i >= 0; i--)
936 if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
942 static void get_line(char *d, size_t size, const char *s)
947 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
956 static void keys_write_all(FILE *f)
960 for (d = 0; d < IN_MAX_DEVS; d++)
962 const int *binds = in_get_dev_binds(d);
963 const char *name = in_get_dev_name(d, 0, 0);
966 if (binds == NULL || name == NULL)
969 fprintf(f, "binddev = %s\n", name);
970 in_get_config(d, IN_CFG_BIND_COUNT, &count);
972 for (k = 0; k < count; k++)
977 act[0] = act[31] = 0;
978 name = in_get_key_name(d, k);
980 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
981 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
982 mask = me_ctrl_actions[i].mask;
984 strncpy(act, me_ctrl_actions[i].name, 31);
985 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
988 mask = me_ctrl_actions[i].mask << 16;
990 strncpy(act, me_ctrl_actions[i].name, 31);
991 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
996 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
997 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
998 mask = emuctrl_actions[i].mask;
1000 strncpy(act, emuctrl_actions[i].name, 31);
1001 fprintf(f, "bind %s = %s\n", name, mystrip(act));
1007 for (k = 0; k < array_size(in_adev); k++)
1009 if (in_adev[k] == d)
1010 fprintf(f, "bind_analog = %d\n", k);
1015 static int parse_bind_val(const char *val, int *type)
1019 *type = IN_BINDTYPE_NONE;
1023 if (strncasecmp(val, "player", 6) == 0)
1025 int player, shift = 0;
1026 player = atoi(val + 6) - 1;
1028 if ((unsigned int)player > 1)
1033 *type = IN_BINDTYPE_PLAYER12;
1034 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
1035 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
1036 return me_ctrl_actions[i].mask << shift;
1039 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
1040 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
1041 *type = IN_BINDTYPE_EMU;
1042 return emuctrl_actions[i].mask;
1049 static void keys_load_all(const char *cfg)
1051 char dev[256], key[128], *act;
1057 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
1060 // don't strip 'dev' because there are weird devices
1061 // with names with space at the end
1062 get_line(dev, sizeof(dev), p);
1064 dev_id = in_config_parse_dev(dev);
1066 printf("input: can't handle dev: %s\n", dev);
1070 in_unbind_all(dev_id, -1, -1);
1071 while ((p = strstr(p, "bind"))) {
1072 if (strncmp(p, "binddev = ", 10) == 0)
1075 if (strncmp(p, "bind_analog", 11) == 0) {
1076 ret = sscanf(p, "bind_analog = %d", &bind);
1079 printf("input: parse error: %16s..\n", p);
1082 if ((unsigned int)bind >= array_size(in_adev)) {
1083 printf("input: analog id %d out of range\n", bind);
1086 in_adev[bind] = dev_id;
1092 printf("input: parse error: %16s..\n", p);
1096 get_line(key, sizeof(key), p);
1097 act = strchr(key, '=');
1099 printf("parse failed: %16s..\n", p);
1107 bind = parse_bind_val(act, &bindtype);
1108 if (bind != -1 && bind != 0) {
1109 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
1110 in_config_bind_key(dev_id, key, bind, bindtype);
1113 lprintf("config: unhandled action \"%s\"\n", act);
1119 static int key_config_loop_wrap(int id, int keys)
1123 for (d = 0; d < IN_MAX_DEVS; d++)
1124 in_set_config_int(d, IN_CFG_ANALOG_MAP_ULDR, 0);
1126 case MA_CTRL_PLAYER1:
1127 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
1129 case MA_CTRL_PLAYER2:
1130 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
1133 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
1138 for (d = 0; d < IN_MAX_DEVS; d++)
1139 in_set_config_int(d, IN_CFG_ANALOG_MAP_ULDR, 1);
1144 static const char h_nubmode[] = "Maps nub-like analog controls to PSX ones better\n"
1145 "Might cause problems with real analog sticks";
1146 static const char *adevnames[IN_MAX_DEVS + 2];
1147 static int stick_sel[2];
1149 static menu_entry e_menu_keyconfig_analog[] =
1151 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
1152 mee_range (" X axis", 0, in_adev_axis[0][0], 0, 7),
1153 mee_range (" Y axis", 0, in_adev_axis[0][1], 0, 7),
1154 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[0], 1, h_nubmode),
1155 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
1156 mee_range (" X axis", 0, in_adev_axis[1][0], 0, 7),
1157 mee_range (" Y axis", 0, in_adev_axis[1][1], 0, 7),
1158 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[1], 1, h_nubmode),
1162 static int key_config_analog(int id, int keys)
1164 int i, d, count, sel = 0;
1165 int sel2dev_map[IN_MAX_DEVS];
1167 memset(adevnames, 0, sizeof(adevnames));
1168 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1169 memset(stick_sel, 0, sizeof(stick_sel));
1171 adevnames[0] = "None";
1173 for (d = 0; d < IN_MAX_DEVS; d++)
1175 const char *name = in_get_dev_name(d, 0, 1);
1180 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1184 if (in_adev[0] == d) stick_sel[0] = i;
1185 if (in_adev[1] == d) stick_sel[1] = i;
1187 adevnames[i++] = name;
1189 adevnames[i] = NULL;
1191 me_loop(e_menu_keyconfig_analog, &sel);
1193 in_adev[0] = sel2dev_map[stick_sel[0]];
1194 in_adev[1] = sel2dev_map[stick_sel[1]];
1199 static const char *mgn_dev_name(int id, int *offs)
1201 const char *name = NULL;
1204 if (id == MA_CTRL_DEV_FIRST)
1207 for (; it < IN_MAX_DEVS; it++) {
1208 name = in_get_dev_name(it, 1, 1);
1217 static const char *mgn_saveloadcfg(int id, int *offs)
1222 static int mh_savecfg(int id, int keys)
1224 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1225 menu_update_msg("config saved");
1227 menu_update_msg("failed to write config");
1232 static int mh_input_rescan(int id, int keys)
1234 //menu_sync_config();
1236 menu_update_msg("rescan complete.");
1241 static const char *men_in_type_sel[] = {
1242 "Standard (SCPH-1080)",
1243 "Analog (SCPH-1150)",
1249 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1250 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1251 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1253 static menu_entry e_menu_keyconfig[] =
1255 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1256 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1257 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
1258 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
1260 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1261 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
1262 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
1263 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
1264 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
1265 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1266 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1267 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1268 mee_handler ("Rescan devices:", mh_input_rescan),
1270 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
1271 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1272 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1273 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1274 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1275 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1276 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1280 static int menu_loop_keyconfig(int id, int keys)
1284 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1285 me_loop(e_menu_keyconfig, &sel);
1289 // ------------ gfx options menu ------------
1291 static const char *men_scaler[] = {
1292 "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen",
1293 #if MENU_SHOW_VARSCALER_C
1298 static const char *men_soft_filter[] = { "None",
1300 "scale2x", "eagle2x",
1303 static const char *men_dummy[] = { NULL };
1304 static const char *men_centering[] = { "Auto", "Ingame", "Borderless", "Force", NULL };
1305 static const char *men_overscan[] = { "OFF", "Auto", "Hack", NULL };
1306 static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
1307 "int. 4:3 - uses integer if possible, else fractional";
1308 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1309 "using d-pad or move it using R+d-pad";
1310 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1311 static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
1313 static const char *men_scanlines[] = { "OFF", "1", "2", "3", NULL };
1314 static const char h_scanline_l[] = "Scanline brightness, 0-100%";
1317 static int menu_loop_cscaler(int id, int keys)
1319 void *saved_layer = NULL;
1320 size_t saved_layer_size = 0;
1321 int was_layer_clipped = 0;
1327 g_scaler = SCALE_CUSTOM;
1328 saved_layer_size = last_vout_w * last_vout_h * last_vout_bpp / 8;
1329 saved_layer = malloc(saved_layer_size);
1331 memcpy(saved_layer, pl_vout_buf, saved_layer_size);
1333 plat_gvideo_open(Config.PsxType);
1335 menu_draw_begin(0, 1);
1336 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1341 if (saved_layer && last_vout_bpp == 16) {
1342 int top_x = max(0, -g_layer_x * last_vout_w / 800) + 1;
1343 int top_y = max(0, -g_layer_y * last_vout_h / 480) + 1;
1345 memcpy(pl_vout_buf, saved_layer, saved_layer_size);
1346 snprintf(text, sizeof(text), "%d,%d %dx%d",
1347 g_layer_x, g_layer_y, g_layer_w, g_layer_h);
1348 basic_text_out16_nf(pl_vout_buf, last_vout_w,
1349 top_x, top_y, text);
1350 basic_text_out16_nf(pl_vout_buf, last_vout_w, 2,
1351 last_vout_h - 20, "d-pad: resize, R+d-pad: move");
1352 pl_vout_buf = plat_gvideo_flip();
1355 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1356 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1357 if (inp & PBTN_UP) g_layer_y--;
1358 if (inp & PBTN_DOWN) g_layer_y++;
1359 if (inp & PBTN_LEFT) g_layer_x--;
1360 if (inp & PBTN_RIGHT) g_layer_x++;
1361 if (!(inp & PBTN_R)) {
1362 if (inp & PBTN_UP) g_layer_h += 2;
1363 if (inp & PBTN_DOWN) g_layer_h -= 2;
1364 if (inp & PBTN_LEFT) g_layer_w += 2;
1365 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1367 if (inp & (PBTN_MOK|PBTN_MBACK))
1370 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1371 int layer_clipped = 0;
1372 g_layer_x = max(-320, min(g_layer_x, 640));
1373 g_layer_y = max(-240, min(g_layer_y, 400));
1374 g_layer_w = max(160, g_layer_w);
1375 g_layer_h = max( 60, g_layer_h);
1376 if (g_layer_x < 0 || g_layer_x + g_layer_w > 800)
1378 if (g_layer_w > 800+400)
1379 g_layer_w = 800+400;
1380 if (g_layer_y < 0 || g_layer_y + g_layer_h > 480)
1382 if (g_layer_h > 480+360)
1383 g_layer_h = 480+360;
1385 plat_gvideo_open(Config.PsxType);
1386 if (layer_clipped || was_layer_clipped)
1387 pl_vout_buf = plat_gvideo_set_mode(&last_vout_w,
1388 &last_vout_h, &last_vout_bpp);
1389 was_layer_clipped = layer_clipped;
1393 plat_gvideo_close();
1399 static menu_entry e_menu_gfx_options[] =
1401 mee_enum ("PSX Screen centering", MA_OPT_CENTERING, pl_rearmed_cbs.screen_centering_type, men_centering),
1402 mee_enum ("Show overscan", MA_OPT_OVERSCAN, pl_rearmed_cbs.show_overscan, men_overscan),
1403 mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1404 mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1405 mee_onoff ("Fullscreen mode", MA_OPT_VOUT_FULL, plat_target.vout_fullscreen, 1),
1406 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1407 mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1408 mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1410 mee_enum ("Scanlines", MA_OPT_SCANLINES, scanlines, men_scanlines),
1411 mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1413 mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1414 mee_onoff ("OpenGL Vsync", MA_OPT_VSYNC, g_opts, OPT_VSYNC),
1415 mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1419 static int menu_loop_gfx_options(int id, int keys)
1423 me_loop(e_menu_gfx_options, &sel);
1428 // ------------ bios/plugins ------------
1430 static const char h_gpu_neon_enhanced[] =
1431 "Renders in double resolution at perf. cost\n"
1432 "(not available for high resolution games)";
1433 static const char h_gpu_neon_enhanced_hack[] =
1434 "Speed hack for above option (glitches some games)";
1435 static const char h_gpu_neon_enhanced_texadj[] =
1436 "Solves some Enh. res. texture issues, some perf hit";
1437 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1439 static menu_entry e_menu_plugin_gpu_neon[] =
1441 mee_onoff_h ("Enhanced resolution", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1442 mee_onoff_h ("Enhanced res. speed hack", 0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1443 mee_onoff_h ("Enh. res. texture adjust", 0, pl_rearmed_cbs.gpu_neon.enhancement_tex_adj, 1, h_gpu_neon_enhanced_texadj),
1444 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1448 static menu_entry e_menu_plugin_gpu_unai[] =
1450 mee_onoff ("Old renderer", 0, pl_rearmed_cbs.gpu_unai.old_renderer, 1),
1451 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai.ilace_force, 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),
1458 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1459 static const char h_gpu_1[] = "Capcom fighting games";
1460 static const char h_gpu_2[] = "Black screens in Lunar";
1461 static const char h_gpu_3[] = "Compatibility mode";
1462 static const char h_gpu_6[] = "Pandemonium 2";
1463 //static const char h_gpu_7[] = "Skip every second frame";
1464 static const char h_gpu_8[] = "Needed by Dark Forces";
1465 static const char h_gpu_9[] = "better g-colors, worse textures";
1466 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1468 static menu_entry e_menu_plugin_gpu_peops[] =
1470 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1471 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1472 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1473 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1474 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1475 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1476 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1477 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1478 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1482 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1483 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1484 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1486 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1488 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1489 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1490 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1491 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1492 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1493 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1494 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1495 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1496 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1497 mee_label ("Fixes/hacks:"),
1498 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1499 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1500 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1501 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1502 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1503 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1504 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1505 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1506 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1507 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1508 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1512 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1513 static const char h_spu_volboost[] = "Large values cause distortion";
1514 static const char h_spu_tempo[] = "Slows down audio if emu is too slow\n"
1515 "This is inaccurate and breaks games";
1517 static menu_entry e_menu_plugin_spu[] =
1519 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1520 mee_onoff ("Reverb", 0, spu_config.iUseReverb, 1),
1521 mee_enum ("Interpolation", 0, spu_config.iUseInterpolation, men_spu_interp),
1522 //mee_onoff ("Adjust XA pitch", 0, spu_config.iXAPitch, 1),
1523 mee_onoff_h ("Adjust tempo", 0, spu_config.iTempo, 1, h_spu_tempo),
1527 static int menu_loop_plugin_spu(int id, int keys)
1530 me_loop(e_menu_plugin_spu, &sel);
1534 static const char *men_gpu_dithering[] = { "OFF", "ON", "Force", NULL };
1536 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1537 "savestates and can't be changed there. Must save\n"
1538 "config and reload the game for change to take effect";
1539 static const char h_plugin_gpu[] =
1540 #if defined(BUILTIN_GPU_NEON)
1541 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1542 #elif defined(BUILTIN_GPU_PEOPS)
1543 "builtin_gpu is the P.E.Op.S GPU, slow but accurate\n"
1544 #elif defined(BUILTIN_GPU_UNAI)
1545 "builtin_gpu is the Unai GPU, very fast\n"
1548 #if !defined(BUILTIN_GPU_NEON) && defined(GPU_NEON)
1549 "gpu_neon is Exophase's NEON GPU, fast and accurate\n"
1551 #ifndef BUILTIN_GPU_PEOPS
1552 "gpu_peops is Pete's soft GPU, slow but accurate\n"
1554 #ifndef BUILTIN_GPU_UNAI
1555 "gpu_unai is the GPU renderer from PCSX4ALL\n"
1558 "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1560 "must save config and reload the game if changed"
1563 static const char h_plugin_spu[] = ""
1565 "spunull effectively disables sound\n"
1566 "must save config and reload the game if changed"
1569 // static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1570 // static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1571 // static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team plugin (new)";
1572 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1574 static int menu_loop_pluginsel_options(int id, int keys)
1577 if (strcmp(gpu_plugins[gpu_plugsel], "gpu_peops.so") == 0)
1578 me_loop(e_menu_plugin_gpu_peops, &sel);
1579 else if (strcmp(gpu_plugins[gpu_plugsel], "gpu_unai.so") == 0)
1580 me_loop(e_menu_plugin_gpu_unai, &sel);
1581 else if (strcmp(gpu_plugins[gpu_plugsel], "gpu_gles.so") == 0)
1582 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1583 else if (strcmp(gpu_plugins[gpu_plugsel], "gpu_neon.so") == 0)
1584 me_loop(e_menu_plugin_gpu_neon, &sel);
1586 #if defined(BUILTIN_GPU_NEON)
1587 me_loop(e_menu_plugin_gpu_neon, &sel);
1588 #elif defined(BUILTIN_GPU_PEOPS)
1589 me_loop(e_menu_plugin_gpu_peops, &sel);
1590 #elif defined(BUILTIN_GPU_UNAI)
1591 me_loop(e_menu_plugin_gpu_unai, &sel);
1596 static menu_entry e_menu_plugin_options[] =
1598 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1599 mee_enum ("GPU Dithering", 0, pl_rearmed_cbs.dithering, men_gpu_dithering),
1600 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1601 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1602 mee_handler ("Configure selected GPU plugin", menu_loop_pluginsel_options),
1603 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1607 static menu_entry e_menu_main2[];
1609 static int menu_loop_plugin_options(int id, int keys)
1612 me_loop(e_menu_plugin_options, &sel);
1614 // sync BIOS/plugins
1615 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1616 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1617 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1618 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1623 // ------------ adv options menu ------------
1626 static const char h_cfg_noch[] = "Disables game-specific compatibility hacks";
1627 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1628 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1629 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1631 static const char h_cfg_stalls[] = "Will cause some games to run too fast";
1633 static menu_entry e_menu_speed_hacks[] =
1636 mee_onoff_h ("Disable compat hacks", 0, ndrc_g.hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1637 mee_onoff_h ("Disable SMC checks", 0, ndrc_g.hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1638 mee_onoff_h ("Assume GTE regs unneeded", 0, ndrc_g.hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1639 mee_onoff_h ("Disable GTE flags", 0, ndrc_g.hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1641 mee_onoff_h ("Disable CPU/GTE stalls", 0, menu_iopts[0], 1, h_cfg_stalls),
1645 static int menu_loop_speed_hacks(int id, int keys)
1648 menu_iopts[0] = Config.DisableStalls;
1649 me_loop(e_menu_speed_hacks, &sel);
1650 Config.DisableStalls = menu_iopts[0];
1654 static const char *men_autooo[] = { "Auto", "Off", "On", NULL };
1656 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1657 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1658 "(green: normal, red: fmod, blue: noise)";
1659 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1660 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1661 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1662 "(proper .cue/.bin dump is needed otherwise)";
1664 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1665 "Might be useful to overcome some dynarec bugs";
1667 static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1668 static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1669 static const char h_cfg_exc[] = "Emulate some PSX's debug hw like breakpoints\n"
1670 "and exceptions (slow, interpreter only, keep off)";
1671 static const char h_cfg_gpul[] = "Try enabling this if the game misses some graphics\n"
1672 "causes a performance hit";
1673 static const char h_cfg_ffps[] = "Instead of 50/60fps for PAL/NTSC use ~49.75/59.81\n"
1674 "Closer to real hw but doesn't match modern displays.";
1675 static const char h_cfg_tcd[] = "Greatly reduce CD load times. Breaks some games.";
1676 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1677 "(adjust this if the game is too slow/too fast/hangs)";
1679 enum { AMO_XA, AMO_CDDA, AMO_IC, AMO_BP, AMO_CPU, AMO_GPUL, AMO_FFPS, AMO_TCD };
1681 static menu_entry e_menu_adv_options[] =
1683 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1684 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1685 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1686 mee_onoff_h ("Disable XA Decoding", 0, menu_iopts[AMO_XA], 1, h_cfg_xa),
1687 mee_onoff_h ("Disable CD Audio", 0, menu_iopts[AMO_CDDA], 1, h_cfg_cdda),
1688 mee_onoff_h ("ICache emulation", 0, menu_iopts[AMO_IC], 1, h_cfg_icache),
1689 mee_onoff_h ("BP exception emulation", 0, menu_iopts[AMO_BP], 1, h_cfg_exc),
1690 mee_enum_h ("GPU l-list slow walking",0, menu_iopts[AMO_GPUL], men_autooo, h_cfg_gpul),
1691 mee_enum_h ("Fractional framerate", 0, menu_iopts[AMO_FFPS], men_autooo, h_cfg_ffps),
1692 mee_onoff_h ("Turbo CD-ROM ", 0, menu_iopts[AMO_TCD], 1, h_cfg_tcd),
1693 #ifdef USE_ASYNC_CDROM
1694 mee_range ("CD-ROM read-ahead", 0, cd_buf_count, 0, 1024),
1696 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
1697 mee_onoff_h ("Disable dynarec (slow!)",0, menu_iopts[AMO_CPU], 1, h_cfg_nodrc),
1699 mee_range_h ("PSX CPU clock, %", 0, psx_clock, 1, 500, h_cfg_psxclk),
1700 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1704 static int menu_loop_adv_options(int id, int keys)
1711 { &Config.Xa, &menu_iopts[AMO_XA] },
1712 { &Config.Cdda, &menu_iopts[AMO_CDDA] },
1713 { &Config.icache_emulation, &menu_iopts[AMO_IC] },
1714 { &Config.PreciseExceptions, &menu_iopts[AMO_BP] },
1715 { &Config.Cpu, &menu_iopts[AMO_CPU] },
1716 { &Config.TurboCD, &menu_iopts[AMO_TCD] },
1719 for (i = 0; i < ARRAY_SIZE(opts); i++)
1720 *opts[i].mopt = *opts[i].opt;
1721 menu_iopts[AMO_GPUL] = Config.GpuListWalking + 1;
1722 menu_iopts[AMO_FFPS] = Config.FractionalFramerate + 1;
1724 me_loop(e_menu_adv_options, &sel);
1726 for (i = 0; i < ARRAY_SIZE(opts); i++)
1727 *opts[i].opt = *opts[i].mopt;
1728 Config.GpuListWalking = menu_iopts[AMO_GPUL] - 1;
1729 Config.FractionalFramerate = menu_iopts[AMO_FFPS] - 1;
1730 cdra_set_buf_count(cd_buf_count);
1735 // ------------ options menu ------------
1737 static int mh_restore_defaults(int id, int keys)
1739 menu_set_defconfig();
1740 menu_update_msg("defaults restored");
1744 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1745 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1747 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1748 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1749 "loading state or both";
1751 static const char h_restore_def[] = "Switches back to default / recommended\n"
1753 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1754 static const char h_sputhr[] = "Warning: has some known bugs\n";
1756 static menu_entry e_menu_options[] =
1758 // mee_range ("Save slot", 0, state_slot, 0, 9),
1759 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1760 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1761 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1762 mee_enum ("Region", 0, region, men_region),
1763 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1765 mee_onoff_h ("Use C64x DSP for sound", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1767 mee_onoff_h ("Threaded SPU", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1769 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1770 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1771 mee_handler ("[Advanced]", menu_loop_adv_options),
1772 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1773 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1774 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1778 static int menu_loop_options(int id, int keys)
1782 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1783 me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1784 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1786 me_loop(e_menu_options, &sel);
1791 // ------------ debug menu ------------
1793 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1795 int w = min(g_menuscreen_w, 1024);
1796 int h = min(g_menuscreen_h, 512);
1797 u16 *d = g_menuscreen_ptr;
1798 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1802 gpuf->ulFreezeVersion = 1;
1803 if (GPU_freeze != NULL)
1804 GPU_freeze(1, gpuf);
1806 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1807 bgr555_to_rgb565(d, s, w * 2);
1809 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1810 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1811 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1812 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1813 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1816 static void debug_menu_loop(void)
1818 int inp, df_x = 0, df_y = 0;
1821 gpuf = malloc(sizeof(*gpuf));
1827 menu_draw_begin(0, 1);
1828 draw_frame_debug(gpuf, df_x, df_y);
1831 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1832 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1833 if (inp & PBTN_MBACK) break;
1834 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1835 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1836 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x -= 2; }
1837 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1843 // --------- memcard manager ---------
1845 static void draw_mc_icon(int dx, int dy, const u16 *s)
1850 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1852 for (y = 0; y < 16; y++, s += 16) {
1853 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1854 for (x = 0; x < 16; x++) {
1856 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1857 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1863 static void draw_mc_bg(void)
1865 McdBlock *blocks1, *blocks2;
1869 blocks1 = malloc(15 * sizeof(blocks1[0]));
1870 blocks2 = malloc(15 * sizeof(blocks1[0]));
1871 if (blocks1 == NULL || blocks2 == NULL)
1874 for (i = 0; i < 15; i++) {
1875 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1876 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1879 menu_draw_begin(1, 1);
1881 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1883 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1887 maxicons = g_menuscreen_h / 32;
1890 row2 = g_menuscreen_w / 2;
1891 for (i = 0; i < maxicons; i++) {
1892 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1893 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1895 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1896 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1899 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1907 static void handle_memcard_sel(void)
1909 strcpy(Config.Mcd1, "none");
1910 if (memcard1_sel != 0)
1911 emu_make_path(Config.Mcd1, sizeof(Config.Mcd1), MEMCARD_DIR, memcards[memcard1_sel]);
1912 strcpy(Config.Mcd2, "none");
1913 if (memcard2_sel != 0)
1914 emu_make_path(Config.Mcd2, sizeof(Config.Mcd2), MEMCARD_DIR, memcards[memcard2_sel]);
1915 LoadMcds(Config.Mcd1, Config.Mcd2);
1919 static menu_entry e_memcard_options[] =
1921 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1922 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1926 static int menu_loop_memcards(int id, int keys)
1932 memcard1_sel = memcard2_sel = 0;
1933 p = strrchr(Config.Mcd1, '/');
1935 for (i = 0; memcards[i] != NULL; i++)
1936 if (strcmp(p + 1, memcards[i]) == 0)
1937 { memcard1_sel = i; break; }
1938 p = strrchr(Config.Mcd2, '/');
1940 for (i = 0; memcards[i] != NULL; i++)
1941 if (strcmp(p + 1, memcards[i]) == 0)
1942 { memcard2_sel = i; break; }
1944 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1946 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1951 // ------------ cheats menu ------------
1953 static void draw_cheatlist(int sel)
1955 int max_cnt, start, i, pos, active;
1957 max_cnt = g_menuscreen_h / me_sfont_h;
1958 start = max_cnt / 2 - sel;
1960 menu_draw_begin(1, 1);
1962 for (i = 0; i < NumCheats; i++) {
1964 if (pos < 0) continue;
1965 if (pos >= max_cnt) break;
1966 active = Cheats[i].Enabled;
1967 smalltext_out16(14, pos * me_sfont_h,
1968 active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1969 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1970 Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1974 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1976 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1980 static void menu_loop_cheats(void)
1982 static int menu_sel = 0;
1987 draw_cheatlist(menu_sel);
1988 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1989 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1990 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1991 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1992 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1993 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1994 if (inp & PBTN_MOK) { // action
1995 if (menu_sel < NumCheats)
1996 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1999 if (inp & PBTN_MBACK)
2004 // --------- main menu help ----------
2006 static void menu_bios_warn(void)
2009 static const char msg[] =
2010 "You don't seem to have copied any BIOS\n"
2013 "While many games work fine with fake\n"
2014 "(HLE) BIOS, others (like MGS and FF8)\n"
2015 "require BIOS to work.\n"
2016 "After copying the file, you'll also need\n"
2017 "to select it in the emu's menu:\n"
2018 "options->[BIOS/Plugins]\n\n"
2019 "The file is usually named SCPH1001.BIN,\n"
2020 "but other not compressed files can be\n"
2022 "Press %s or %s to continue";
2023 char tmp_msg[sizeof(msg) + 64];
2025 snprintf(tmp_msg, sizeof(tmp_msg), msg, Config.BiosDir,
2026 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
2029 draw_menu_message(tmp_msg, NULL);
2031 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2032 if (inp & (PBTN_MBACK|PBTN_MOK))
2037 // ------------ main menu ------------
2039 static menu_entry e_menu_main[];
2041 static void draw_frame_main(void)
2050 if (CdromId[0] != 0) {
2051 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
2052 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
2053 Config.HLE ? "HLE" : "BIOS");
2054 smalltext_out16(4, 1, buff, 0x105f);
2058 capacity = plat_target_bat_capacity_get();
2060 tmp = localtime(<ime);
2061 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
2062 if (capacity >= 0) {
2063 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
2068 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
2072 static void draw_frame_credits(void)
2074 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
2077 static const char credits_text[] =
2079 "(C) 1999-2003 PCSX Team\n"
2080 "(C) 2005-2009 PCSX-df Team\n"
2081 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
2082 "ARM recompiler (C) 2009-2011 Ari64\n"
2083 "ARM NEON GPU (c) 2011-2012 Exophase\n"
2084 "PEOpS GPU and SPU by Pete Bernert\n"
2085 " and the P.E.Op.S. team\n"
2086 "PCSX4ALL plugin by PCSX4ALL team\n"
2087 " Chui, Franxis, Unai\n\n"
2088 "integration, optimization and\n"
2089 " frontend (C) 2010-2015 notaz\n";
2091 static int reset_game(void)
2097 if (LoadCdrom() == -1)
2103 static int reload_plugins(const char *cdimg)
2109 set_cd_image(cdimg);
2111 pcnt_hook_plugins();
2112 if (OpenPlugins() == -1) {
2113 menu_update_msg("failed to open plugins");
2116 plugin_call_rearmed_cbs();
2118 cdrIsoMultidiskCount = 1;
2120 CdromLabel[0] = '\0';
2125 static int run_bios(void)
2127 boolean origSlowBoot = Config.SlowBoot;
2133 if (reload_plugins(NULL) != 0)
2135 Config.SlowBoot = 1;
2137 Config.SlowBoot = origSlowBoot;
2143 static int run_exe(void)
2145 const char *exts[] = { "exe", NULL };
2148 fname = menu_loop_romsel(last_selected_fname,
2149 sizeof(last_selected_fname), exts, NULL);
2154 if (reload_plugins(NULL) != 0)
2158 if (Load(fname) != 0) {
2159 menu_update_msg("exe load failed, bad file?");
2168 static int run_cd_image(const char *fname)
2170 int autoload_state = g_autostateld_opt;
2171 size_t fname_len = strlen(fname);
2172 const char *ppfname = NULL;
2175 // simle ppf handling, like game.chd.ppf
2176 if (4 < fname_len && fname_len < sizeof(fname2)
2177 && strcasecmp(fname + fname_len - 4, ".ppf") == 0) {
2178 memcpy(fname2, fname, fname_len - 4);
2179 fname2[fname_len - 4] = 0;
2185 reload_plugins(fname);
2187 // always autodetect, menu_sync_config will override as needed
2190 if (CheckCdrom() == -1) {
2191 // Only check the CD if we are starting the console with a CD
2193 menu_update_msg("unsupported/invalid CD image");
2197 BuildPPFCache(ppfname);
2201 // Read main executable directly from CDRom and start it
2202 if (LoadCdrom() == -1) {
2204 menu_update_msg("failed to load CD image");
2211 if (autoload_state) {
2212 unsigned int newest = 0;
2213 int time = 0, slot, newest_slot = -1;
2215 for (slot = 0; slot < 10; slot++) {
2216 if (emu_check_save_file(slot, &time)) {
2217 if ((unsigned int)time > newest) {
2224 if (newest_slot >= 0) {
2225 lprintf("autoload slot %d\n", newest_slot);
2226 emu_load_state(newest_slot);
2229 lprintf("no save to autoload.\n");
2236 static int romsel_run(void)
2238 int prev_gpu, prev_spu;
2241 fname = menu_loop_romsel(last_selected_fname,
2242 sizeof(last_selected_fname), filter_exts,
2243 optional_cdimg_filter);
2247 printf("selected file: %s\n", fname);
2251 if (run_cd_image(fname) != 0)
2254 prev_gpu = gpu_plugsel;
2255 prev_spu = spu_plugsel;
2256 if (menu_load_config(1) != 0)
2257 menu_load_config(0);
2259 // check for plugin changes, have to repeat
2260 // loading if game config changed plugins to reload them
2261 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2262 printf("plugin change detected, reloading plugins..\n");
2263 if (run_cd_image(fname) != 0)
2267 strcpy(last_selected_fname, fname);
2268 menu_do_last_cd_img(0);
2272 static int swap_cd_image(void)
2276 fname = menu_loop_romsel(last_selected_fname,
2277 sizeof(last_selected_fname), filter_exts,
2278 optional_cdimg_filter);
2282 printf("selected file: %s\n", fname);
2285 CdromLabel[0] = '\0';
2287 set_cd_image(fname);
2288 if (ReloadCdromPlugin() < 0) {
2289 menu_update_msg("failed to load cdr plugin");
2292 if (cdra_open() < 0) {
2293 menu_update_msg("failed to open cdr plugin");
2297 SetCdOpenCaseTime(time(NULL) + 2);
2300 strcpy(last_selected_fname, fname);
2304 static int swap_cd_multidisk(void)
2306 cdrIsoMultidiskSelect++;
2308 CdromLabel[0] = '\0';
2311 if (cdra_open() < 0) {
2312 menu_update_msg("failed to open cdr plugin");
2316 SetCdOpenCaseTime(time(NULL) + 2);
2322 static void load_pcsx_cht(void)
2324 static const char *exts[] = { "cht", NULL };
2328 fname = menu_loop_romsel(last_selected_fname,
2329 sizeof(last_selected_fname), exts, NULL);
2333 printf("selected cheat file: %s\n", fname);
2336 if (NumCheats == 0 && NumCodes == 0)
2337 menu_update_msg("failed to load cheats");
2339 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2340 menu_update_msg(msg);
2342 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2345 static int main_menu_handler(int id, int keys)
2349 case MA_MAIN_RESUME_GAME:
2353 case MA_MAIN_SAVE_STATE:
2355 return menu_loop_savestate(0);
2357 case MA_MAIN_LOAD_STATE:
2359 return menu_loop_savestate(1);
2361 case MA_MAIN_RESET_GAME:
2362 if (ready_to_go && reset_game() == 0)
2365 case MA_MAIN_LOAD_ROM:
2366 if (romsel_run() == 0)
2369 case MA_MAIN_SWAP_CD:
2370 if (swap_cd_image() == 0)
2373 case MA_MAIN_SWAP_CD_MULTI:
2374 if (swap_cd_multidisk() == 0)
2377 case MA_MAIN_RUN_BIOS:
2378 if (run_bios() == 0)
2381 case MA_MAIN_RUN_EXE:
2385 case MA_MAIN_CHEATS:
2388 case MA_MAIN_LOAD_CHEATS:
2391 case MA_MAIN_CREDITS:
2392 draw_menu_message(credits_text, draw_frame_credits);
2393 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2396 emu_core_ask_exit();
2399 lprintf("%s: something unknown selected\n", __FUNCTION__);
2406 static menu_entry e_menu_main2[] =
2408 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2409 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2410 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2411 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2412 mee_handler ("Memcard manager", menu_loop_memcards),
2413 mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS, main_menu_handler),
2417 static int main_menu2_handler(int id, int keys)
2421 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2422 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2423 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2424 me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2426 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2429 static const char h_extra[] = "Change CD, manage memcards..\n";
2431 static menu_entry e_menu_main[] =
2435 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2436 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2437 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2438 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2439 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2440 mee_handler ("Options", menu_loop_options),
2441 mee_handler ("Controls", menu_loop_keyconfig),
2442 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
2443 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2444 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2445 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2449 // ----------------------------
2451 static void menu_leave_emu(void);
2453 void menu_loop(void)
2455 static int warned_about_bios = 0;
2460 if (config_save_counter == 0) {
2462 if (bioses[1] != NULL) {
2463 // autoselect BIOS to make user's life easier
2464 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2467 else if (!warned_about_bios) {
2469 warned_about_bios = 1;
2473 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2474 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2475 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2476 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2477 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2479 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2482 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2483 } while (!ready_to_go && !g_emu_want_quit);
2485 /* wait until menu, ok, back is released */
2486 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2489 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2494 static int qsort_strcmp(const void *p1, const void *p2)
2496 char * const *s1 = (char * const *)p1;
2497 char * const *s2 = (char * const *)p2;
2498 return strcasecmp(*s1, *s2);
2501 static void scan_bios_plugins(void)
2503 char fname[MAXPATHLEN];
2505 int bios_i, gpu_i, spu_i, mc_i;
2509 gpu_plugins[0] = "builtin_gpu";
2510 spu_plugins[0] = "builtin_spu";
2511 memcards[0] = "(none)";
2512 bios_i = gpu_i = spu_i = mc_i = 1;
2514 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2515 dir = opendir(fname);
2517 perror("scan_bios_plugins bios opendir");
2536 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2539 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2540 if (stat(fname, &st) != 0
2541 || (st.st_size != 512*1024 && st.st_size != 4*1024*1024)) {
2542 printf("bad BIOS file: %s\n", ent->d_name);
2546 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2547 bioses[bios_i++] = strdup(ent->d_name);
2551 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2558 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2559 dir = opendir(fname);
2561 perror("scan_bios_plugins plugins opendir");
2576 p = strstr(ent->d_name, ".so");
2580 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2581 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2583 fprintf(stderr, "%s\n", dlerror());
2587 // now what do we have here?
2588 tmp = dlsym(h, "GPUinit");
2591 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2592 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2596 tmp = dlsym(h, "SPUinit");
2599 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2600 spu_plugins[spu_i++] = strdup(ent->d_name);
2604 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2612 emu_make_path(fname, sizeof(fname), MEMCARD_DIR, NULL);
2613 dir = opendir(fname);
2615 perror("scan_bios_plugins memcards opendir");
2630 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2633 emu_make_path(fname, sizeof(fname), MEMCARD_DIR, ent->d_name);
2634 if (stat(fname, &st) != 0) {
2635 printf("bad memcard file: %s\n", ent->d_name);
2639 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2640 memcards[mc_i++] = strdup(ent->d_name);
2644 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2648 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2653 void menu_init(void)
2655 char buff[MAXPATHLEN];
2658 cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2660 scan_bios_plugins();
2663 menu_set_defconfig();
2664 menu_load_config(0);
2665 menu_do_last_cd_img(1);
2670 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2671 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2672 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2673 fprintf(stderr, "OOM\n");
2677 emu_make_data_path(buff, "skin/background.png", sizeof(buff));
2678 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2680 i = plat_target.cpu_clock_set != NULL
2681 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2682 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, i);
2684 i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2685 e_menu_gfx_options[i].data = plat_target.vout_methods;
2686 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2687 plat_target.vout_methods != NULL);
2689 #ifndef SDL_OVERLAY_2X
2690 i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_FULL);
2691 e_menu_gfx_options[i].data = plat_target.vout_methods;
2692 me_enable(e_menu_gfx_options, MA_OPT_VOUT_FULL, 0);
2695 i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2696 e_menu_gfx_options[i].data = plat_target.hwfilters;
2697 me_enable(e_menu_gfx_options, MA_OPT_HWFILTER, plat_target.hwfilters != NULL);
2698 if (plat_target.hwfilters && !strcmp(plat_target.hwfilters[0], "linear"))
2699 e_menu_gfx_options[i].name = "OpenGL filter";
2701 me_enable(e_menu_gfx_options, MA_OPT_VSYNC, 0);
2703 me_enable(e_menu_gfx_options, MA_OPT_GAMMA, plat_target.gamma_set != NULL);
2705 me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2707 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2708 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2709 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER_C);
2710 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2711 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2712 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2713 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2716 void menu_notify_mode_change(int w, int h, int bpp)
2720 last_vout_bpp = bpp;
2723 static void menu_leave_emu(void)
2725 if (GPU_close != NULL) {
2726 int ret = GPU_close();
2728 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2731 plat_video_menu_enter(ready_to_go);
2733 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2734 if (pl_vout_buf != NULL && ready_to_go) {
2735 int x = max(0, g_menuscreen_w - last_vout_w);
2736 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2737 int w = min(g_menuscreen_w, last_vout_w);
2738 int h = min(g_menuscreen_h, last_vout_h);
2739 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2740 char *s = pl_vout_buf;
2742 if (last_vout_bpp == 16) {
2743 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2744 menu_darken_bg(d, s, w, 0);
2747 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2748 rgb888_to_rgb565(d, s, w * 3);
2749 menu_darken_bg(d, d, w, 0);
2755 cpu_clock = plat_target_cpu_clock_get();
2758 void menu_prepare_emu(void)
2760 R3000Acpu *prev_cpu = psxCpu;
2762 plat_video_menu_leave();
2764 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
2765 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2769 if (psxCpu != prev_cpu) {
2770 prev_cpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
2771 prev_cpu->Shutdown();
2773 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
2777 psxCpu->ApplyConfig();
2780 plat_target_cpu_clock_set(cpu_clock);
2782 // push config to GPU plugin
2783 plugin_call_rearmed_cbs();
2785 if (GPU_open != NULL) {
2786 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2788 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2792 void menu_update_msg(const char *msg)
2794 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2795 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2797 menu_error_time = plat_get_ticks_ms();
2798 lprintf("msg: %s\n", menu_error_msg);
2801 void menu_finish(void)
2803 if (cpu_clock_st > 0)
2804 plat_target_cpu_clock_set(cpu_clock_st);