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,
98 static int last_vout_w, last_vout_h, last_vout_bpp;
99 static int cpu_clock, cpu_clock_st, volume_boost;
100 static int frameskip = 1; // 0 - auto, 1 - off
101 static char last_selected_fname[MAXPATHLEN];
102 static int config_save_counter, region, in_type_sel1, in_type_sel2;
103 static int psx_clock;
104 static int memcard1_sel = -1, memcard2_sel = -1;
105 extern int g_autostateld_opt;
106 static int menu_iopts[8];
107 int g_opts, g_scaler, g_gamma = 100;
108 int scanlines, scanline_level = 20;
109 int soft_scaling, analog_deadzone; // for Caanoo
112 #ifndef HAVE_PRE_ARMV7
113 #define DEFAULT_PSX_CLOCK (10000 / CYCLE_MULT_DEFAULT)
114 #define DEFAULT_PSX_CLOCK_S "57"
116 #define DEFAULT_PSX_CLOCK 50
117 #define DEFAULT_PSX_CLOCK_S "50"
120 static const char *bioses[32];
121 static const char *gpu_plugins[16];
122 static const char *spu_plugins[16];
123 static const char *memcards[32];
124 static int bios_sel, gpu_plugsel, spu_plugsel;
126 #ifndef UI_FEATURES_H
127 #define MENU_BIOS_PATH "bios/"
128 #define MENU_SHOW_VARSCALER 0
129 #define MENU_SHOW_VOUTMODE 1
130 #define MENU_SHOW_SCALER2 0
131 #define MENU_SHOW_NUBS_BTNS 0
132 #define MENU_SHOW_VIBRATION 0
133 #define MENU_SHOW_DEADZONE 0
134 #define MENU_SHOW_MINIMIZE 0
135 #define MENU_SHOW_FULLSCREEN 1
136 #define MENU_SHOW_VOLUME 0
139 static int min(int x, int y) { return x < y ? x : y; }
140 static int max(int x, int y) { return x > y ? x : y; }
142 void emu_make_path(char *buff, const char *end, int size)
146 end_len = strlen(end);
147 pos = plat_get_root_dir(buff, size);
148 strncpy(buff + pos, end, size - pos);
150 if (pos + end_len > size - 1)
151 printf("Warning: path truncated: %s\n", buff);
154 static int emu_check_save_file(int slot, int *time)
156 char fname[MAXPATHLEN];
160 ret = emu_check_state(slot);
161 if (ret != 0 || time == NULL)
162 return ret == 0 ? 1 : 0;
164 ret = get_state_filename(fname, sizeof(fname), slot);
168 ret = stat(fname, &status);
172 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
173 return 1; // probably bad rtc like on some Caanoos
175 *time = status.st_mtime;
180 static int emu_save_load_game(int load, int unused)
185 ret = emu_load_state(state_slot);
187 // reflect hle/bios mode from savestate
190 else if (bios_sel == 0 && bioses[1] != NULL)
191 // XXX: maybe find the right bios instead
195 ret = emu_save_state(state_slot);
200 static void rm_namelist_entry(struct dirent **namelist,
201 int count, const char *name)
205 for (i = 1; i < count; i++) {
206 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
209 if (strcmp(name, namelist[i]->d_name) == 0) {
217 static int optional_cdimg_filter(struct dirent **namelist, int count,
221 char buf[256], buf2[257];
222 int i, d, ret, good_cue;
229 for (i = 1; i < count; i++) {
230 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
233 ext = strrchr(namelist[i]->d_name, '.');
235 // should not happen but whatever
242 // first find .cue files and remove files they reference
243 if (strcasecmp(ext, "cue") == 0)
245 snprintf(buf, sizeof(buf), "%s/%s", basedir,
246 namelist[i]->d_name);
256 while (fgets(buf, sizeof(buf), f)) {
257 ret = sscanf(buf, " FILE \"%256[^\"]\"", buf2);
259 ret = sscanf(buf, " FILE %256s", buf2);
263 p = strrchr(buf2, '/');
265 p = strrchr(buf2, '\\');
271 snprintf(buf, sizeof(buf), "%s/%s", basedir, p);
272 ret = STAT(buf, &statf);
274 rm_namelist_entry(namelist, count, p);
287 p = strcasestr(namelist[i]->d_name, "track");
289 ret = strtoul(p + 5, NULL, 10);
299 for (i = d = 1; i < count; i++)
300 if (namelist[i] != NULL)
301 namelist[d++] = namelist[i];
306 // propagate menu settings to the emu vars
307 static void menu_sync_config(void)
309 static int allow_abs_only_old;
314 Config.PsxType = region - 1;
316 Config.cycle_multiplier = 10000 / psx_clock;
318 switch (in_type_sel1) {
319 case 1: in_type[0] = PSE_PAD_TYPE_ANALOGPAD; break;
320 case 2: in_type[0] = PSE_PAD_TYPE_GUNCON; break;
321 case 3: in_type[0] = PSE_PAD_TYPE_GUN; break;
322 case 4: in_type[0] = PSE_PAD_TYPE_NONE; break;
323 default: in_type[0] = PSE_PAD_TYPE_STANDARD;
325 switch (in_type_sel2) {
326 case 1: in_type[1] = PSE_PAD_TYPE_ANALOGPAD; break;
327 case 2: in_type[1] = PSE_PAD_TYPE_GUNCON; break;
328 case 3: in_type[1] = PSE_PAD_TYPE_GUN; break;
329 case 4: in_type[1] = PSE_PAD_TYPE_NONE; break;
330 default: in_type[1] = PSE_PAD_TYPE_STANDARD;
332 if (in_evdev_allow_abs_only != allow_abs_only_old) {
334 allow_abs_only_old = in_evdev_allow_abs_only;
337 spu_config.iVolume = 768 + 128 * volume_boost;
338 pl_rearmed_cbs.frameskip = frameskip - 1;
339 pl_timing_prepare(Config.PsxType);
342 static void menu_set_defconfig(void)
344 emu_set_default_config();
347 g_scaler = SCALE_4_3;
350 frameskip = 1; // 1 - off
351 analog_deadzone = 50;
356 plat_target.vout_fullscreen = 0;
357 psx_clock = DEFAULT_PSX_CLOCK;
360 in_type_sel1 = in_type_sel2 = 0;
361 in_evdev_allow_abs_only = 0;
366 #define CE_CONFIG_STR(val) \
367 { #val, 0, Config.val }
369 #define CE_CONFIG_VAL(val) \
370 { #val, sizeof(Config.val), &Config.val }
372 #define CE_STR(val) \
375 #define CE_INTVAL(val) \
376 { #val, sizeof(val), &val }
378 #define CE_INTVAL_N(name, val) \
379 { name, sizeof(val), &val }
381 #define CE_INTVAL_P(val) \
382 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
384 // 'versioned' var, used when defaults change
385 #define CE_CONFIG_STR_V(val, ver) \
386 { #val #ver, 0, Config.val }
388 #define CE_INTVAL_V(val, ver) \
389 { #val #ver, sizeof(val), &val }
391 #define CE_INTVAL_PV(val, ver) \
392 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
394 static const struct {
400 CE_CONFIG_STR_V(Gpu, 3),
402 // CE_CONFIG_STR(Cdr),
406 CE_CONFIG_VAL(Debug),
407 CE_CONFIG_VAL(PsxOut),
408 CE_CONFIG_VAL(icache_emulation),
409 CE_CONFIG_VAL(DisableStalls),
411 CE_CONFIG_VAL(GpuListWalking),
412 CE_CONFIG_VAL(FractionalFramerate),
413 CE_CONFIG_VAL(PreciseExceptions),
414 CE_CONFIG_VAL(TurboCD),
416 CE_INTVAL_V(g_scaler, 3),
418 CE_INTVAL(g_layer_x),
419 CE_INTVAL(g_layer_y),
420 CE_INTVAL(g_layer_w),
421 CE_INTVAL(g_layer_h),
422 CE_INTVAL(soft_filter),
423 CE_INTVAL(scanlines),
424 CE_INTVAL(scanline_level),
425 CE_INTVAL(plat_target.vout_method),
426 CE_INTVAL(plat_target.hwfilter),
427 CE_INTVAL(plat_target.vout_fullscreen),
428 CE_INTVAL(state_slot),
429 CE_INTVAL(cpu_clock),
431 CE_INTVAL(in_type_sel1),
432 CE_INTVAL(in_type_sel2),
433 CE_INTVAL(analog_deadzone),
434 CE_INTVAL(memcard1_sel),
435 CE_INTVAL(memcard2_sel),
436 CE_INTVAL(g_autostateld_opt),
437 CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
438 CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
439 CE_INTVAL_V(frameskip, 4),
440 CE_INTVAL_P(gpu_peops.iUseDither),
441 CE_INTVAL_P(gpu_peops.dwActFixes),
442 CE_INTVAL_P(gpu_unai_old.lineskip),
443 CE_INTVAL_P(gpu_unai_old.abe_hack),
444 CE_INTVAL_P(gpu_unai_old.no_light),
445 CE_INTVAL_P(gpu_unai_old.no_blend),
446 CE_INTVAL_P(gpu_unai.ilace_force),
447 CE_INTVAL_P(gpu_unai.pixel_skip),
448 CE_INTVAL_P(gpu_unai.lighting),
449 CE_INTVAL_P(gpu_unai.fast_lighting),
450 CE_INTVAL_P(gpu_unai.blending),
451 CE_INTVAL_P(gpu_unai.dithering),
452 CE_INTVAL_P(gpu_unai.scale_hires),
453 CE_INTVAL_P(gpu_neon.allow_interlace),
454 CE_INTVAL_P(gpu_neon.enhancement_enable),
455 CE_INTVAL_P(gpu_neon.enhancement_no_main),
456 CE_INTVAL_P(gpu_neon.enhancement_tex_adj),
457 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
458 CE_INTVAL_P(gpu_peopsgl.iFilterType),
459 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
460 CE_INTVAL_P(gpu_peopsgl.iUseMask),
461 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
462 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
463 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
464 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
465 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
466 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
467 CE_INTVAL_P(screen_centering_type),
468 CE_INTVAL_P(screen_centering_x),
469 CE_INTVAL_P(screen_centering_y),
470 CE_INTVAL(spu_config.iUseReverb),
471 CE_INTVAL(spu_config.iXAPitch),
472 CE_INTVAL(spu_config.iUseInterpolation),
473 CE_INTVAL(spu_config.iTempo),
474 CE_INTVAL(spu_config.iUseThread),
475 CE_INTVAL(config_save_counter),
476 CE_INTVAL(in_evdev_allow_abs_only),
477 CE_INTVAL(volume_boost),
478 CE_INTVAL(psx_clock),
479 CE_INTVAL(new_dynarec_hacks),
480 CE_INTVAL(in_enable_vibration),
483 static char *get_cd_label(void)
485 static char trimlabel[33];
488 strncpy(trimlabel, CdromLabel, 32);
490 for (j = 31; j >= 0; j--)
491 if (trimlabel[j] == ' ')
497 static void make_cfg_fname(char *buf, size_t size, int is_game)
500 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
502 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
505 static void keys_write_all(FILE *f);
506 static char *mystrip(char *str);
508 static void write_u32_value(FILE *f, u32 v)
512 fprintf(f, "%x\n", v);
515 static int menu_write_config(int is_game)
517 char cfgfile[MAXPATHLEN];
521 config_save_counter++;
523 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
524 f = fopen(cfgfile, "w");
526 printf("menu_write_config: failed to open: %s\n", cfgfile);
530 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
531 fprintf(f, "%s = ", config_data[i].name);
532 switch (config_data[i].len) {
534 fprintf(f, "%s\n", (char *)config_data[i].val);
537 write_u32_value(f, *(u8 *)config_data[i].val);
540 write_u32_value(f, *(u16 *)config_data[i].val);
543 write_u32_value(f, *(u32 *)config_data[i].val);
546 printf("menu_write_config: unhandled len %d for %s\n",
547 (int)config_data[i].len, config_data[i].name);
558 static int menu_do_last_cd_img(int is_get)
560 static const char *defaults[] = { "/media", "/mnt/sd", "/mnt" };
566 snprintf(path, sizeof(path), "." PCSX_DOT_DIR "lastcdimg.txt");
567 f = fopen(path, is_get ? "r" : "w");
574 ret = fread(last_selected_fname, 1, sizeof(last_selected_fname) - 1, f);
575 last_selected_fname[ret] = 0;
576 mystrip(last_selected_fname);
579 fprintf(f, "%s\n", last_selected_fname);
584 for (i = 0; last_selected_fname[0] == 0
585 || STAT(last_selected_fname, &st) != 0; i++)
587 if (i >= ARRAY_SIZE(defaults))
589 strcpy(last_selected_fname, defaults[i]);
596 static void parse_str_val(char *cval, const char *src)
599 strncpy(cval, src, MAXPATHLEN);
600 cval[MAXPATHLEN - 1] = 0;
601 tmp = strchr(cval, '\n');
603 tmp = strchr(cval, '\r');
608 static void keys_load_all(const char *cfg);
610 int menu_load_config(int is_game)
612 char cfgfile[MAXPATHLEN];
618 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
619 f = fopen(cfgfile, "r");
621 printf("menu_load_config: failed to open: %s\n", cfgfile);
625 fseek(f, 0, SEEK_END);
628 printf("bad size %ld: %s\n", size, cfgfile);
632 cfg = malloc(size + 1);
636 fseek(f, 0, SEEK_SET);
637 if (fread(cfg, 1, size, f) != size) {
638 printf("failed to read: %s\n", cfgfile);
643 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
647 tmp = strstr(cfg, config_data[i].name);
650 tmp += strlen(config_data[i].name);
651 if (strncmp(tmp, " = ", 3) != 0)
655 if (config_data[i].len == 0) {
656 parse_str_val(config_data[i].val, tmp);
661 val = strtoul(tmp, &tmp2, 16);
662 if (tmp2 == NULL || tmp == tmp2)
663 continue; // parse failed
665 switch (config_data[i].len) {
667 *(u8 *)config_data[i].val = val;
670 *(u16 *)config_data[i].val = val;
673 *(u32 *)config_data[i].val = val;
676 printf("menu_load_config: unhandled len %d for %s\n",
677 (int)config_data[i].len, config_data[i].name);
683 char *tmp = strstr(cfg, "lastcdimg = ");
686 parse_str_val(last_selected_fname, tmp);
701 for (i = bios_sel = 0; bioses[i] != NULL; i++)
702 if (strcmp(Config.Bios, bioses[i]) == 0)
703 { bios_sel = i; break; }
705 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
706 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
707 { gpu_plugsel = i; break; }
709 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
710 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
711 { spu_plugsel = i; break; }
713 // memcard selections
714 char mcd1_old[sizeof(Config.Mcd1)];
715 char mcd2_old[sizeof(Config.Mcd2)];
716 strcpy(mcd1_old, Config.Mcd1);
717 strcpy(mcd2_old, Config.Mcd2);
719 if ((unsigned int)memcard1_sel < ARRAY_SIZE(memcards)) {
720 if (memcard1_sel == 0)
721 strcpy(Config.Mcd1, "none");
722 else if (memcards[memcard1_sel] != NULL)
723 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s",
724 MEMCARD_DIR, memcards[memcard1_sel]);
726 if ((unsigned int)memcard2_sel < ARRAY_SIZE(memcards)) {
727 if (memcard2_sel == 0)
728 strcpy(Config.Mcd2, "none");
729 else if (memcards[memcard2_sel] != NULL)
730 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s",
731 MEMCARD_DIR, memcards[memcard2_sel]);
733 if (strcmp(mcd1_old, Config.Mcd1) || strcmp(mcd2_old, Config.Mcd2))
734 LoadMcds(Config.Mcd1, Config.Mcd2);
739 static const char *filter_exts[] = {
740 "bin", "img", "mdf", "iso", "cue", "z",
744 "bz", "znx", "pbp", "cbn", "ppf", NULL
747 // rrrr rggg gggb bbbb
748 static unsigned short fname2color(const char *fname)
750 static const char *other_exts[] = {
751 "ccd", "toc", "mds", "sub", "table", "index", "sbi"
753 const char *ext = strrchr(fname, '.');
759 for (i = 0; filter_exts[i] != NULL; i++)
760 if (strcasecmp(ext, filter_exts[i]) == 0)
762 for (i = 0; i < array_size(other_exts); i++)
763 if (strcasecmp(ext, other_exts[i]) == 0)
768 static void draw_savestate_bg(int slot);
770 #define MENU_ALIGN_LEFT
771 #ifndef HAVE_PRE_ARMV7 // assume hires device
777 #include "libpicofe/menu.c"
779 // a bit of black magic here
780 static void draw_savestate_bg(int slot)
782 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
784 char fname[MAXPATHLEN];
791 ret = get_state_filename(fname, sizeof(fname), slot);
795 f = gzopen(fname, "rb");
799 if ((ret = (int)gzseek(f, 0x29933d, SEEK_SET)) != 0x29933d) {
800 fprintf(stderr, "gzseek failed: %d\n", ret);
805 gpu = malloc(sizeof(*gpu));
811 ret = gzread(f, gpu, sizeof(*gpu));
813 if (ret != sizeof(*gpu)) {
814 fprintf(stderr, "gzread failed\n");
818 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
820 if (gpu->ulStatus & 0x800000)
821 goto out; // disabled
823 x = gpu->ulControl[5] & 0x3ff;
824 y = (gpu->ulControl[5] >> 10) & 0x1ff;
825 w = psx_widths[(gpu->ulStatus >> 16) & 7];
826 tmp = gpu->ulControl[7];
827 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
828 if (gpu->ulStatus & 0x80000) // doubleheight
830 if (h <= 0 || h > 512)
836 s = (u16 *)gpu->psxVRam + y * 1024 + x;
838 x = max(0, g_menuscreen_w - w) & ~3;
839 y = max(0, g_menuscreen_h / 2 - h / 2);
840 w = min(g_menuscreen_w, w);
841 h = min(g_menuscreen_h, h);
842 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
844 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
845 if (gpu->ulStatus & 0x200000)
846 bgr888_to_rgb565(d, s, w * 3);
848 bgr555_to_rgb565(d, s, w * 2);
850 // darken this so that menu text is visible
851 if (g_menuscreen_w - w < 320)
852 menu_darken_bg(d, d, w, 0);
859 // -------------- key config --------------
861 me_bind_action me_ctrl_actions[] =
863 { "UP ", 1 << DKEY_UP},
864 { "DOWN ", 1 << DKEY_DOWN },
865 { "LEFT ", 1 << DKEY_LEFT },
866 { "RIGHT ", 1 << DKEY_RIGHT },
867 { "TRIANGLE", 1 << DKEY_TRIANGLE },
868 { "CIRCLE ", 1 << DKEY_CIRCLE },
869 { "CROSS ", 1 << DKEY_CROSS },
870 { "SQUARE ", 1 << DKEY_SQUARE },
871 { "L1 ", 1 << DKEY_L1 },
872 { "R1 ", 1 << DKEY_R1 },
873 { "L2 ", 1 << DKEY_L2 },
874 { "R2 ", 1 << DKEY_R2 },
875 { "L3 ", 1 << DKEY_L3 },
876 { "R3 ", 1 << DKEY_R3 },
877 { "START ", 1 << DKEY_START },
878 { "SELECT ", 1 << DKEY_SELECT },
882 me_bind_action emuctrl_actions[] =
884 { "Save State ", 1 << SACTION_SAVE_STATE },
885 { "Load State ", 1 << SACTION_LOAD_STATE },
886 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
887 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
888 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
889 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
890 { "Show/Hide FPS ", 1 << SACTION_TOGGLE_FPS },
891 #ifndef HAVE_PRE_ARMV7
892 { "Switch Renderer ", 1 << SACTION_SWITCH_DISPMODE },
894 { "Fast Forward ", 1 << SACTION_FAST_FORWARD },
895 #if MENU_SHOW_MINIMIZE
896 { "Minimize ", 1 << SACTION_MINIMIZE },
898 #if MENU_SHOW_FULLSCREEN
899 { "Toggle fullscreen", 1 << SACTION_TOGGLE_FULLSCREEN },
901 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
902 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
903 { "Gun A button ", 1 << SACTION_GUN_A },
904 { "Gun B button ", 1 << SACTION_GUN_B },
905 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
907 { "Volume Up ", 1 << SACTION_VOLUME_UP },
908 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
910 { "Analog toggle ", 1 << SACTION_ANALOG_TOGGLE },
914 static char *mystrip(char *str)
919 for (i = 0; i < len; i++)
920 if (str[i] != ' ') break;
921 if (i > 0) memmove(str, str + i, len - i + 1);
924 for (i = len - 1; i >= 0; i--)
925 if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
931 static void get_line(char *d, size_t size, const char *s)
936 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
945 static void keys_write_all(FILE *f)
949 for (d = 0; d < IN_MAX_DEVS; d++)
951 const int *binds = in_get_dev_binds(d);
952 const char *name = in_get_dev_name(d, 0, 0);
955 if (binds == NULL || name == NULL)
958 fprintf(f, "binddev = %s\n", name);
959 in_get_config(d, IN_CFG_BIND_COUNT, &count);
961 for (k = 0; k < count; k++)
966 act[0] = act[31] = 0;
967 name = in_get_key_name(d, k);
969 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
970 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
971 mask = me_ctrl_actions[i].mask;
973 strncpy(act, me_ctrl_actions[i].name, 31);
974 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
977 mask = me_ctrl_actions[i].mask << 16;
979 strncpy(act, me_ctrl_actions[i].name, 31);
980 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
985 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
986 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
987 mask = emuctrl_actions[i].mask;
989 strncpy(act, emuctrl_actions[i].name, 31);
990 fprintf(f, "bind %s = %s\n", name, mystrip(act));
996 for (k = 0; k < array_size(in_adev); k++)
999 fprintf(f, "bind_analog = %d\n", k);
1004 static int parse_bind_val(const char *val, int *type)
1008 *type = IN_BINDTYPE_NONE;
1012 if (strncasecmp(val, "player", 6) == 0)
1014 int player, shift = 0;
1015 player = atoi(val + 6) - 1;
1017 if ((unsigned int)player > 1)
1022 *type = IN_BINDTYPE_PLAYER12;
1023 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
1024 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
1025 return me_ctrl_actions[i].mask << shift;
1028 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
1029 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
1030 *type = IN_BINDTYPE_EMU;
1031 return emuctrl_actions[i].mask;
1038 static void keys_load_all(const char *cfg)
1040 char dev[256], key[128], *act;
1046 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
1049 // don't strip 'dev' because there are weird devices
1050 // with names with space at the end
1051 get_line(dev, sizeof(dev), p);
1053 dev_id = in_config_parse_dev(dev);
1055 printf("input: can't handle dev: %s\n", dev);
1059 in_unbind_all(dev_id, -1, -1);
1060 while ((p = strstr(p, "bind"))) {
1061 if (strncmp(p, "binddev = ", 10) == 0)
1064 if (strncmp(p, "bind_analog", 11) == 0) {
1065 ret = sscanf(p, "bind_analog = %d", &bind);
1068 printf("input: parse error: %16s..\n", p);
1071 if ((unsigned int)bind >= array_size(in_adev)) {
1072 printf("input: analog id %d out of range\n", bind);
1075 in_adev[bind] = dev_id;
1081 printf("input: parse error: %16s..\n", p);
1085 get_line(key, sizeof(key), p);
1086 act = strchr(key, '=');
1088 printf("parse failed: %16s..\n", p);
1096 bind = parse_bind_val(act, &bindtype);
1097 if (bind != -1 && bind != 0) {
1098 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
1099 in_config_bind_key(dev_id, key, bind, bindtype);
1102 lprintf("config: unhandled action \"%s\"\n", act);
1108 static int key_config_loop_wrap(int id, int keys)
1111 case MA_CTRL_PLAYER1:
1112 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
1114 case MA_CTRL_PLAYER2:
1115 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
1118 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
1126 static const char h_nubmode[] = "Maps nub-like analog controls to PSX ones better\n"
1127 "Might cause problems with real analog sticks";
1128 static const char *adevnames[IN_MAX_DEVS + 2];
1129 static int stick_sel[2];
1131 static menu_entry e_menu_keyconfig_analog[] =
1133 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
1134 mee_range (" X axis", 0, in_adev_axis[0][0], 0, 7),
1135 mee_range (" Y axis", 0, in_adev_axis[0][1], 0, 7),
1136 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[0], 1, h_nubmode),
1137 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
1138 mee_range (" X axis", 0, in_adev_axis[1][0], 0, 7),
1139 mee_range (" Y axis", 0, in_adev_axis[1][1], 0, 7),
1140 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[1], 1, h_nubmode),
1144 static int key_config_analog(int id, int keys)
1146 int i, d, count, sel = 0;
1147 int sel2dev_map[IN_MAX_DEVS];
1149 memset(adevnames, 0, sizeof(adevnames));
1150 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1151 memset(stick_sel, 0, sizeof(stick_sel));
1153 adevnames[0] = "None";
1155 for (d = 0; d < IN_MAX_DEVS; d++)
1157 const char *name = in_get_dev_name(d, 0, 1);
1162 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1166 if (in_adev[0] == d) stick_sel[0] = i;
1167 if (in_adev[1] == d) stick_sel[1] = i;
1169 adevnames[i++] = name;
1171 adevnames[i] = NULL;
1173 me_loop(e_menu_keyconfig_analog, &sel);
1175 in_adev[0] = sel2dev_map[stick_sel[0]];
1176 in_adev[1] = sel2dev_map[stick_sel[1]];
1181 static const char *mgn_dev_name(int id, int *offs)
1183 const char *name = NULL;
1186 if (id == MA_CTRL_DEV_FIRST)
1189 for (; it < IN_MAX_DEVS; it++) {
1190 name = in_get_dev_name(it, 1, 1);
1199 static const char *mgn_saveloadcfg(int id, int *offs)
1204 static int mh_savecfg(int id, int keys)
1206 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1207 menu_update_msg("config saved");
1209 menu_update_msg("failed to write config");
1214 static int mh_input_rescan(int id, int keys)
1216 //menu_sync_config();
1218 menu_update_msg("rescan complete.");
1223 static const char *men_in_type_sel[] = {
1224 "Standard (SCPH-1080)",
1225 "Analog (SCPH-1150)",
1231 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1232 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1233 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1235 static menu_entry e_menu_keyconfig[] =
1237 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1238 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1239 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
1240 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
1242 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1243 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
1244 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
1245 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
1246 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
1247 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1248 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1249 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1250 mee_handler ("Rescan devices:", mh_input_rescan),
1252 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
1253 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1254 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1255 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1256 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1257 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1258 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1262 static int menu_loop_keyconfig(int id, int keys)
1266 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1267 me_loop(e_menu_keyconfig, &sel);
1271 // ------------ gfx options menu ------------
1273 static const char *men_scaler[] = {
1274 "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL
1276 static const char *men_soft_filter[] = { "None",
1278 "scale2x", "eagle2x",
1281 static const char *men_dummy[] = { NULL };
1282 static const char *men_centering[] = { "Auto", "Ingame", "Borderless", "Force", NULL };
1283 static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
1284 "int. 4:3 - uses integer if possible, else fractional";
1285 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1286 "using d-pad or move it using R+d-pad";
1287 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1288 static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
1290 static const char *men_scanlines[] = { "OFF", "1", "2", "3", NULL };
1291 static const char h_scanline_l[] = "Scanline brightness, 0-100%";
1294 static int menu_loop_cscaler(int id, int keys)
1296 void *saved_layer = NULL;
1297 size_t saved_layer_size = 0;
1298 int was_layer_clipped = 0;
1304 g_scaler = SCALE_CUSTOM;
1305 saved_layer_size = last_vout_w * last_vout_h * last_vout_bpp / 8;
1306 saved_layer = malloc(saved_layer_size);
1308 memcpy(saved_layer, pl_vout_buf, saved_layer_size);
1310 plat_gvideo_open(Config.PsxType);
1312 menu_draw_begin(0, 1);
1313 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1318 if (saved_layer && last_vout_bpp == 16) {
1319 int top_x = max(0, -g_layer_x * last_vout_w / 800) + 1;
1320 int top_y = max(0, -g_layer_y * last_vout_h / 480) + 1;
1322 memcpy(pl_vout_buf, saved_layer, saved_layer_size);
1323 snprintf(text, sizeof(text), "%d,%d %dx%d",
1324 g_layer_x, g_layer_y, g_layer_w, g_layer_h);
1325 basic_text_out16_nf(pl_vout_buf, last_vout_w,
1326 top_x, top_y, text);
1327 basic_text_out16_nf(pl_vout_buf, last_vout_w, 2,
1328 last_vout_h - 20, "d-pad: resize, R+d-pad: move");
1329 pl_vout_buf = plat_gvideo_flip();
1332 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1333 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1334 if (inp & PBTN_UP) g_layer_y--;
1335 if (inp & PBTN_DOWN) g_layer_y++;
1336 if (inp & PBTN_LEFT) g_layer_x--;
1337 if (inp & PBTN_RIGHT) g_layer_x++;
1338 if (!(inp & PBTN_R)) {
1339 if (inp & PBTN_UP) g_layer_h += 2;
1340 if (inp & PBTN_DOWN) g_layer_h -= 2;
1341 if (inp & PBTN_LEFT) g_layer_w += 2;
1342 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1344 if (inp & (PBTN_MOK|PBTN_MBACK))
1347 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1348 int layer_clipped = 0;
1349 g_layer_x = max(-320, min(g_layer_x, 640));
1350 g_layer_y = max(-240, min(g_layer_y, 400));
1351 g_layer_w = max(160, g_layer_w);
1352 g_layer_h = max( 60, g_layer_h);
1353 if (g_layer_x < 0 || g_layer_x + g_layer_w > 800)
1355 if (g_layer_w > 800+400)
1356 g_layer_w = 800+400;
1357 if (g_layer_y < 0 || g_layer_y + g_layer_h > 480)
1359 if (g_layer_h > 480+360)
1360 g_layer_h = 480+360;
1362 plat_gvideo_open(Config.PsxType);
1363 if (layer_clipped || was_layer_clipped)
1364 pl_vout_buf = plat_gvideo_set_mode(&last_vout_w,
1365 &last_vout_h, &last_vout_bpp);
1366 was_layer_clipped = layer_clipped;
1370 plat_gvideo_close();
1376 static menu_entry e_menu_gfx_options[] =
1378 mee_enum ("Screen centering", MA_OPT_CENTERING, pl_rearmed_cbs.screen_centering_type, men_centering),
1379 mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1380 mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1381 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1382 mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1383 mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1385 mee_enum ("Scanlines", MA_OPT_SCANLINES, scanlines, men_scanlines),
1386 mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1388 mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1389 // mee_onoff ("Vsync", 0, vsync, 1),
1390 mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1394 static int menu_loop_gfx_options(int id, int keys)
1398 me_loop(e_menu_gfx_options, &sel);
1403 // ------------ bios/plugins ------------
1405 #ifdef BUILTIN_GPU_NEON
1407 static const char h_gpu_neon[] =
1408 "Configure built-in NEON GPU plugin";
1409 static const char h_gpu_neon_enhanced[] =
1410 "Renders in double resolution at the cost of lower performance\n"
1411 "(not available for high resolution games)";
1412 static const char h_gpu_neon_enhanced_hack[] =
1413 "Speed hack for above option (glitches some games)";
1414 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1416 static menu_entry e_menu_plugin_gpu_neon[] =
1418 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1419 mee_onoff_h ("Enhanced resolution", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1420 mee_onoff_h ("Enhanced res. speed hack", 0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1421 mee_onoff ("Enh. res. texture adjust", 0, pl_rearmed_cbs.gpu_neon.enhancement_tex_adj, 1),
1425 static int menu_loop_plugin_gpu_neon(int id, int keys)
1428 me_loop(e_menu_plugin_gpu_neon, &sel);
1434 static menu_entry e_menu_plugin_gpu_unai_old[] =
1436 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai_old.lineskip, 1),
1437 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai_old.abe_hack, 1),
1438 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai_old.no_light, 1),
1439 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai_old.no_blend, 1),
1443 static int menu_loop_plugin_gpu_unai_old(int id, int keys)
1446 me_loop(e_menu_plugin_gpu_unai_old, &sel);
1450 static menu_entry e_menu_plugin_gpu_unai[] =
1452 mee_onoff ("Interlace", 0, pl_rearmed_cbs.gpu_unai.ilace_force, 1),
1453 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_unai.dithering, 1),
1454 mee_onoff ("Lighting", 0, pl_rearmed_cbs.gpu_unai.lighting, 1),
1455 mee_onoff ("Fast lighting", 0, pl_rearmed_cbs.gpu_unai.fast_lighting, 1),
1456 mee_onoff ("Blending", 0, pl_rearmed_cbs.gpu_unai.blending, 1),
1457 mee_onoff ("Pixel skip", 0, pl_rearmed_cbs.gpu_unai.pixel_skip, 1),
1461 static int menu_loop_plugin_gpu_unai(int id, int keys)
1464 me_loop(e_menu_plugin_gpu_unai, &sel);
1469 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1470 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1471 static const char h_gpu_1[] = "Capcom fighting games";
1472 static const char h_gpu_2[] = "Black screens in Lunar";
1473 static const char h_gpu_3[] = "Compatibility mode";
1474 static const char h_gpu_6[] = "Pandemonium 2";
1475 //static const char h_gpu_7[] = "Skip every second frame";
1476 static const char h_gpu_8[] = "Needed by Dark Forces";
1477 static const char h_gpu_9[] = "better g-colors, worse textures";
1478 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1480 static menu_entry e_menu_plugin_gpu_peops[] =
1482 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1483 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1484 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1485 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1486 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1487 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1488 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1489 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1490 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1491 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1495 static int menu_loop_plugin_gpu_peops(int id, int keys)
1498 me_loop(e_menu_plugin_gpu_peops, &sel);
1502 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1503 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1504 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1506 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1508 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1509 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1510 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1511 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1512 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1513 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1514 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1515 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1516 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1517 mee_label ("Fixes/hacks:"),
1518 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1519 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1520 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1521 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1522 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1523 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1524 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1525 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1526 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1527 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1528 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1532 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1535 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1539 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1540 static const char h_spu_volboost[] = "Large values cause distortion";
1541 static const char h_spu_tempo[] = "Slows down audio if emu is too slow\n"
1542 "This is inaccurate and breaks games";
1544 static menu_entry e_menu_plugin_spu[] =
1546 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1547 mee_onoff ("Reverb", 0, spu_config.iUseReverb, 1),
1548 mee_enum ("Interpolation", 0, spu_config.iUseInterpolation, men_spu_interp),
1549 //mee_onoff ("Adjust XA pitch", 0, spu_config.iXAPitch, 1),
1550 mee_onoff_h ("Adjust tempo", 0, spu_config.iTempo, 1, h_spu_tempo),
1554 static int menu_loop_plugin_spu(int id, int keys)
1557 me_loop(e_menu_plugin_spu, &sel);
1561 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1562 "savestates and can't be changed there. Must save\n"
1563 "config and reload the game for change to take effect";
1564 static const char h_plugin_gpu[] =
1565 #ifdef BUILTIN_GPU_NEON
1566 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1568 "gpu_peops is Pete's soft GPU, slow but accurate\n"
1569 "gpu_unai_old is from old PCSX4ALL, fast but glitchy\n"
1570 "gpu_unai is newer, more accurate but slower\n"
1571 "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1572 "must save config and reload the game if changed";
1573 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1574 "must save config and reload the game if changed";
1575 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1576 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1577 static const char h_gpu_unai_old[] = "Configure Unai/PCSX4ALL Team GPU plugin (old)";
1578 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team plugin (new)";
1579 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1581 static menu_entry e_menu_plugin_options[] =
1583 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1584 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1585 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1586 #ifdef BUILTIN_GPU_NEON
1587 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1589 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1590 mee_handler_h ("Configure gpu_unai_old GPU plugin", menu_loop_plugin_gpu_unai_old, h_gpu_unai_old),
1591 mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1592 mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1593 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1597 static menu_entry e_menu_main2[];
1599 static int menu_loop_plugin_options(int id, int keys)
1602 me_loop(e_menu_plugin_options, &sel);
1604 // sync BIOS/plugins
1605 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1606 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1607 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1608 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1613 // ------------ adv options menu ------------
1616 static const char h_cfg_noch[] = "Disables game-specific compatibility hacks";
1617 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1618 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1619 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1621 static const char h_cfg_stalls[] = "Will cause some games to run too fast";
1623 static menu_entry e_menu_speed_hacks[] =
1626 mee_onoff_h ("Disable compat hacks", 0, new_dynarec_hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1627 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1628 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1629 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1631 mee_onoff_h ("Disable CPU/GTE stalls", 0, menu_iopts[0], 1, h_cfg_stalls),
1635 static int menu_loop_speed_hacks(int id, int keys)
1638 menu_iopts[0] = Config.DisableStalls;
1639 me_loop(e_menu_speed_hacks, &sel);
1640 Config.DisableStalls = menu_iopts[0];
1644 static const char *men_autooo[] = { "Auto", "Off", "On", NULL };
1646 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1647 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1648 "(green: normal, red: fmod, blue: noise)";
1649 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1650 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1651 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1652 "(proper .cue/.bin dump is needed otherwise)";
1654 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1655 "Might be useful to overcome some dynarec bugs";
1657 static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1658 static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1659 static const char h_cfg_exc[] = "Emulate some PSX's debug hw like breakpoints\n"
1660 "and exceptions (slow, interpreter only, keep off)";
1661 static const char h_cfg_gpul[] = "Try enabling this if the game misses some graphics\n"
1662 "causes a performance hit";
1663 static const char h_cfg_ffps[] = "Instead of 50/60fps for PAL/NTSC use ~49.75/59.81\n"
1664 "Closer to real hw but doesn't match modern displays.";
1665 static const char h_cfg_tcd[] = "Greatly reduce CD load times. Breaks some games.";
1666 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1667 "(adjust this if the game is too slow/too fast/hangs)";
1669 enum { AMO_XA, AMO_CDDA, AMO_IC, AMO_BP, AMO_CPU, AMO_GPUL, AMO_FFPS, AMO_TCD };
1671 static menu_entry e_menu_adv_options[] =
1673 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1674 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1675 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1676 mee_onoff_h ("Disable XA Decoding", 0, menu_iopts[AMO_XA], 1, h_cfg_xa),
1677 mee_onoff_h ("Disable CD Audio", 0, menu_iopts[AMO_CDDA], 1, h_cfg_cdda),
1678 mee_onoff_h ("ICache emulation", 0, menu_iopts[AMO_IC], 1, h_cfg_icache),
1679 mee_onoff_h ("BP exception emulation", 0, menu_iopts[AMO_BP], 1, h_cfg_exc),
1680 mee_enum_h ("GPU l-list slow walking",0, menu_iopts[AMO_GPUL], men_autooo, h_cfg_gpul),
1681 mee_enum_h ("Fractional framerate", 0, menu_iopts[AMO_FFPS], men_autooo, h_cfg_ffps),
1682 mee_onoff_h ("Turbo CD-ROM ", 0, menu_iopts[AMO_TCD], 1, h_cfg_tcd),
1683 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
1684 mee_onoff_h ("Disable dynarec (slow!)",0, menu_iopts[AMO_CPU], 1, h_cfg_nodrc),
1686 mee_range_h ("PSX CPU clock, %", 0, psx_clock, 1, 500, h_cfg_psxclk),
1687 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1691 static int menu_loop_adv_options(int id, int keys)
1698 { &Config.Xa, &menu_iopts[AMO_XA] },
1699 { &Config.Cdda, &menu_iopts[AMO_CDDA] },
1700 { &Config.icache_emulation, &menu_iopts[AMO_IC] },
1701 { &Config.PreciseExceptions, &menu_iopts[AMO_BP] },
1702 { &Config.Cpu, &menu_iopts[AMO_CPU] },
1703 { &Config.TurboCD, &menu_iopts[AMO_TCD] },
1706 for (i = 0; i < ARRAY_SIZE(opts); i++)
1707 *opts[i].mopt = *opts[i].opt;
1708 menu_iopts[AMO_GPUL] = Config.GpuListWalking + 1;
1709 menu_iopts[AMO_FFPS] = Config.FractionalFramerate + 1;
1711 me_loop(e_menu_adv_options, &sel);
1713 for (i = 0; i < ARRAY_SIZE(opts); i++)
1714 *opts[i].opt = *opts[i].mopt;
1715 Config.GpuListWalking = menu_iopts[AMO_GPUL] - 1;
1716 Config.FractionalFramerate = menu_iopts[AMO_FFPS] - 1;
1721 // ------------ options menu ------------
1723 static int mh_restore_defaults(int id, int keys)
1725 menu_set_defconfig();
1726 menu_update_msg("defaults restored");
1730 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1731 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1733 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1734 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1735 "loading state or both";
1737 static const char h_restore_def[] = "Switches back to default / recommended\n"
1739 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1740 static const char h_sputhr[] = "Warning: has some known bugs\n";
1742 static menu_entry e_menu_options[] =
1744 // mee_range ("Save slot", 0, state_slot, 0, 9),
1745 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1746 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1747 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1748 mee_enum ("Region", 0, region, men_region),
1749 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1751 mee_onoff_h ("Use C64x DSP for sound", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1753 mee_onoff_h ("Threaded SPU", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1755 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1756 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1757 mee_handler ("[Advanced]", menu_loop_adv_options),
1758 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1759 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1760 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1764 static int menu_loop_options(int id, int keys)
1768 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1769 me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1770 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1772 me_loop(e_menu_options, &sel);
1777 // ------------ debug menu ------------
1779 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1781 int w = min(g_menuscreen_w, 1024);
1782 int h = min(g_menuscreen_h, 512);
1783 u16 *d = g_menuscreen_ptr;
1784 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1788 gpuf->ulFreezeVersion = 1;
1789 if (GPU_freeze != NULL)
1790 GPU_freeze(1, gpuf);
1792 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1793 bgr555_to_rgb565(d, s, w * 2);
1795 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1796 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1797 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1798 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1799 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1802 static void debug_menu_loop(void)
1804 int inp, df_x = 0, df_y = 0;
1807 gpuf = malloc(sizeof(*gpuf));
1813 menu_draw_begin(0, 1);
1814 draw_frame_debug(gpuf, df_x, df_y);
1817 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1818 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1819 if (inp & PBTN_MBACK) break;
1820 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1821 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1822 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x -= 2; }
1823 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1829 // --------- memcard manager ---------
1831 static void draw_mc_icon(int dx, int dy, const u16 *s)
1836 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1838 for (y = 0; y < 16; y++, s += 16) {
1839 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1840 for (x = 0; x < 16; x++) {
1842 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1843 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1849 static void draw_mc_bg(void)
1851 McdBlock *blocks1, *blocks2;
1855 blocks1 = malloc(15 * sizeof(blocks1[0]));
1856 blocks2 = malloc(15 * sizeof(blocks1[0]));
1857 if (blocks1 == NULL || blocks2 == NULL)
1860 for (i = 0; i < 15; i++) {
1861 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1862 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1865 menu_draw_begin(1, 1);
1867 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1869 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1873 maxicons = g_menuscreen_h / 32;
1876 row2 = g_menuscreen_w / 2;
1877 for (i = 0; i < maxicons; i++) {
1878 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1879 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1881 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1882 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1885 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1893 static void handle_memcard_sel(void)
1895 strcpy(Config.Mcd1, "none");
1896 if (memcard1_sel != 0)
1897 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1898 strcpy(Config.Mcd2, "none");
1899 if (memcard2_sel != 0)
1900 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1901 LoadMcds(Config.Mcd1, Config.Mcd2);
1905 static menu_entry e_memcard_options[] =
1907 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1908 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1912 static int menu_loop_memcards(int id, int keys)
1918 memcard1_sel = memcard2_sel = 0;
1919 p = strrchr(Config.Mcd1, '/');
1921 for (i = 0; memcards[i] != NULL; i++)
1922 if (strcmp(p + 1, memcards[i]) == 0)
1923 { memcard1_sel = i; break; }
1924 p = strrchr(Config.Mcd2, '/');
1926 for (i = 0; memcards[i] != NULL; i++)
1927 if (strcmp(p + 1, memcards[i]) == 0)
1928 { memcard2_sel = i; break; }
1930 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1932 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1937 // ------------ cheats menu ------------
1939 static void draw_cheatlist(int sel)
1941 int max_cnt, start, i, pos, active;
1943 max_cnt = g_menuscreen_h / me_sfont_h;
1944 start = max_cnt / 2 - sel;
1946 menu_draw_begin(1, 1);
1948 for (i = 0; i < NumCheats; i++) {
1950 if (pos < 0) continue;
1951 if (pos >= max_cnt) break;
1952 active = Cheats[i].Enabled;
1953 smalltext_out16(14, pos * me_sfont_h,
1954 active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1955 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1956 Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1960 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1962 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1966 static void menu_loop_cheats(void)
1968 static int menu_sel = 0;
1973 draw_cheatlist(menu_sel);
1974 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1975 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1976 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1977 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1978 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1979 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1980 if (inp & PBTN_MOK) { // action
1981 if (menu_sel < NumCheats)
1982 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1985 if (inp & PBTN_MBACK)
1990 // --------- main menu help ----------
1992 static void menu_bios_warn(void)
1995 static const char msg[] =
1996 "You don't seem to have copied any BIOS\n"
1998 MENU_BIOS_PATH "\n\n"
2000 "While many games work fine with fake\n"
2001 "(HLE) BIOS, others (like MGS and FF8)\n"
2002 "require BIOS to work.\n"
2003 "After copying the file, you'll also need\n"
2004 "to select it in the emu's menu:\n"
2005 "options->[BIOS/Plugins]\n\n"
2006 "The file is usually named SCPH1001.BIN,\n"
2007 "but other not compressed files can be\n"
2009 "Press %s or %s to continue";
2010 char tmp_msg[sizeof(msg) + 64];
2012 snprintf(tmp_msg, sizeof(tmp_msg), msg,
2013 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
2016 draw_menu_message(tmp_msg, NULL);
2018 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2019 if (inp & (PBTN_MBACK|PBTN_MOK))
2024 // ------------ main menu ------------
2026 static menu_entry e_menu_main[];
2028 static void draw_frame_main(void)
2037 if (CdromId[0] != 0) {
2038 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
2039 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
2040 Config.HLE ? "HLE" : "BIOS");
2041 smalltext_out16(4, 1, buff, 0x105f);
2045 capacity = plat_target_bat_capacity_get();
2047 tmp = localtime(<ime);
2048 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
2049 if (capacity >= 0) {
2050 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
2055 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
2059 static void draw_frame_credits(void)
2061 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
2064 static const char credits_text[] =
2066 "(C) 1999-2003 PCSX Team\n"
2067 "(C) 2005-2009 PCSX-df Team\n"
2068 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
2069 "ARM recompiler (C) 2009-2011 Ari64\n"
2070 #ifdef BUILTIN_GPU_NEON
2071 "ARM NEON GPU (c) 2011-2012 Exophase\n"
2073 "PEOpS GPU and SPU by Pete Bernert\n"
2074 " and the P.E.Op.S. team\n"
2075 "PCSX4ALL plugin by PCSX4ALL team\n"
2076 " Chui, Franxis, Unai\n\n"
2077 "integration, optimization and\n"
2078 " frontend (C) 2010-2015 notaz\n";
2080 static int reset_game(void)
2086 if (LoadCdrom() == -1)
2092 static int reload_plugins(const char *cdimg)
2098 set_cd_image(cdimg);
2100 pcnt_hook_plugins();
2102 if (OpenPlugins() == -1) {
2103 menu_update_msg("failed to open plugins");
2106 plugin_call_rearmed_cbs();
2108 cdrIsoMultidiskCount = 1;
2110 CdromLabel[0] = '\0';
2115 static int run_bios(void)
2117 boolean origSlowBoot = Config.SlowBoot;
2123 if (reload_plugins(NULL) != 0)
2125 Config.SlowBoot = 1;
2127 Config.SlowBoot = origSlowBoot;
2133 static int run_exe(void)
2135 const char *exts[] = { "exe", NULL };
2138 fname = menu_loop_romsel(last_selected_fname,
2139 sizeof(last_selected_fname), exts, NULL);
2144 if (reload_plugins(NULL) != 0)
2148 if (Load(fname) != 0) {
2149 menu_update_msg("exe load failed, bad file?");
2158 static int run_cd_image(const char *fname)
2160 int autoload_state = g_autostateld_opt;
2161 size_t fname_len = strlen(fname);
2162 const char *ppfname = NULL;
2165 // simle ppf handling, like game.chd.ppf
2166 if (4 < fname_len && fname_len < sizeof(fname2)
2167 && strcasecmp(fname + fname_len - 4, ".ppf") == 0) {
2168 memcpy(fname2, fname, fname_len - 4);
2169 fname2[fname_len - 4] = 0;
2175 reload_plugins(fname);
2177 // always autodetect, menu_sync_config will override as needed
2180 if (CheckCdrom() == -1) {
2181 // Only check the CD if we are starting the console with a CD
2183 menu_update_msg("unsupported/invalid CD image");
2187 BuildPPFCache(ppfname);
2191 // Read main executable directly from CDRom and start it
2192 if (LoadCdrom() == -1) {
2194 menu_update_msg("failed to load CD image");
2201 if (autoload_state) {
2202 unsigned int newest = 0;
2203 int time = 0, slot, newest_slot = -1;
2205 for (slot = 0; slot < 10; slot++) {
2206 if (emu_check_save_file(slot, &time)) {
2207 if ((unsigned int)time > newest) {
2214 if (newest_slot >= 0) {
2215 lprintf("autoload slot %d\n", newest_slot);
2216 emu_load_state(newest_slot);
2219 lprintf("no save to autoload.\n");
2226 static int romsel_run(void)
2228 int prev_gpu, prev_spu;
2231 fname = menu_loop_romsel(last_selected_fname,
2232 sizeof(last_selected_fname), filter_exts,
2233 optional_cdimg_filter);
2237 printf("selected file: %s\n", fname);
2239 new_dynarec_clear_full();
2241 if (run_cd_image(fname) != 0)
2244 prev_gpu = gpu_plugsel;
2245 prev_spu = spu_plugsel;
2246 if (menu_load_config(1) != 0)
2247 menu_load_config(0);
2249 // check for plugin changes, have to repeat
2250 // loading if game config changed plugins to reload them
2251 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2252 printf("plugin change detected, reloading plugins..\n");
2253 if (run_cd_image(fname) != 0)
2257 strcpy(last_selected_fname, fname);
2258 menu_do_last_cd_img(0);
2262 static int swap_cd_image(void)
2266 fname = menu_loop_romsel(last_selected_fname,
2267 sizeof(last_selected_fname), filter_exts,
2268 optional_cdimg_filter);
2272 printf("selected file: %s\n", fname);
2275 CdromLabel[0] = '\0';
2277 set_cd_image(fname);
2278 if (ReloadCdromPlugin() < 0) {
2279 menu_update_msg("failed to load cdr plugin");
2282 if (CDR_open() < 0) {
2283 menu_update_msg("failed to open cdr plugin");
2287 SetCdOpenCaseTime(time(NULL) + 2);
2290 strcpy(last_selected_fname, fname);
2294 static int swap_cd_multidisk(void)
2296 cdrIsoMultidiskSelect++;
2298 CdromLabel[0] = '\0';
2301 if (CDR_open() < 0) {
2302 menu_update_msg("failed to open cdr plugin");
2306 SetCdOpenCaseTime(time(NULL) + 2);
2312 static void load_pcsx_cht(void)
2314 static const char *exts[] = { "cht", NULL };
2318 fname = menu_loop_romsel(last_selected_fname,
2319 sizeof(last_selected_fname), exts, NULL);
2323 printf("selected cheat file: %s\n", fname);
2326 if (NumCheats == 0 && NumCodes == 0)
2327 menu_update_msg("failed to load cheats");
2329 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2330 menu_update_msg(msg);
2332 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2335 static int main_menu_handler(int id, int keys)
2339 case MA_MAIN_RESUME_GAME:
2343 case MA_MAIN_SAVE_STATE:
2345 return menu_loop_savestate(0);
2347 case MA_MAIN_LOAD_STATE:
2349 return menu_loop_savestate(1);
2351 case MA_MAIN_RESET_GAME:
2352 if (ready_to_go && reset_game() == 0)
2355 case MA_MAIN_LOAD_ROM:
2356 if (romsel_run() == 0)
2359 case MA_MAIN_SWAP_CD:
2360 if (swap_cd_image() == 0)
2363 case MA_MAIN_SWAP_CD_MULTI:
2364 if (swap_cd_multidisk() == 0)
2367 case MA_MAIN_RUN_BIOS:
2368 if (run_bios() == 0)
2371 case MA_MAIN_RUN_EXE:
2375 case MA_MAIN_CHEATS:
2378 case MA_MAIN_LOAD_CHEATS:
2381 case MA_MAIN_CREDITS:
2382 draw_menu_message(credits_text, draw_frame_credits);
2383 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2386 emu_core_ask_exit();
2389 lprintf("%s: something unknown selected\n", __FUNCTION__);
2396 static menu_entry e_menu_main2[] =
2398 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2399 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2400 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2401 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2402 mee_handler ("Memcard manager", menu_loop_memcards),
2403 mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS, main_menu_handler),
2407 static int main_menu2_handler(int id, int keys)
2411 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2412 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2413 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2414 me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2416 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2419 static const char h_extra[] = "Change CD, manage memcards..\n";
2421 static menu_entry e_menu_main[] =
2425 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2426 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2427 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2428 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2429 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2430 mee_handler ("Options", menu_loop_options),
2431 mee_handler ("Controls", menu_loop_keyconfig),
2432 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
2433 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2434 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2435 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2439 // ----------------------------
2441 static void menu_leave_emu(void);
2443 void menu_loop(void)
2445 static int warned_about_bios = 0;
2450 if (config_save_counter == 0) {
2452 if (bioses[1] != NULL) {
2453 // autoselect BIOS to make user's life easier
2454 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2457 else if (!warned_about_bios) {
2459 warned_about_bios = 1;
2463 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2464 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2465 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2466 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2467 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2469 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2472 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2473 } while (!ready_to_go && !g_emu_want_quit);
2475 /* wait until menu, ok, back is released */
2476 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2479 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2484 static int qsort_strcmp(const void *p1, const void *p2)
2486 char * const *s1 = (char * const *)p1;
2487 char * const *s2 = (char * const *)p2;
2488 return strcasecmp(*s1, *s2);
2491 static void scan_bios_plugins(void)
2493 char fname[MAXPATHLEN];
2495 int bios_i, gpu_i, spu_i, mc_i;
2500 gpu_plugins[0] = "builtin_gpu";
2501 spu_plugins[0] = "builtin_spu";
2502 memcards[0] = "(none)";
2503 bios_i = gpu_i = spu_i = mc_i = 1;
2505 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2506 dir = opendir(fname);
2508 perror("scan_bios_plugins bios opendir");
2523 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2526 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2527 if (stat(fname, &st) != 0
2528 || (st.st_size != 512*1024 && st.st_size != 4*1024*1024)) {
2529 printf("bad BIOS file: %s\n", ent->d_name);
2533 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2534 bioses[bios_i++] = strdup(ent->d_name);
2538 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2544 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2545 dir = opendir(fname);
2547 perror("scan_bios_plugins plugins opendir");
2561 p = strstr(ent->d_name, ".so");
2565 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2566 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2568 fprintf(stderr, "%s\n", dlerror());
2572 // now what do we have here?
2573 tmp = dlsym(h, "GPUinit");
2576 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2577 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2581 tmp = dlsym(h, "SPUinit");
2584 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2585 spu_plugins[spu_i++] = strdup(ent->d_name);
2589 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2596 dir = opendir("." MEMCARD_DIR);
2598 perror("scan_bios_plugins memcards opendir");
2613 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2616 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2617 if (stat(fname, &st) != 0) {
2618 printf("bad memcard file: %s\n", ent->d_name);
2622 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2623 memcards[mc_i++] = strdup(ent->d_name);
2627 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2631 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2636 void menu_init(void)
2638 char buff[MAXPATHLEN];
2641 cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2643 scan_bios_plugins();
2646 menu_set_defconfig();
2647 menu_load_config(0);
2648 menu_do_last_cd_img(1);
2653 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2654 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2655 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2656 fprintf(stderr, "OOM\n");
2660 emu_make_path(buff, "skin/background.png", sizeof(buff));
2661 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2663 i = plat_target.cpu_clock_set != NULL
2664 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2665 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, i);
2667 i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2668 e_menu_gfx_options[i].data = plat_target.vout_methods;
2669 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2670 plat_target.vout_methods != NULL);
2672 i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2673 e_menu_gfx_options[i].data = plat_target.hwfilters;
2674 me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2675 plat_target.hwfilters != NULL);
2677 me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2678 plat_target.gamma_set != NULL);
2680 #ifdef HAVE_PRE_ARMV7
2681 me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2683 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2684 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2685 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2686 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2687 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2688 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2689 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2692 void menu_notify_mode_change(int w, int h, int bpp)
2696 last_vout_bpp = bpp;
2699 static void menu_leave_emu(void)
2701 if (GPU_close != NULL) {
2702 int ret = GPU_close();
2704 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2707 plat_video_menu_enter(ready_to_go);
2709 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2710 if (pl_vout_buf != NULL && ready_to_go) {
2711 int x = max(0, g_menuscreen_w - last_vout_w);
2712 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2713 int w = min(g_menuscreen_w, last_vout_w);
2714 int h = min(g_menuscreen_h, last_vout_h);
2715 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2716 char *s = pl_vout_buf;
2718 if (last_vout_bpp == 16) {
2719 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2720 menu_darken_bg(d, s, w, 0);
2723 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2724 rgb888_to_rgb565(d, s, w * 3);
2725 menu_darken_bg(d, d, w, 0);
2731 cpu_clock = plat_target_cpu_clock_get();
2734 void menu_prepare_emu(void)
2736 R3000Acpu *prev_cpu = psxCpu;
2738 plat_video_menu_leave();
2740 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
2741 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2745 if (psxCpu != prev_cpu) {
2746 prev_cpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
2747 prev_cpu->Shutdown();
2750 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
2754 psxCpu->ApplyConfig();
2756 // core doesn't care about Config.Cdda changes,
2757 // so handle them manually here
2762 plat_target_cpu_clock_set(cpu_clock);
2764 // push config to GPU plugin
2765 plugin_call_rearmed_cbs();
2767 if (GPU_open != NULL) {
2768 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2770 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2774 void menu_update_msg(const char *msg)
2776 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2777 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2779 menu_error_time = plat_get_ticks_ms();
2780 lprintf("msg: %s\n", menu_error_msg);
2783 void menu_finish(void)
2785 if (cpu_clock_st > 0)
2786 plat_target_cpu_clock_set(cpu_clock_st);