2 * (C) GraÅžvydas "notaz" Ignotas, 2010-2015
4 * This work is licensed under the terms of any of these licenses
6 * - GNU GPL, version 2 or later.
7 * - GNU LGPL, version 2.1 or later.
8 * See the COPYING file in the top-level directory.
22 #include <sys/types.h>
31 #include "plugin_lib.h"
35 #include "libpicofe/plat.h"
36 #include "libpicofe/input.h"
37 #include "libpicofe/linux/in_evdev.h"
38 #include "libpicofe/plat.h"
39 #include "../libpcsxcore/misc.h"
40 #include "../libpcsxcore/cdrom.h"
41 #include "../libpcsxcore/cdriso.h"
42 #include "../libpcsxcore/cheat.h"
43 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
44 #include "../plugins/dfsound/spu_config.h"
45 #include "psemu_plugin_defs.h"
46 #include "arm_features.h"
49 #define REARMED_BIRTHDAY_TIME 1293306830 /* 25 Dec 2010 */
51 #define array_size(x) (sizeof(x) / sizeof(x[0]))
62 MA_MAIN_SWAP_CD_MULTI,
93 MA_OPT_SCANLINE_LEVEL,
97 static int last_vout_w, last_vout_h, last_vout_bpp;
98 static int cpu_clock, cpu_clock_st, volume_boost;
99 static int frameskip = 1; // 0 - auto, 1 - off
100 static char last_selected_fname[MAXPATHLEN];
101 static int config_save_counter, region, in_type_sel1, in_type_sel2;
102 static int psx_clock;
103 static int memcard1_sel = -1, memcard2_sel = -1;
104 extern int g_autostateld_opt;
105 static int menu_iopts[8];
106 int g_opts, g_scaler, g_gamma = 100;
107 int scanlines, scanline_level = 20;
108 int soft_scaling, analog_deadzone; // for Caanoo
111 #ifndef HAVE_PRE_ARMV7
112 #define DEFAULT_PSX_CLOCK (10000 / CYCLE_MULT_DEFAULT)
113 #define DEFAULT_PSX_CLOCK_S "57"
115 #define DEFAULT_PSX_CLOCK 50
116 #define DEFAULT_PSX_CLOCK_S "50"
119 static const char *bioses[32];
120 static const char *gpu_plugins[16];
121 static const char *spu_plugins[16];
122 static const char *memcards[32];
123 static int bios_sel, gpu_plugsel, spu_plugsel;
125 #ifndef UI_FEATURES_H
126 #define MENU_BIOS_PATH "bios/"
127 #define MENU_SHOW_VARSCALER 0
128 #define MENU_SHOW_VOUTMODE 1
129 #define MENU_SHOW_SCALER2 0
130 #define MENU_SHOW_NUBS_BTNS 0
131 #define MENU_SHOW_VIBRATION 0
132 #define MENU_SHOW_DEADZONE 0
133 #define MENU_SHOW_MINIMIZE 0
134 #define MENU_SHOW_FULLSCREEN 1
135 #define MENU_SHOW_VOLUME 0
138 static int min(int x, int y) { return x < y ? x : y; }
139 static int max(int x, int y) { return x > y ? x : y; }
141 void emu_make_path(char *buff, const char *end, int size)
145 end_len = strlen(end);
146 pos = plat_get_root_dir(buff, size);
147 strncpy(buff + pos, end, size - pos);
149 if (pos + end_len > size - 1)
150 printf("Warning: path truncated: %s\n", buff);
153 static int emu_check_save_file(int slot, int *time)
155 char fname[MAXPATHLEN];
159 ret = emu_check_state(slot);
160 if (ret != 0 || time == NULL)
161 return ret == 0 ? 1 : 0;
163 ret = get_state_filename(fname, sizeof(fname), slot);
167 ret = stat(fname, &status);
171 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
172 return 1; // probably bad rtc like on some Caanoos
174 *time = status.st_mtime;
179 static int emu_save_load_game(int load, int unused)
184 ret = emu_load_state(state_slot);
186 // reflect hle/bios mode from savestate
189 else if (bios_sel == 0 && bioses[1] != NULL)
190 // XXX: maybe find the right bios instead
194 ret = emu_save_state(state_slot);
199 static void rm_namelist_entry(struct dirent **namelist,
200 int count, const char *name)
204 for (i = 1; i < count; i++) {
205 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
208 if (strcmp(name, namelist[i]->d_name) == 0) {
216 static int optional_cdimg_filter(struct dirent **namelist, int count,
220 char buf[256], buf2[257];
221 int i, d, ret, good_cue;
228 for (i = 1; i < count; i++) {
229 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
232 ext = strrchr(namelist[i]->d_name, '.');
234 // should not happen but whatever
241 // first find .cue files and remove files they reference
242 if (strcasecmp(ext, "cue") == 0)
244 snprintf(buf, sizeof(buf), "%s/%s", basedir,
245 namelist[i]->d_name);
255 while (fgets(buf, sizeof(buf), f)) {
256 ret = sscanf(buf, " FILE \"%256[^\"]\"", buf2);
258 ret = sscanf(buf, " FILE %256s", buf2);
262 p = strrchr(buf2, '/');
264 p = strrchr(buf2, '\\');
270 snprintf(buf, sizeof(buf), "%s/%s", basedir, p);
271 ret = STAT(buf, &statf);
273 rm_namelist_entry(namelist, count, p);
286 p = strcasestr(namelist[i]->d_name, "track");
288 ret = strtoul(p + 5, NULL, 10);
298 for (i = d = 1; i < count; i++)
299 if (namelist[i] != NULL)
300 namelist[d++] = namelist[i];
305 // propagate menu settings to the emu vars
306 static void menu_sync_config(void)
308 static int allow_abs_only_old;
313 Config.PsxType = region - 1;
315 Config.cycle_multiplier = 10000 / psx_clock;
317 switch (in_type_sel1) {
318 case 1: in_type[0] = PSE_PAD_TYPE_ANALOGPAD; break;
319 case 2: in_type[0] = PSE_PAD_TYPE_GUNCON; break;
320 case 3: in_type[0] = PSE_PAD_TYPE_GUN; break;
321 case 4: in_type[0] = PSE_PAD_TYPE_NONE; break;
322 default: in_type[0] = PSE_PAD_TYPE_STANDARD;
324 switch (in_type_sel2) {
325 case 1: in_type[1] = PSE_PAD_TYPE_ANALOGPAD; break;
326 case 2: in_type[1] = PSE_PAD_TYPE_GUNCON; break;
327 case 3: in_type[1] = PSE_PAD_TYPE_GUN; break;
328 case 4: in_type[1] = PSE_PAD_TYPE_NONE; break;
329 default: in_type[1] = PSE_PAD_TYPE_STANDARD;
331 if (in_evdev_allow_abs_only != allow_abs_only_old) {
333 allow_abs_only_old = in_evdev_allow_abs_only;
336 spu_config.iVolume = 768 + 128 * volume_boost;
337 pl_rearmed_cbs.frameskip = frameskip - 1;
338 pl_timing_prepare(Config.PsxType);
341 static void menu_set_defconfig(void)
343 emu_set_default_config();
346 g_scaler = SCALE_4_3;
349 frameskip = 1; // 1 - off
350 analog_deadzone = 50;
355 plat_target.vout_fullscreen = 0;
356 psx_clock = DEFAULT_PSX_CLOCK;
359 in_type_sel1 = in_type_sel2 = 0;
360 in_evdev_allow_abs_only = 0;
365 #define CE_CONFIG_STR(val) \
366 { #val, 0, Config.val }
368 #define CE_CONFIG_VAL(val) \
369 { #val, sizeof(Config.val), &Config.val }
371 #define CE_STR(val) \
374 #define CE_INTVAL(val) \
375 { #val, sizeof(val), &val }
377 #define CE_INTVAL_N(name, val) \
378 { name, sizeof(val), &val }
380 #define CE_INTVAL_P(val) \
381 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
383 // 'versioned' var, used when defaults change
384 #define CE_CONFIG_STR_V(val, ver) \
385 { #val #ver, 0, Config.val }
387 #define CE_INTVAL_V(val, ver) \
388 { #val #ver, sizeof(val), &val }
390 #define CE_INTVAL_PV(val, ver) \
391 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
393 static const struct {
399 CE_CONFIG_STR_V(Gpu, 3),
401 // CE_CONFIG_STR(Cdr),
405 CE_CONFIG_VAL(Debug),
406 CE_CONFIG_VAL(PsxOut),
407 CE_CONFIG_VAL(icache_emulation),
408 CE_CONFIG_VAL(DisableStalls),
410 CE_CONFIG_VAL(GpuListWalking),
411 CE_CONFIG_VAL(FractionalFramerate),
412 CE_CONFIG_VAL(PreciseExceptions),
413 CE_CONFIG_VAL(TurboCD),
415 CE_INTVAL_V(g_scaler, 3),
417 CE_INTVAL(g_layer_x),
418 CE_INTVAL(g_layer_y),
419 CE_INTVAL(g_layer_w),
420 CE_INTVAL(g_layer_h),
421 CE_INTVAL(soft_filter),
422 CE_INTVAL(scanlines),
423 CE_INTVAL(scanline_level),
424 CE_INTVAL(plat_target.vout_method),
425 CE_INTVAL(plat_target.hwfilter),
426 CE_INTVAL(plat_target.vout_fullscreen),
427 CE_INTVAL(state_slot),
428 CE_INTVAL(cpu_clock),
430 CE_INTVAL(in_type_sel1),
431 CE_INTVAL(in_type_sel2),
432 CE_INTVAL(analog_deadzone),
433 CE_INTVAL(memcard1_sel),
434 CE_INTVAL(memcard2_sel),
435 CE_INTVAL(g_autostateld_opt),
436 CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
437 CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
438 CE_INTVAL_V(frameskip, 4),
439 CE_INTVAL_P(gpu_peops.iUseDither),
440 CE_INTVAL_P(gpu_peops.dwActFixes),
441 CE_INTVAL_P(gpu_unai_old.lineskip),
442 CE_INTVAL_P(gpu_unai_old.abe_hack),
443 CE_INTVAL_P(gpu_unai_old.no_light),
444 CE_INTVAL_P(gpu_unai_old.no_blend),
445 CE_INTVAL_P(gpu_unai.ilace_force),
446 CE_INTVAL_P(gpu_unai.pixel_skip),
447 CE_INTVAL_P(gpu_unai.lighting),
448 CE_INTVAL_P(gpu_unai.fast_lighting),
449 CE_INTVAL_P(gpu_unai.blending),
450 CE_INTVAL_P(gpu_unai.dithering),
451 CE_INTVAL_P(gpu_unai.scale_hires),
452 CE_INTVAL_P(gpu_neon.allow_interlace),
453 CE_INTVAL_P(gpu_neon.enhancement_enable),
454 CE_INTVAL_P(gpu_neon.enhancement_no_main),
455 CE_INTVAL_P(gpu_neon.enhancement_tex_adj),
456 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
457 CE_INTVAL_P(gpu_peopsgl.iFilterType),
458 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
459 CE_INTVAL_P(gpu_peopsgl.iUseMask),
460 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
461 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
462 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
463 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
464 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
465 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
466 CE_INTVAL_P(screen_centering_type),
467 CE_INTVAL_P(screen_centering_x),
468 CE_INTVAL_P(screen_centering_y),
469 CE_INTVAL(spu_config.iUseReverb),
470 CE_INTVAL(spu_config.iXAPitch),
471 CE_INTVAL(spu_config.iUseInterpolation),
472 CE_INTVAL(spu_config.iTempo),
473 CE_INTVAL(spu_config.iUseThread),
474 CE_INTVAL(config_save_counter),
475 CE_INTVAL(in_evdev_allow_abs_only),
476 CE_INTVAL(volume_boost),
477 CE_INTVAL(psx_clock),
478 CE_INTVAL(new_dynarec_hacks),
479 CE_INTVAL(in_enable_vibration),
482 static char *get_cd_label(void)
484 static char trimlabel[33];
487 strncpy(trimlabel, CdromLabel, 32);
489 for (j = 31; j >= 0; j--)
490 if (trimlabel[j] == ' ')
496 static void make_cfg_fname(char *buf, size_t size, int is_game)
499 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
501 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
504 static void keys_write_all(FILE *f);
505 static char *mystrip(char *str);
507 static void write_u32_value(FILE *f, u32 v)
511 fprintf(f, "%x\n", v);
514 static int menu_write_config(int is_game)
516 char cfgfile[MAXPATHLEN];
520 config_save_counter++;
522 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
523 f = fopen(cfgfile, "w");
525 printf("menu_write_config: failed to open: %s\n", cfgfile);
529 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
530 fprintf(f, "%s = ", config_data[i].name);
531 switch (config_data[i].len) {
533 fprintf(f, "%s\n", (char *)config_data[i].val);
536 write_u32_value(f, *(u8 *)config_data[i].val);
539 write_u32_value(f, *(u16 *)config_data[i].val);
542 write_u32_value(f, *(u32 *)config_data[i].val);
545 printf("menu_write_config: unhandled len %d for %s\n",
546 (int)config_data[i].len, config_data[i].name);
557 static int menu_do_last_cd_img(int is_get)
559 static const char *defaults[] = { "/media", "/mnt/sd", "/mnt" };
565 snprintf(path, sizeof(path), "." PCSX_DOT_DIR "lastcdimg.txt");
566 f = fopen(path, is_get ? "r" : "w");
573 ret = fread(last_selected_fname, 1, sizeof(last_selected_fname) - 1, f);
574 last_selected_fname[ret] = 0;
575 mystrip(last_selected_fname);
578 fprintf(f, "%s\n", last_selected_fname);
583 for (i = 0; last_selected_fname[0] == 0
584 || STAT(last_selected_fname, &st) != 0; i++)
586 if (i >= ARRAY_SIZE(defaults))
588 strcpy(last_selected_fname, defaults[i]);
595 static void parse_str_val(char *cval, const char *src)
598 strncpy(cval, src, MAXPATHLEN);
599 cval[MAXPATHLEN - 1] = 0;
600 tmp = strchr(cval, '\n');
602 tmp = strchr(cval, '\r');
607 static void keys_load_all(const char *cfg);
609 int menu_load_config(int is_game)
611 char cfgfile[MAXPATHLEN];
617 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
618 f = fopen(cfgfile, "r");
620 printf("menu_load_config: failed to open: %s\n", cfgfile);
624 fseek(f, 0, SEEK_END);
627 printf("bad size %ld: %s\n", size, cfgfile);
631 cfg = malloc(size + 1);
635 fseek(f, 0, SEEK_SET);
636 if (fread(cfg, 1, size, f) != size) {
637 printf("failed to read: %s\n", cfgfile);
642 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
646 tmp = strstr(cfg, config_data[i].name);
649 tmp += strlen(config_data[i].name);
650 if (strncmp(tmp, " = ", 3) != 0)
654 if (config_data[i].len == 0) {
655 parse_str_val(config_data[i].val, tmp);
660 val = strtoul(tmp, &tmp2, 16);
661 if (tmp2 == NULL || tmp == tmp2)
662 continue; // parse failed
664 switch (config_data[i].len) {
666 *(u8 *)config_data[i].val = val;
669 *(u16 *)config_data[i].val = val;
672 *(u32 *)config_data[i].val = val;
675 printf("menu_load_config: unhandled len %d for %s\n",
676 (int)config_data[i].len, config_data[i].name);
682 char *tmp = strstr(cfg, "lastcdimg = ");
685 parse_str_val(last_selected_fname, tmp);
700 for (i = bios_sel = 0; bioses[i] != NULL; i++)
701 if (strcmp(Config.Bios, bioses[i]) == 0)
702 { bios_sel = i; break; }
704 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
705 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
706 { gpu_plugsel = i; break; }
708 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
709 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
710 { spu_plugsel = i; break; }
712 // memcard selections
713 char mcd1_old[sizeof(Config.Mcd1)];
714 char mcd2_old[sizeof(Config.Mcd2)];
715 strcpy(mcd1_old, Config.Mcd1);
716 strcpy(mcd2_old, Config.Mcd2);
718 if ((unsigned int)memcard1_sel < ARRAY_SIZE(memcards)) {
719 if (memcard1_sel == 0)
720 strcpy(Config.Mcd1, "none");
721 else if (memcards[memcard1_sel] != NULL)
722 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s",
723 MEMCARD_DIR, memcards[memcard1_sel]);
725 if ((unsigned int)memcard2_sel < ARRAY_SIZE(memcards)) {
726 if (memcard2_sel == 0)
727 strcpy(Config.Mcd2, "none");
728 else if (memcards[memcard2_sel] != NULL)
729 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s",
730 MEMCARD_DIR, memcards[memcard2_sel]);
732 if (strcmp(mcd1_old, Config.Mcd1) || strcmp(mcd2_old, Config.Mcd2))
733 LoadMcds(Config.Mcd1, Config.Mcd2);
738 static const char *filter_exts[] = {
739 "bin", "img", "mdf", "iso", "cue", "z",
743 "bz", "znx", "pbp", "cbn", NULL
746 // rrrr rggg gggb bbbb
747 static unsigned short fname2color(const char *fname)
749 static const char *other_exts[] = {
750 "ccd", "toc", "mds", "sub", "table", "index", "sbi"
752 const char *ext = strrchr(fname, '.');
758 for (i = 0; filter_exts[i] != NULL; i++)
759 if (strcasecmp(ext, filter_exts[i]) == 0)
761 for (i = 0; i < array_size(other_exts); i++)
762 if (strcasecmp(ext, other_exts[i]) == 0)
767 static void draw_savestate_bg(int slot);
769 #define MENU_ALIGN_LEFT
770 #ifndef HAVE_PRE_ARMV7 // assume hires device
776 #include "libpicofe/menu.c"
778 // a bit of black magic here
779 static void draw_savestate_bg(int slot)
781 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
783 char fname[MAXPATHLEN];
790 ret = get_state_filename(fname, sizeof(fname), slot);
794 f = gzopen(fname, "rb");
798 if ((ret = (int)gzseek(f, 0x29933d, SEEK_SET)) != 0x29933d) {
799 fprintf(stderr, "gzseek failed: %d\n", ret);
804 gpu = malloc(sizeof(*gpu));
810 ret = gzread(f, gpu, sizeof(*gpu));
812 if (ret != sizeof(*gpu)) {
813 fprintf(stderr, "gzread failed\n");
817 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
819 if (gpu->ulStatus & 0x800000)
820 goto out; // disabled
822 x = gpu->ulControl[5] & 0x3ff;
823 y = (gpu->ulControl[5] >> 10) & 0x1ff;
824 w = psx_widths[(gpu->ulStatus >> 16) & 7];
825 tmp = gpu->ulControl[7];
826 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
827 if (gpu->ulStatus & 0x80000) // doubleheight
829 if (h <= 0 || h > 512)
835 s = (u16 *)gpu->psxVRam + y * 1024 + x;
837 x = max(0, g_menuscreen_w - w) & ~3;
838 y = max(0, g_menuscreen_h / 2 - h / 2);
839 w = min(g_menuscreen_w, w);
840 h = min(g_menuscreen_h, h);
841 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
843 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
844 if (gpu->ulStatus & 0x200000)
845 bgr888_to_rgb565(d, s, w * 3);
847 bgr555_to_rgb565(d, s, w * 2);
849 // darken this so that menu text is visible
850 if (g_menuscreen_w - w < 320)
851 menu_darken_bg(d, d, w, 0);
858 // -------------- key config --------------
860 me_bind_action me_ctrl_actions[] =
862 { "UP ", 1 << DKEY_UP},
863 { "DOWN ", 1 << DKEY_DOWN },
864 { "LEFT ", 1 << DKEY_LEFT },
865 { "RIGHT ", 1 << DKEY_RIGHT },
866 { "TRIANGLE", 1 << DKEY_TRIANGLE },
867 { "CIRCLE ", 1 << DKEY_CIRCLE },
868 { "CROSS ", 1 << DKEY_CROSS },
869 { "SQUARE ", 1 << DKEY_SQUARE },
870 { "L1 ", 1 << DKEY_L1 },
871 { "R1 ", 1 << DKEY_R1 },
872 { "L2 ", 1 << DKEY_L2 },
873 { "R2 ", 1 << DKEY_R2 },
874 { "L3 ", 1 << DKEY_L3 },
875 { "R3 ", 1 << DKEY_R3 },
876 { "START ", 1 << DKEY_START },
877 { "SELECT ", 1 << DKEY_SELECT },
881 me_bind_action emuctrl_actions[] =
883 { "Save State ", 1 << SACTION_SAVE_STATE },
884 { "Load State ", 1 << SACTION_LOAD_STATE },
885 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
886 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
887 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
888 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
889 { "Show/Hide FPS ", 1 << SACTION_TOGGLE_FPS },
890 #ifndef HAVE_PRE_ARMV7
891 { "Switch Renderer ", 1 << SACTION_SWITCH_DISPMODE },
893 { "Fast Forward ", 1 << SACTION_FAST_FORWARD },
894 #if MENU_SHOW_MINIMIZE
895 { "Minimize ", 1 << SACTION_MINIMIZE },
897 #if MENU_SHOW_FULLSCREEN
898 { "Toggle fullscreen", 1 << SACTION_TOGGLE_FULLSCREEN },
900 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
901 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
902 { "Gun A button ", 1 << SACTION_GUN_A },
903 { "Gun B button ", 1 << SACTION_GUN_B },
904 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
906 { "Volume Up ", 1 << SACTION_VOLUME_UP },
907 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
909 { "Analog toggle ", 1 << SACTION_ANALOG_TOGGLE },
913 static char *mystrip(char *str)
918 for (i = 0; i < len; i++)
919 if (str[i] != ' ') break;
920 if (i > 0) memmove(str, str + i, len - i + 1);
923 for (i = len - 1; i >= 0; i--)
924 if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
930 static void get_line(char *d, size_t size, const char *s)
935 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
944 static void keys_write_all(FILE *f)
948 for (d = 0; d < IN_MAX_DEVS; d++)
950 const int *binds = in_get_dev_binds(d);
951 const char *name = in_get_dev_name(d, 0, 0);
954 if (binds == NULL || name == NULL)
957 fprintf(f, "binddev = %s\n", name);
958 in_get_config(d, IN_CFG_BIND_COUNT, &count);
960 for (k = 0; k < count; k++)
965 act[0] = act[31] = 0;
966 name = in_get_key_name(d, k);
968 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
969 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
970 mask = me_ctrl_actions[i].mask;
972 strncpy(act, me_ctrl_actions[i].name, 31);
973 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
976 mask = me_ctrl_actions[i].mask << 16;
978 strncpy(act, me_ctrl_actions[i].name, 31);
979 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
984 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
985 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
986 mask = emuctrl_actions[i].mask;
988 strncpy(act, emuctrl_actions[i].name, 31);
989 fprintf(f, "bind %s = %s\n", name, mystrip(act));
995 for (k = 0; k < array_size(in_adev); k++)
998 fprintf(f, "bind_analog = %d\n", k);
1003 static int parse_bind_val(const char *val, int *type)
1007 *type = IN_BINDTYPE_NONE;
1011 if (strncasecmp(val, "player", 6) == 0)
1013 int player, shift = 0;
1014 player = atoi(val + 6) - 1;
1016 if ((unsigned int)player > 1)
1021 *type = IN_BINDTYPE_PLAYER12;
1022 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
1023 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
1024 return me_ctrl_actions[i].mask << shift;
1027 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
1028 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
1029 *type = IN_BINDTYPE_EMU;
1030 return emuctrl_actions[i].mask;
1037 static void keys_load_all(const char *cfg)
1039 char dev[256], key[128], *act;
1045 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
1048 // don't strip 'dev' because there are weird devices
1049 // with names with space at the end
1050 get_line(dev, sizeof(dev), p);
1052 dev_id = in_config_parse_dev(dev);
1054 printf("input: can't handle dev: %s\n", dev);
1058 in_unbind_all(dev_id, -1, -1);
1059 while ((p = strstr(p, "bind"))) {
1060 if (strncmp(p, "binddev = ", 10) == 0)
1063 if (strncmp(p, "bind_analog", 11) == 0) {
1064 ret = sscanf(p, "bind_analog = %d", &bind);
1067 printf("input: parse error: %16s..\n", p);
1070 if ((unsigned int)bind >= array_size(in_adev)) {
1071 printf("input: analog id %d out of range\n", bind);
1074 in_adev[bind] = dev_id;
1080 printf("input: parse error: %16s..\n", p);
1084 get_line(key, sizeof(key), p);
1085 act = strchr(key, '=');
1087 printf("parse failed: %16s..\n", p);
1095 bind = parse_bind_val(act, &bindtype);
1096 if (bind != -1 && bind != 0) {
1097 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
1098 in_config_bind_key(dev_id, key, bind, bindtype);
1101 lprintf("config: unhandled action \"%s\"\n", act);
1107 static int key_config_loop_wrap(int id, int keys)
1110 case MA_CTRL_PLAYER1:
1111 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
1113 case MA_CTRL_PLAYER2:
1114 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
1117 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
1125 static const char h_nubmode[] = "Maps nub-like analog controls to PSX ones better\n"
1126 "Might cause problems with real analog sticks";
1127 static const char *adevnames[IN_MAX_DEVS + 2];
1128 static int stick_sel[2];
1130 static menu_entry e_menu_keyconfig_analog[] =
1132 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
1133 mee_range (" X axis", 0, in_adev_axis[0][0], 0, 7),
1134 mee_range (" Y axis", 0, in_adev_axis[0][1], 0, 7),
1135 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[0], 1, h_nubmode),
1136 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
1137 mee_range (" X axis", 0, in_adev_axis[1][0], 0, 7),
1138 mee_range (" Y axis", 0, in_adev_axis[1][1], 0, 7),
1139 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[1], 1, h_nubmode),
1143 static int key_config_analog(int id, int keys)
1145 int i, d, count, sel = 0;
1146 int sel2dev_map[IN_MAX_DEVS];
1148 memset(adevnames, 0, sizeof(adevnames));
1149 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1150 memset(stick_sel, 0, sizeof(stick_sel));
1152 adevnames[0] = "None";
1154 for (d = 0; d < IN_MAX_DEVS; d++)
1156 const char *name = in_get_dev_name(d, 0, 1);
1161 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1165 if (in_adev[0] == d) stick_sel[0] = i;
1166 if (in_adev[1] == d) stick_sel[1] = i;
1168 adevnames[i++] = name;
1170 adevnames[i] = NULL;
1172 me_loop(e_menu_keyconfig_analog, &sel);
1174 in_adev[0] = sel2dev_map[stick_sel[0]];
1175 in_adev[1] = sel2dev_map[stick_sel[1]];
1180 static const char *mgn_dev_name(int id, int *offs)
1182 const char *name = NULL;
1185 if (id == MA_CTRL_DEV_FIRST)
1188 for (; it < IN_MAX_DEVS; it++) {
1189 name = in_get_dev_name(it, 1, 1);
1198 static const char *mgn_saveloadcfg(int id, int *offs)
1203 static int mh_savecfg(int id, int keys)
1205 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1206 menu_update_msg("config saved");
1208 menu_update_msg("failed to write config");
1213 static int mh_input_rescan(int id, int keys)
1215 //menu_sync_config();
1217 menu_update_msg("rescan complete.");
1222 static const char *men_in_type_sel[] = {
1223 "Standard (SCPH-1080)",
1224 "Analog (SCPH-1150)",
1230 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1231 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1232 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1234 static menu_entry e_menu_keyconfig[] =
1236 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1237 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1238 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
1239 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
1241 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1242 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
1243 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
1244 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
1245 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
1246 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1247 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1248 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1249 mee_handler ("Rescan devices:", mh_input_rescan),
1251 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
1252 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1253 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1254 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1255 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1256 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1257 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1261 static int menu_loop_keyconfig(int id, int keys)
1265 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1266 me_loop(e_menu_keyconfig, &sel);
1270 // ------------ gfx options menu ------------
1272 static const char *men_scaler[] = {
1273 "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL
1275 static const char *men_soft_filter[] = { "None",
1277 "scale2x", "eagle2x",
1280 static const char *men_dummy[] = { NULL };
1281 static const char *men_centering[] = { "Auto", "Ingame", "Borderless", "Force", NULL };
1282 static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
1283 "int. 4:3 - uses integer if possible, else fractional";
1284 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1285 "using d-pad or move it using R+d-pad";
1286 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1287 static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
1289 static const char *men_scanlines[] = { "OFF", "1", "2", "3", NULL };
1290 static const char h_scanline_l[] = "Scanline brightness, 0-100%";
1293 static int menu_loop_cscaler(int id, int keys)
1295 void *saved_layer = NULL;
1296 size_t saved_layer_size = 0;
1297 int was_layer_clipped = 0;
1303 g_scaler = SCALE_CUSTOM;
1304 saved_layer_size = last_vout_w * last_vout_h * last_vout_bpp / 8;
1305 saved_layer = malloc(saved_layer_size);
1307 memcpy(saved_layer, pl_vout_buf, saved_layer_size);
1309 plat_gvideo_open(Config.PsxType);
1311 menu_draw_begin(0, 1);
1312 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1317 if (saved_layer && last_vout_bpp == 16) {
1318 int top_x = max(0, -g_layer_x * last_vout_w / 800) + 1;
1319 int top_y = max(0, -g_layer_y * last_vout_h / 480) + 1;
1321 memcpy(pl_vout_buf, saved_layer, saved_layer_size);
1322 snprintf(text, sizeof(text), "%d,%d %dx%d",
1323 g_layer_x, g_layer_y, g_layer_w, g_layer_h);
1324 basic_text_out16_nf(pl_vout_buf, last_vout_w,
1325 top_x, top_y, text);
1326 basic_text_out16_nf(pl_vout_buf, last_vout_w, 2,
1327 last_vout_h - 20, "d-pad: resize, R+d-pad: move");
1328 pl_vout_buf = plat_gvideo_flip();
1331 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1332 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1333 if (inp & PBTN_UP) g_layer_y--;
1334 if (inp & PBTN_DOWN) g_layer_y++;
1335 if (inp & PBTN_LEFT) g_layer_x--;
1336 if (inp & PBTN_RIGHT) g_layer_x++;
1337 if (!(inp & PBTN_R)) {
1338 if (inp & PBTN_UP) g_layer_h += 2;
1339 if (inp & PBTN_DOWN) g_layer_h -= 2;
1340 if (inp & PBTN_LEFT) g_layer_w += 2;
1341 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1343 if (inp & (PBTN_MOK|PBTN_MBACK))
1346 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1347 int layer_clipped = 0;
1348 g_layer_x = max(-320, min(g_layer_x, 640));
1349 g_layer_y = max(-240, min(g_layer_y, 400));
1350 g_layer_w = max(160, g_layer_w);
1351 g_layer_h = max( 60, g_layer_h);
1352 if (g_layer_x < 0 || g_layer_x + g_layer_w > 800)
1354 if (g_layer_w > 800+400)
1355 g_layer_w = 800+400;
1356 if (g_layer_y < 0 || g_layer_y + g_layer_h > 480)
1358 if (g_layer_h > 480+360)
1359 g_layer_h = 480+360;
1361 plat_gvideo_open(Config.PsxType);
1362 if (layer_clipped || was_layer_clipped)
1363 pl_vout_buf = plat_gvideo_set_mode(&last_vout_w,
1364 &last_vout_h, &last_vout_bpp);
1365 was_layer_clipped = layer_clipped;
1369 plat_gvideo_close();
1375 static menu_entry e_menu_gfx_options[] =
1377 mee_enum ("Screen centering", MA_OPT_CENTERING, pl_rearmed_cbs.screen_centering_type, men_centering),
1378 mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1379 mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1380 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1381 mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1382 mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1384 mee_enum ("Scanlines", MA_OPT_SCANLINES, scanlines, men_scanlines),
1385 mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1387 mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1388 // mee_onoff ("Vsync", 0, vsync, 1),
1389 mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1393 static int menu_loop_gfx_options(int id, int keys)
1397 me_loop(e_menu_gfx_options, &sel);
1402 // ------------ bios/plugins ------------
1404 #ifdef BUILTIN_GPU_NEON
1406 static const char h_gpu_neon[] =
1407 "Configure built-in NEON GPU plugin";
1408 static const char h_gpu_neon_enhanced[] =
1409 "Renders in double resolution at the cost of lower performance\n"
1410 "(not available for high resolution games)";
1411 static const char h_gpu_neon_enhanced_hack[] =
1412 "Speed hack for above option (glitches some games)";
1413 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1415 static menu_entry e_menu_plugin_gpu_neon[] =
1417 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1418 mee_onoff_h ("Enhanced resolution", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1419 mee_onoff_h ("Enhanced res. speed hack", 0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1420 mee_onoff ("Enh. res. texture adjust", 0, pl_rearmed_cbs.gpu_neon.enhancement_tex_adj, 1),
1424 static int menu_loop_plugin_gpu_neon(int id, int keys)
1427 me_loop(e_menu_plugin_gpu_neon, &sel);
1433 static menu_entry e_menu_plugin_gpu_unai_old[] =
1435 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai_old.lineskip, 1),
1436 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai_old.abe_hack, 1),
1437 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai_old.no_light, 1),
1438 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai_old.no_blend, 1),
1442 static int menu_loop_plugin_gpu_unai_old(int id, int keys)
1445 me_loop(e_menu_plugin_gpu_unai_old, &sel);
1449 static menu_entry e_menu_plugin_gpu_unai[] =
1451 mee_onoff ("Interlace", 0, pl_rearmed_cbs.gpu_unai.ilace_force, 1),
1452 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_unai.dithering, 1),
1453 mee_onoff ("Lighting", 0, pl_rearmed_cbs.gpu_unai.lighting, 1),
1454 mee_onoff ("Fast lighting", 0, pl_rearmed_cbs.gpu_unai.fast_lighting, 1),
1455 mee_onoff ("Blending", 0, pl_rearmed_cbs.gpu_unai.blending, 1),
1456 mee_onoff ("Pixel skip", 0, pl_rearmed_cbs.gpu_unai.pixel_skip, 1),
1460 static int menu_loop_plugin_gpu_unai(int id, int keys)
1463 me_loop(e_menu_plugin_gpu_unai, &sel);
1468 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1469 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1470 static const char h_gpu_1[] = "Capcom fighting games";
1471 static const char h_gpu_2[] = "Black screens in Lunar";
1472 static const char h_gpu_3[] = "Compatibility mode";
1473 static const char h_gpu_6[] = "Pandemonium 2";
1474 //static const char h_gpu_7[] = "Skip every second frame";
1475 static const char h_gpu_8[] = "Needed by Dark Forces";
1476 static const char h_gpu_9[] = "better g-colors, worse textures";
1477 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1479 static menu_entry e_menu_plugin_gpu_peops[] =
1481 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1482 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1483 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1484 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1485 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1486 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1487 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1488 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1489 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1490 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1494 static int menu_loop_plugin_gpu_peops(int id, int keys)
1497 me_loop(e_menu_plugin_gpu_peops, &sel);
1501 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1502 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1503 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1505 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1507 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1508 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1509 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1510 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1511 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1512 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1513 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1514 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1515 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1516 mee_label ("Fixes/hacks:"),
1517 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1518 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1519 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1520 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1521 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1522 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1523 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1524 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1525 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1526 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1527 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1531 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1534 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1538 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1539 static const char h_spu_volboost[] = "Large values cause distortion";
1540 static const char h_spu_tempo[] = "Slows down audio if emu is too slow\n"
1541 "This is inaccurate and breaks games";
1543 static menu_entry e_menu_plugin_spu[] =
1545 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1546 mee_onoff ("Reverb", 0, spu_config.iUseReverb, 1),
1547 mee_enum ("Interpolation", 0, spu_config.iUseInterpolation, men_spu_interp),
1548 //mee_onoff ("Adjust XA pitch", 0, spu_config.iXAPitch, 1),
1549 mee_onoff_h ("Adjust tempo", 0, spu_config.iTempo, 1, h_spu_tempo),
1553 static int menu_loop_plugin_spu(int id, int keys)
1556 me_loop(e_menu_plugin_spu, &sel);
1560 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1561 "savestates and can't be changed there. Must save\n"
1562 "config and reload the game for change to take effect";
1563 static const char h_plugin_gpu[] =
1564 #ifdef BUILTIN_GPU_NEON
1565 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1567 "gpu_peops is Pete's soft GPU, slow but accurate\n"
1568 "gpu_unai_old is from old PCSX4ALL, fast but glitchy\n"
1569 "gpu_unai is newer, more accurate but slower\n"
1570 "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1571 "must save config and reload the game if changed";
1572 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1573 "must save config and reload the game if changed";
1574 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1575 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1576 static const char h_gpu_unai_old[] = "Configure Unai/PCSX4ALL Team GPU plugin (old)";
1577 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team plugin (new)";
1578 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1580 static menu_entry e_menu_plugin_options[] =
1582 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1583 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1584 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1585 #ifdef BUILTIN_GPU_NEON
1586 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1588 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1589 mee_handler_h ("Configure gpu_unai_old GPU plugin", menu_loop_plugin_gpu_unai_old, h_gpu_unai_old),
1590 mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1591 mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1592 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1596 static menu_entry e_menu_main2[];
1598 static int menu_loop_plugin_options(int id, int keys)
1601 me_loop(e_menu_plugin_options, &sel);
1603 // sync BIOS/plugins
1604 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1605 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1606 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1607 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1612 // ------------ adv options menu ------------
1615 static const char h_cfg_noch[] = "Disables game-specific compatibility hacks";
1616 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1617 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1618 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1620 static const char h_cfg_stalls[] = "Will cause some games to run too fast";
1622 static menu_entry e_menu_speed_hacks[] =
1625 mee_onoff_h ("Disable compat hacks", 0, new_dynarec_hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1626 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1627 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1628 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1630 mee_onoff_h ("Disable CPU/GTE stalls", 0, menu_iopts[0], 1, h_cfg_stalls),
1634 static int menu_loop_speed_hacks(int id, int keys)
1637 menu_iopts[0] = Config.DisableStalls;
1638 me_loop(e_menu_speed_hacks, &sel);
1639 Config.DisableStalls = menu_iopts[0];
1643 static const char *men_autooo[] = { "Auto", "Off", "On", NULL };
1645 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1646 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1647 "(green: normal, red: fmod, blue: noise)";
1648 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1649 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1650 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1651 "(proper .cue/.bin dump is needed otherwise)";
1653 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1654 "Might be useful to overcome some dynarec bugs";
1656 static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1657 static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1658 static const char h_cfg_exc[] = "Emulate some PSX's debug hw like breakpoints\n"
1659 "and exceptions (slow, interpreter only, keep off)";
1660 static const char h_cfg_gpul[] = "Try enabling this if the game misses some graphics\n"
1661 "causes a performance hit";
1662 static const char h_cfg_ffps[] = "Instead of 50/60fps for PAL/NTSC use ~49.75/59.81\n"
1663 "Closer to real hw but doesn't match modern displays.";
1664 static const char h_cfg_tcd[] = "Greatly reduce CD load times. Breaks some games.";
1665 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1666 "(adjust this if the game is too slow/too fast/hangs)";
1668 enum { AMO_XA, AMO_CDDA, AMO_IC, AMO_BP, AMO_CPU, AMO_GPUL, AMO_FFPS, AMO_TCD };
1670 static menu_entry e_menu_adv_options[] =
1672 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1673 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1674 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1675 mee_onoff_h ("Disable XA Decoding", 0, menu_iopts[AMO_XA], 1, h_cfg_xa),
1676 mee_onoff_h ("Disable CD Audio", 0, menu_iopts[AMO_CDDA], 1, h_cfg_cdda),
1677 mee_onoff_h ("ICache emulation", 0, menu_iopts[AMO_IC], 1, h_cfg_icache),
1678 mee_onoff_h ("BP exception emulation", 0, menu_iopts[AMO_BP], 1, h_cfg_exc),
1679 mee_enum_h ("GPU l-list slow walking",0, menu_iopts[AMO_GPUL], men_autooo, h_cfg_gpul),
1680 mee_enum_h ("Fractional framerate", 0, menu_iopts[AMO_FFPS], men_autooo, h_cfg_ffps),
1681 mee_onoff_h ("Turbo CD-ROM ", 0, menu_iopts[AMO_TCD], 1, h_cfg_tcd),
1682 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
1683 mee_onoff_h ("Disable dynarec (slow!)",0, menu_iopts[AMO_CPU], 1, h_cfg_nodrc),
1685 mee_range_h ("PSX CPU clock, %", 0, psx_clock, 1, 500, h_cfg_psxclk),
1686 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1690 static int menu_loop_adv_options(int id, int keys)
1697 { &Config.Xa, &menu_iopts[AMO_XA] },
1698 { &Config.Cdda, &menu_iopts[AMO_CDDA] },
1699 { &Config.icache_emulation, &menu_iopts[AMO_IC] },
1700 { &Config.PreciseExceptions, &menu_iopts[AMO_BP] },
1701 { &Config.Cpu, &menu_iopts[AMO_CPU] },
1702 { &Config.TurboCD, &menu_iopts[AMO_TCD] },
1705 for (i = 0; i < ARRAY_SIZE(opts); i++)
1706 *opts[i].mopt = *opts[i].opt;
1707 menu_iopts[AMO_GPUL] = Config.GpuListWalking + 1;
1708 menu_iopts[AMO_FFPS] = Config.FractionalFramerate + 1;
1710 me_loop(e_menu_adv_options, &sel);
1712 for (i = 0; i < ARRAY_SIZE(opts); i++)
1713 *opts[i].opt = *opts[i].mopt;
1714 Config.GpuListWalking = menu_iopts[AMO_GPUL] - 1;
1715 Config.FractionalFramerate = menu_iopts[AMO_FFPS] - 1;
1720 // ------------ options menu ------------
1722 static int mh_restore_defaults(int id, int keys)
1724 menu_set_defconfig();
1725 menu_update_msg("defaults restored");
1729 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1730 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1732 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1733 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1734 "loading state or both";
1736 static const char h_restore_def[] = "Switches back to default / recommended\n"
1738 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1739 static const char h_sputhr[] = "Warning: has some known bugs\n";
1741 static menu_entry e_menu_options[] =
1743 // mee_range ("Save slot", 0, state_slot, 0, 9),
1744 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1745 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1746 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1747 mee_enum ("Region", 0, region, men_region),
1748 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1750 mee_onoff_h ("Use C64x DSP for sound", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1752 mee_onoff_h ("Threaded SPU", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1, h_sputhr),
1754 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1755 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1756 mee_handler ("[Advanced]", menu_loop_adv_options),
1757 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1758 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1759 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1763 static int menu_loop_options(int id, int keys)
1767 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1768 me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1769 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1771 me_loop(e_menu_options, &sel);
1776 // ------------ debug menu ------------
1778 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1780 int w = min(g_menuscreen_w, 1024);
1781 int h = min(g_menuscreen_h, 512);
1782 u16 *d = g_menuscreen_ptr;
1783 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1787 gpuf->ulFreezeVersion = 1;
1788 if (GPU_freeze != NULL)
1789 GPU_freeze(1, gpuf);
1791 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1792 bgr555_to_rgb565(d, s, w * 2);
1794 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1795 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1796 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1797 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1798 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1801 static void debug_menu_loop(void)
1803 int inp, df_x = 0, df_y = 0;
1806 gpuf = malloc(sizeof(*gpuf));
1812 menu_draw_begin(0, 1);
1813 draw_frame_debug(gpuf, df_x, df_y);
1816 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1817 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1818 if (inp & PBTN_MBACK) break;
1819 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1820 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1821 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x -= 2; }
1822 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1828 // --------- memcard manager ---------
1830 static void draw_mc_icon(int dx, int dy, const u16 *s)
1835 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1837 for (y = 0; y < 16; y++, s += 16) {
1838 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1839 for (x = 0; x < 16; x++) {
1841 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1842 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1848 static void draw_mc_bg(void)
1850 McdBlock *blocks1, *blocks2;
1854 blocks1 = malloc(15 * sizeof(blocks1[0]));
1855 blocks2 = malloc(15 * sizeof(blocks1[0]));
1856 if (blocks1 == NULL || blocks2 == NULL)
1859 for (i = 0; i < 15; i++) {
1860 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1861 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1864 menu_draw_begin(1, 1);
1866 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1868 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1872 maxicons = g_menuscreen_h / 32;
1875 row2 = g_menuscreen_w / 2;
1876 for (i = 0; i < maxicons; i++) {
1877 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1878 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1880 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1881 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1884 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1892 static void handle_memcard_sel(void)
1894 strcpy(Config.Mcd1, "none");
1895 if (memcard1_sel != 0)
1896 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1897 strcpy(Config.Mcd2, "none");
1898 if (memcard2_sel != 0)
1899 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1900 LoadMcds(Config.Mcd1, Config.Mcd2);
1904 static menu_entry e_memcard_options[] =
1906 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1907 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1911 static int menu_loop_memcards(int id, int keys)
1917 memcard1_sel = memcard2_sel = 0;
1918 p = strrchr(Config.Mcd1, '/');
1920 for (i = 0; memcards[i] != NULL; i++)
1921 if (strcmp(p + 1, memcards[i]) == 0)
1922 { memcard1_sel = i; break; }
1923 p = strrchr(Config.Mcd2, '/');
1925 for (i = 0; memcards[i] != NULL; i++)
1926 if (strcmp(p + 1, memcards[i]) == 0)
1927 { memcard2_sel = i; break; }
1929 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1931 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1936 // ------------ cheats menu ------------
1938 static void draw_cheatlist(int sel)
1940 int max_cnt, start, i, pos, active;
1942 max_cnt = g_menuscreen_h / me_sfont_h;
1943 start = max_cnt / 2 - sel;
1945 menu_draw_begin(1, 1);
1947 for (i = 0; i < NumCheats; i++) {
1949 if (pos < 0) continue;
1950 if (pos >= max_cnt) break;
1951 active = Cheats[i].Enabled;
1952 smalltext_out16(14, pos * me_sfont_h,
1953 active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1954 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1955 Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1959 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1961 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1965 static void menu_loop_cheats(void)
1967 static int menu_sel = 0;
1972 draw_cheatlist(menu_sel);
1973 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1974 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1975 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1976 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1977 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1978 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1979 if (inp & PBTN_MOK) { // action
1980 if (menu_sel < NumCheats)
1981 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1984 if (inp & PBTN_MBACK)
1989 // --------- main menu help ----------
1991 static void menu_bios_warn(void)
1994 static const char msg[] =
1995 "You don't seem to have copied any BIOS\n"
1997 MENU_BIOS_PATH "\n\n"
1999 "While many games work fine with fake\n"
2000 "(HLE) BIOS, others (like MGS and FF8)\n"
2001 "require BIOS to work.\n"
2002 "After copying the file, you'll also need\n"
2003 "to select it in the emu's menu:\n"
2004 "options->[BIOS/Plugins]\n\n"
2005 "The file is usually named SCPH1001.BIN,\n"
2006 "but other not compressed files can be\n"
2008 "Press %s or %s to continue";
2009 char tmp_msg[sizeof(msg) + 64];
2011 snprintf(tmp_msg, sizeof(tmp_msg), msg,
2012 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
2015 draw_menu_message(tmp_msg, NULL);
2017 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2018 if (inp & (PBTN_MBACK|PBTN_MOK))
2023 // ------------ main menu ------------
2025 static menu_entry e_menu_main[];
2027 static void draw_frame_main(void)
2036 if (CdromId[0] != 0) {
2037 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
2038 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
2039 Config.HLE ? "HLE" : "BIOS");
2040 smalltext_out16(4, 1, buff, 0x105f);
2044 capacity = plat_target_bat_capacity_get();
2046 tmp = localtime(<ime);
2047 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
2048 if (capacity >= 0) {
2049 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
2054 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
2058 static void draw_frame_credits(void)
2060 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
2063 static const char credits_text[] =
2065 "(C) 1999-2003 PCSX Team\n"
2066 "(C) 2005-2009 PCSX-df Team\n"
2067 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
2068 "ARM recompiler (C) 2009-2011 Ari64\n"
2069 #ifdef BUILTIN_GPU_NEON
2070 "ARM NEON GPU (c) 2011-2012 Exophase\n"
2072 "PEOpS GPU and SPU by Pete Bernert\n"
2073 " and the P.E.Op.S. team\n"
2074 "PCSX4ALL plugin by PCSX4ALL team\n"
2075 " Chui, Franxis, Unai\n\n"
2076 "integration, optimization and\n"
2077 " frontend (C) 2010-2015 notaz\n";
2079 static int reset_game(void)
2085 if (LoadCdrom() == -1)
2091 static int reload_plugins(const char *cdimg)
2097 set_cd_image(cdimg);
2099 pcnt_hook_plugins();
2101 if (OpenPlugins() == -1) {
2102 menu_update_msg("failed to open plugins");
2105 plugin_call_rearmed_cbs();
2107 cdrIsoMultidiskCount = 1;
2109 CdromLabel[0] = '\0';
2114 static int run_bios(void)
2116 boolean origSlowBoot = Config.SlowBoot;
2122 if (reload_plugins(NULL) != 0)
2124 Config.SlowBoot = 1;
2126 Config.SlowBoot = origSlowBoot;
2132 static int run_exe(void)
2134 const char *exts[] = { "exe", NULL };
2137 fname = menu_loop_romsel(last_selected_fname,
2138 sizeof(last_selected_fname), exts, NULL);
2143 if (reload_plugins(NULL) != 0)
2147 if (Load(fname) != 0) {
2148 menu_update_msg("exe load failed, bad file?");
2157 static int run_cd_image(const char *fname)
2159 int autoload_state = g_autostateld_opt;
2162 reload_plugins(fname);
2164 // always autodetect, menu_sync_config will override as needed
2167 if (CheckCdrom() == -1) {
2168 // Only check the CD if we are starting the console with a CD
2170 menu_update_msg("unsupported/invalid CD image");
2176 // Read main executable directly from CDRom and start it
2177 if (LoadCdrom() == -1) {
2179 menu_update_msg("failed to load CD image");
2186 if (autoload_state) {
2187 unsigned int newest = 0;
2188 int time, slot, newest_slot = -1;
2190 for (slot = 0; slot < 10; slot++) {
2191 if (emu_check_save_file(slot, &time)) {
2192 if ((unsigned int)time > newest) {
2199 if (newest_slot >= 0) {
2200 lprintf("autoload slot %d\n", newest_slot);
2201 emu_load_state(newest_slot);
2204 lprintf("no save to autoload.\n");
2211 static int romsel_run(void)
2213 int prev_gpu, prev_spu;
2216 fname = menu_loop_romsel(last_selected_fname,
2217 sizeof(last_selected_fname), filter_exts,
2218 optional_cdimg_filter);
2222 printf("selected file: %s\n", fname);
2224 new_dynarec_clear_full();
2226 if (run_cd_image(fname) != 0)
2229 prev_gpu = gpu_plugsel;
2230 prev_spu = spu_plugsel;
2231 if (menu_load_config(1) != 0)
2232 menu_load_config(0);
2234 // check for plugin changes, have to repeat
2235 // loading if game config changed plugins to reload them
2236 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2237 printf("plugin change detected, reloading plugins..\n");
2238 if (run_cd_image(fname) != 0)
2242 strcpy(last_selected_fname, fname);
2243 menu_do_last_cd_img(0);
2247 static int swap_cd_image(void)
2251 fname = menu_loop_romsel(last_selected_fname,
2252 sizeof(last_selected_fname), filter_exts,
2253 optional_cdimg_filter);
2257 printf("selected file: %s\n", fname);
2260 CdromLabel[0] = '\0';
2262 set_cd_image(fname);
2263 if (ReloadCdromPlugin() < 0) {
2264 menu_update_msg("failed to load cdr plugin");
2267 if (CDR_open() < 0) {
2268 menu_update_msg("failed to open cdr plugin");
2272 SetCdOpenCaseTime(time(NULL) + 2);
2275 strcpy(last_selected_fname, fname);
2279 static int swap_cd_multidisk(void)
2281 cdrIsoMultidiskSelect++;
2283 CdromLabel[0] = '\0';
2286 if (CDR_open() < 0) {
2287 menu_update_msg("failed to open cdr plugin");
2291 SetCdOpenCaseTime(time(NULL) + 2);
2297 static void load_pcsx_cht(void)
2299 static const char *exts[] = { "cht", NULL };
2303 fname = menu_loop_romsel(last_selected_fname,
2304 sizeof(last_selected_fname), exts, NULL);
2308 printf("selected cheat file: %s\n", fname);
2311 if (NumCheats == 0 && NumCodes == 0)
2312 menu_update_msg("failed to load cheats");
2314 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2315 menu_update_msg(msg);
2317 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2320 static int main_menu_handler(int id, int keys)
2324 case MA_MAIN_RESUME_GAME:
2328 case MA_MAIN_SAVE_STATE:
2330 return menu_loop_savestate(0);
2332 case MA_MAIN_LOAD_STATE:
2334 return menu_loop_savestate(1);
2336 case MA_MAIN_RESET_GAME:
2337 if (ready_to_go && reset_game() == 0)
2340 case MA_MAIN_LOAD_ROM:
2341 if (romsel_run() == 0)
2344 case MA_MAIN_SWAP_CD:
2345 if (swap_cd_image() == 0)
2348 case MA_MAIN_SWAP_CD_MULTI:
2349 if (swap_cd_multidisk() == 0)
2352 case MA_MAIN_RUN_BIOS:
2353 if (run_bios() == 0)
2356 case MA_MAIN_RUN_EXE:
2360 case MA_MAIN_CHEATS:
2363 case MA_MAIN_LOAD_CHEATS:
2366 case MA_MAIN_CREDITS:
2367 draw_menu_message(credits_text, draw_frame_credits);
2368 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2371 emu_core_ask_exit();
2374 lprintf("%s: something unknown selected\n", __FUNCTION__);
2381 static menu_entry e_menu_main2[] =
2383 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2384 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2385 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2386 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2387 mee_handler ("Memcard manager", menu_loop_memcards),
2388 mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS, main_menu_handler),
2392 static int main_menu2_handler(int id, int keys)
2396 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2397 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2398 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2399 me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2401 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2404 static const char h_extra[] = "Change CD, manage memcards..\n";
2406 static menu_entry e_menu_main[] =
2410 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2411 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2412 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2413 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2414 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2415 mee_handler ("Options", menu_loop_options),
2416 mee_handler ("Controls", menu_loop_keyconfig),
2417 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
2418 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2419 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2420 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2424 // ----------------------------
2426 static void menu_leave_emu(void);
2428 void menu_loop(void)
2430 static int warned_about_bios = 0;
2435 if (config_save_counter == 0) {
2437 if (bioses[1] != NULL) {
2438 // autoselect BIOS to make user's life easier
2439 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2442 else if (!warned_about_bios) {
2444 warned_about_bios = 1;
2448 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2449 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2450 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2451 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2452 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2454 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2457 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2458 } while (!ready_to_go && !g_emu_want_quit);
2460 /* wait until menu, ok, back is released */
2461 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2464 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2469 static int qsort_strcmp(const void *p1, const void *p2)
2471 char * const *s1 = (char * const *)p1;
2472 char * const *s2 = (char * const *)p2;
2473 return strcasecmp(*s1, *s2);
2476 static void scan_bios_plugins(void)
2478 char fname[MAXPATHLEN];
2480 int bios_i, gpu_i, spu_i, mc_i;
2485 gpu_plugins[0] = "builtin_gpu";
2486 spu_plugins[0] = "builtin_spu";
2487 memcards[0] = "(none)";
2488 bios_i = gpu_i = spu_i = mc_i = 1;
2490 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2491 dir = opendir(fname);
2493 perror("scan_bios_plugins bios opendir");
2508 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2511 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2512 if (stat(fname, &st) != 0
2513 || (st.st_size != 512*1024 && st.st_size != 4*1024*1024)) {
2514 printf("bad BIOS file: %s\n", ent->d_name);
2518 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2519 bioses[bios_i++] = strdup(ent->d_name);
2523 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2529 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2530 dir = opendir(fname);
2532 perror("scan_bios_plugins plugins opendir");
2546 p = strstr(ent->d_name, ".so");
2550 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2551 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2553 fprintf(stderr, "%s\n", dlerror());
2557 // now what do we have here?
2558 tmp = dlsym(h, "GPUinit");
2561 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2562 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2566 tmp = dlsym(h, "SPUinit");
2569 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2570 spu_plugins[spu_i++] = strdup(ent->d_name);
2574 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2581 dir = opendir("." MEMCARD_DIR);
2583 perror("scan_bios_plugins memcards opendir");
2598 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2601 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2602 if (stat(fname, &st) != 0) {
2603 printf("bad memcard file: %s\n", ent->d_name);
2607 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2608 memcards[mc_i++] = strdup(ent->d_name);
2612 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2616 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2621 void menu_init(void)
2623 char buff[MAXPATHLEN];
2626 cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2628 scan_bios_plugins();
2631 menu_set_defconfig();
2632 menu_load_config(0);
2633 menu_do_last_cd_img(1);
2638 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2639 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2640 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2641 fprintf(stderr, "OOM\n");
2645 emu_make_path(buff, "skin/background.png", sizeof(buff));
2646 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2648 i = plat_target.cpu_clock_set != NULL
2649 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2650 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, i);
2652 i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2653 e_menu_gfx_options[i].data = plat_target.vout_methods;
2654 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2655 plat_target.vout_methods != NULL);
2657 i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2658 e_menu_gfx_options[i].data = plat_target.hwfilters;
2659 me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2660 plat_target.hwfilters != NULL);
2662 me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2663 plat_target.gamma_set != NULL);
2665 #ifdef HAVE_PRE_ARMV7
2666 me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2668 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2669 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2670 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2671 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2672 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2673 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2674 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2677 void menu_notify_mode_change(int w, int h, int bpp)
2681 last_vout_bpp = bpp;
2684 static void menu_leave_emu(void)
2686 if (GPU_close != NULL) {
2687 int ret = GPU_close();
2689 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2692 plat_video_menu_enter(ready_to_go);
2694 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2695 if (pl_vout_buf != NULL && ready_to_go) {
2696 int x = max(0, g_menuscreen_w - last_vout_w);
2697 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2698 int w = min(g_menuscreen_w, last_vout_w);
2699 int h = min(g_menuscreen_h, last_vout_h);
2700 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2701 char *s = pl_vout_buf;
2703 if (last_vout_bpp == 16) {
2704 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2705 menu_darken_bg(d, s, w, 0);
2708 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2709 rgb888_to_rgb565(d, s, w * 3);
2710 menu_darken_bg(d, d, w, 0);
2716 cpu_clock = plat_target_cpu_clock_get();
2719 void menu_prepare_emu(void)
2721 R3000Acpu *prev_cpu = psxCpu;
2723 plat_video_menu_leave();
2725 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
2726 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2730 if (psxCpu != prev_cpu) {
2731 prev_cpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
2732 prev_cpu->Shutdown();
2735 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
2739 psxCpu->ApplyConfig();
2741 // core doesn't care about Config.Cdda changes,
2742 // so handle them manually here
2747 plat_target_cpu_clock_set(cpu_clock);
2749 // push config to GPU plugin
2750 plugin_call_rearmed_cbs();
2752 if (GPU_open != NULL) {
2753 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2755 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2759 void menu_update_msg(const char *msg)
2761 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2762 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2764 menu_error_time = plat_get_ticks_ms();
2765 lprintf("msg: %s\n", menu_error_msg);
2768 void menu_finish(void)
2770 if (cpu_clock_st > 0)
2771 plat_target_cpu_clock_set(cpu_clock_st);