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.
17 #include <sys/types.h>
26 #include "plugin_lib.h"
30 #include "libpicofe/plat.h"
31 #include "libpicofe/input.h"
32 #include "libpicofe/linux/in_evdev.h"
33 #include "libpicofe/plat.h"
34 #include "../libpcsxcore/misc.h"
35 #include "../libpcsxcore/cdrom.h"
36 #include "../libpcsxcore/cdriso.h"
37 #include "../libpcsxcore/cheat.h"
38 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
39 #include "../plugins/dfinput/externals.h"
40 #include "../plugins/dfsound/spu_config.h"
41 #include "psemu_plugin_defs.h"
42 #include "arm_features.h"
45 #define REARMED_BIRTHDAY_TIME 1293306830 /* 25 Dec 2010 */
47 #define array_size(x) (sizeof(x) / sizeof(x[0]))
58 MA_MAIN_SWAP_CD_MULTI,
89 MA_OPT_SCANLINE_LEVEL,
92 static int last_vout_w, last_vout_h, last_vout_bpp;
93 static int cpu_clock, cpu_clock_st, volume_boost, frameskip;
94 static char last_selected_fname[MAXPATHLEN];
95 static int config_save_counter, region, in_type_sel1, in_type_sel2;
97 static int memcard1_sel = -1, memcard2_sel = -1;
98 extern int g_autostateld_opt;
99 int g_opts, g_scaler, g_gamma = 100;
100 int scanlines, scanline_level = 20;
101 int soft_scaling, analog_deadzone; // for Caanoo
104 #ifndef HAVE_PRE_ARMV7
105 #define DEFAULT_PSX_CLOCK 57
106 #define DEFAULT_PSX_CLOCK_S "57"
108 #define DEFAULT_PSX_CLOCK 50
109 #define DEFAULT_PSX_CLOCK_S "50"
112 static const char *bioses[24];
113 static const char *gpu_plugins[16];
114 static const char *spu_plugins[16];
115 static const char *memcards[32];
116 static int bios_sel, gpu_plugsel, spu_plugsel;
118 #ifndef UI_FEATURES_H
119 #define MENU_BIOS_PATH "bios/"
120 #define MENU_SHOW_VARSCALER 0
121 #define MENU_SHOW_VOUTMODE 1
122 #define MENU_SHOW_SCALER2 0
123 #define MENU_SHOW_NUBS_BTNS 0
124 #define MENU_SHOW_VIBRATION 0
125 #define MENU_SHOW_DEADZONE 0
126 #define MENU_SHOW_MINIMIZE 0
127 #define MENU_SHOW_FULLSCREEN 1
128 #define MENU_SHOW_VOLUME 0
131 static int min(int x, int y) { return x < y ? x : y; }
132 static int max(int x, int y) { return x > y ? x : y; }
134 void emu_make_path(char *buff, const char *end, int size)
138 end_len = strlen(end);
139 pos = plat_get_root_dir(buff, size);
140 strncpy(buff + pos, end, size - pos);
142 if (pos + end_len > size - 1)
143 printf("Warning: path truncated: %s\n", buff);
146 static int emu_check_save_file(int slot, int *time)
148 char fname[MAXPATHLEN];
152 ret = emu_check_state(slot);
153 if (ret != 0 || time == NULL)
154 return ret == 0 ? 1 : 0;
156 ret = get_state_filename(fname, sizeof(fname), slot);
160 ret = stat(fname, &status);
164 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
165 return 1; // probably bad rtc like on some Caanoos
167 *time = status.st_mtime;
172 static int emu_save_load_game(int load, int unused)
177 ret = emu_load_state(state_slot);
179 // reflect hle/bios mode from savestate
182 else if (bios_sel == 0 && bioses[1] != NULL)
183 // XXX: maybe find the right bios instead
187 ret = emu_save_state(state_slot);
192 static void rm_namelist_entry(struct dirent **namelist,
193 int count, const char *name)
197 for (i = 1; i < count; i++) {
198 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
201 if (strcmp(name, namelist[i]->d_name) == 0) {
209 static int optional_cdimg_filter(struct dirent **namelist, int count,
213 char buf[256], buf2[256];
214 int i, d, ret, good_cue;
221 for (i = 1; i < count; i++) {
222 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
225 ext = strrchr(namelist[i]->d_name, '.');
227 // should not happen but whatever
234 // first find .cue files and remove files they reference
235 if (strcasecmp(ext, "cue") == 0)
237 snprintf(buf, sizeof(buf), "%s/%s", basedir,
238 namelist[i]->d_name);
248 while (fgets(buf, sizeof(buf), f)) {
249 ret = sscanf(buf, " FILE \"%256[^\"]\"", buf2);
251 ret = sscanf(buf, " FILE %256s", buf2);
255 p = strrchr(buf2, '/');
257 p = strrchr(buf2, '\\');
263 snprintf(buf, sizeof(buf), "%s/%s", basedir, p);
264 ret = stat64(buf, &statf);
266 rm_namelist_entry(namelist, count, p);
279 p = strcasestr(namelist[i]->d_name, "track");
281 ret = strtoul(p + 5, NULL, 10);
291 for (i = d = 1; i < count; i++)
292 if (namelist[i] != NULL)
293 namelist[d++] = namelist[i];
298 // propagate menu settings to the emu vars
299 static void menu_sync_config(void)
301 static int allow_abs_only_old;
306 Config.PsxType = region - 1;
308 cycle_multiplier = 10000 / psx_clock;
310 switch (in_type_sel1) {
311 case 1: in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
312 case 2: in_type1 = PSE_PAD_TYPE_NEGCON; break;
313 default: in_type1 = PSE_PAD_TYPE_STANDARD;
315 switch (in_type_sel2) {
316 case 1: in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
317 case 2: in_type2 = PSE_PAD_TYPE_NEGCON; break;
318 default: in_type2 = PSE_PAD_TYPE_STANDARD;
320 if (in_evdev_allow_abs_only != allow_abs_only_old) {
322 allow_abs_only_old = in_evdev_allow_abs_only;
325 spu_config.iVolume = 768 + 128 * volume_boost;
326 pl_rearmed_cbs.frameskip = frameskip - 1;
327 pl_timing_prepare(Config.PsxType);
330 static void menu_set_defconfig(void)
332 emu_set_default_config();
335 g_scaler = SCALE_4_3;
339 analog_deadzone = 50;
344 plat_target.vout_fullscreen = 0;
345 psx_clock = DEFAULT_PSX_CLOCK;
348 in_type_sel1 = in_type_sel2 = 0;
349 in_evdev_allow_abs_only = 0;
354 #define CE_CONFIG_STR(val) \
355 { #val, 0, Config.val }
357 #define CE_CONFIG_VAL(val) \
358 { #val, sizeof(Config.val), &Config.val }
360 #define CE_STR(val) \
363 #define CE_INTVAL(val) \
364 { #val, sizeof(val), &val }
366 #define CE_INTVAL_N(name, val) \
367 { name, sizeof(val), &val }
369 #define CE_INTVAL_P(val) \
370 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
372 // 'versioned' var, used when defaults change
373 #define CE_CONFIG_STR_V(val, ver) \
374 { #val #ver, 0, Config.val }
376 #define CE_INTVAL_V(val, ver) \
377 { #val #ver, sizeof(val), &val }
379 #define CE_INTVAL_PV(val, ver) \
380 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
382 static const struct {
388 CE_CONFIG_STR_V(Gpu, 3),
390 // CE_CONFIG_STR(Cdr),
392 // CE_CONFIG_VAL(Sio),
395 CE_CONFIG_VAL(Debug),
396 CE_CONFIG_VAL(PsxOut),
397 CE_CONFIG_VAL(SpuIrq),
398 CE_CONFIG_VAL(RCntFix),
399 CE_CONFIG_VAL(VSyncWA),
402 CE_INTVAL_V(g_scaler, 3),
404 CE_INTVAL(g_layer_x),
405 CE_INTVAL(g_layer_y),
406 CE_INTVAL(g_layer_w),
407 CE_INTVAL(g_layer_h),
408 CE_INTVAL(soft_filter),
409 CE_INTVAL(scanlines),
410 CE_INTVAL(scanline_level),
411 CE_INTVAL(plat_target.vout_method),
412 CE_INTVAL(plat_target.hwfilter),
413 CE_INTVAL(plat_target.vout_fullscreen),
414 CE_INTVAL(state_slot),
415 CE_INTVAL(cpu_clock),
417 CE_INTVAL(in_type_sel1),
418 CE_INTVAL(in_type_sel2),
419 CE_INTVAL(analog_deadzone),
420 CE_INTVAL(memcard1_sel),
421 CE_INTVAL(memcard2_sel),
422 CE_INTVAL(g_autostateld_opt),
423 CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
424 CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
425 CE_INTVAL_V(frameskip, 3),
426 CE_INTVAL_P(gpu_peops.iUseDither),
427 CE_INTVAL_P(gpu_peops.dwActFixes),
428 CE_INTVAL_P(gpu_unai.lineskip),
429 CE_INTVAL_P(gpu_unai.abe_hack),
430 CE_INTVAL_P(gpu_unai.no_light),
431 CE_INTVAL_P(gpu_unai.no_blend),
432 CE_INTVAL_P(gpu_neon.allow_interlace),
433 CE_INTVAL_P(gpu_neon.enhancement_enable),
434 CE_INTVAL_P(gpu_neon.enhancement_no_main),
435 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
436 CE_INTVAL_P(gpu_peopsgl.iFilterType),
437 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
438 CE_INTVAL_P(gpu_peopsgl.iUseMask),
439 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
440 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
441 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
442 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
443 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
444 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
445 CE_INTVAL(spu_config.iUseReverb),
446 CE_INTVAL(spu_config.iXAPitch),
447 CE_INTVAL(spu_config.iUseInterpolation),
448 CE_INTVAL(spu_config.iTempo),
449 CE_INTVAL(spu_config.iUseThread),
450 CE_INTVAL(config_save_counter),
451 CE_INTVAL(in_evdev_allow_abs_only),
452 CE_INTVAL(volume_boost),
453 CE_INTVAL(psx_clock),
454 CE_INTVAL(new_dynarec_hacks),
455 CE_INTVAL(in_enable_vibration),
458 static char *get_cd_label(void)
460 static char trimlabel[33];
463 strncpy(trimlabel, CdromLabel, 32);
465 for (j = 31; j >= 0; j--)
466 if (trimlabel[j] == ' ')
472 static void make_cfg_fname(char *buf, size_t size, int is_game)
475 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
477 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
480 static void keys_write_all(FILE *f);
481 static char *mystrip(char *str);
483 static int menu_write_config(int is_game)
485 char cfgfile[MAXPATHLEN];
489 config_save_counter++;
491 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
492 f = fopen(cfgfile, "w");
494 printf("menu_write_config: failed to open: %s\n", cfgfile);
498 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
499 fprintf(f, "%s = ", config_data[i].name);
500 switch (config_data[i].len) {
502 fprintf(f, "%s\n", (char *)config_data[i].val);
505 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
508 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
511 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
514 printf("menu_write_config: unhandled len %d for %s\n",
515 (int)config_data[i].len, config_data[i].name);
526 static int menu_do_last_cd_img(int is_get)
528 static const char *defaults[] = { "/media", "/mnt/sd", "/mnt" };
534 snprintf(path, sizeof(path), "." PCSX_DOT_DIR "lastcdimg.txt");
535 f = fopen(path, is_get ? "r" : "w");
542 ret = fread(last_selected_fname, 1, sizeof(last_selected_fname) - 1, f);
543 last_selected_fname[ret] = 0;
544 mystrip(last_selected_fname);
547 fprintf(f, "%s\n", last_selected_fname);
552 for (i = 0; last_selected_fname[0] == 0
553 || stat64(last_selected_fname, &st) != 0; i++)
555 if (i >= ARRAY_SIZE(defaults))
557 strcpy(last_selected_fname, defaults[i]);
564 static void parse_str_val(char *cval, const char *src)
567 strncpy(cval, src, MAXPATHLEN);
568 cval[MAXPATHLEN - 1] = 0;
569 tmp = strchr(cval, '\n');
571 tmp = strchr(cval, '\r');
576 static void keys_load_all(const char *cfg);
578 static int menu_load_config(int is_game)
580 char cfgfile[MAXPATHLEN];
586 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
587 f = fopen(cfgfile, "r");
589 printf("menu_load_config: failed to open: %s\n", cfgfile);
593 fseek(f, 0, SEEK_END);
596 printf("bad size %ld: %s\n", size, cfgfile);
600 cfg = malloc(size + 1);
604 fseek(f, 0, SEEK_SET);
605 if (fread(cfg, 1, size, f) != size) {
606 printf("failed to read: %s\n", cfgfile);
611 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
615 tmp = strstr(cfg, config_data[i].name);
618 tmp += strlen(config_data[i].name);
619 if (strncmp(tmp, " = ", 3) != 0)
623 if (config_data[i].len == 0) {
624 parse_str_val(config_data[i].val, tmp);
629 val = strtoul(tmp, &tmp2, 16);
630 if (tmp2 == NULL || tmp == tmp2)
631 continue; // parse failed
633 switch (config_data[i].len) {
635 *(u8 *)config_data[i].val = val;
638 *(u16 *)config_data[i].val = val;
641 *(u32 *)config_data[i].val = val;
644 printf("menu_load_config: unhandled len %d for %s\n",
645 (int)config_data[i].len, config_data[i].name);
651 char *tmp = strstr(cfg, "lastcdimg = ");
654 parse_str_val(last_selected_fname, tmp);
669 for (i = bios_sel = 0; bioses[i] != NULL; i++)
670 if (strcmp(Config.Bios, bioses[i]) == 0)
671 { bios_sel = i; break; }
673 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
674 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
675 { gpu_plugsel = i; break; }
677 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
678 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
679 { spu_plugsel = i; break; }
681 // memcard selections
682 char mcd1_old[sizeof(Config.Mcd1)];
683 char mcd2_old[sizeof(Config.Mcd2)];
684 strcpy(mcd1_old, Config.Mcd1);
685 strcpy(mcd2_old, Config.Mcd2);
687 if ((unsigned int)memcard1_sel < ARRAY_SIZE(memcards)) {
688 if (memcard1_sel == 0)
689 strcpy(Config.Mcd1, "none");
690 else if (memcards[memcard1_sel] != NULL)
691 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s",
692 MEMCARD_DIR, memcards[memcard1_sel]);
694 if ((unsigned int)memcard2_sel < ARRAY_SIZE(memcards)) {
695 if (memcard2_sel == 0)
696 strcpy(Config.Mcd2, "none");
697 else if (memcards[memcard2_sel] != NULL)
698 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s",
699 MEMCARD_DIR, memcards[memcard2_sel]);
701 if (strcmp(mcd1_old, Config.Mcd1) || strcmp(mcd2_old, Config.Mcd2))
702 LoadMcds(Config.Mcd1, Config.Mcd2);
707 static const char *filter_exts[] = {
708 "bin", "img", "mdf", "iso", "cue", "z",
709 "bz", "znx", "pbp", "cbn", NULL
712 // rrrr rggg gggb bbbb
713 static unsigned short fname2color(const char *fname)
715 static const char *other_exts[] = {
716 "ccd", "toc", "mds", "sub", "table", "index", "sbi"
718 const char *ext = strrchr(fname, '.');
724 for (i = 0; filter_exts[i] != NULL; i++)
725 if (strcasecmp(ext, filter_exts[i]) == 0)
727 for (i = 0; i < array_size(other_exts); i++)
728 if (strcasecmp(ext, other_exts[i]) == 0)
733 static void draw_savestate_bg(int slot);
735 #define MENU_ALIGN_LEFT
736 #ifndef HAVE_PRE_ARMV7 // assume hires device
742 #include "libpicofe/menu.c"
744 // a bit of black magic here
745 static void draw_savestate_bg(int slot)
747 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
749 char fname[MAXPATHLEN];
756 ret = get_state_filename(fname, sizeof(fname), slot);
760 f = gzopen(fname, "rb");
764 if ((ret = (int)gzseek(f, 0x29933d, SEEK_SET)) != 0x29933d) {
765 fprintf(stderr, "gzseek failed: %d\n", ret);
770 gpu = malloc(sizeof(*gpu));
776 ret = gzread(f, gpu, sizeof(*gpu));
778 if (ret != sizeof(*gpu)) {
779 fprintf(stderr, "gzread failed\n");
783 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
785 if (gpu->ulStatus & 0x800000)
786 goto out; // disabled
788 x = gpu->ulControl[5] & 0x3ff;
789 y = (gpu->ulControl[5] >> 10) & 0x1ff;
790 w = psx_widths[(gpu->ulStatus >> 16) & 7];
791 tmp = gpu->ulControl[7];
792 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
793 if (gpu->ulStatus & 0x80000) // doubleheight
795 if (h <= 0 || h > 512)
801 s = (u16 *)gpu->psxVRam + y * 1024 + x;
803 x = max(0, g_menuscreen_w - w) & ~3;
804 y = max(0, g_menuscreen_h / 2 - h / 2);
805 w = min(g_menuscreen_w, w);
806 h = min(g_menuscreen_h, h);
807 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
809 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
810 if (gpu->ulStatus & 0x200000)
811 bgr888_to_rgb565(d, s, w * 3);
813 bgr555_to_rgb565(d, s, w * 2);
815 // darken this so that menu text is visible
816 if (g_menuscreen_w - w < 320)
817 menu_darken_bg(d, d, w * 2, 0);
824 // -------------- key config --------------
826 me_bind_action me_ctrl_actions[] =
828 { "UP ", 1 << DKEY_UP},
829 { "DOWN ", 1 << DKEY_DOWN },
830 { "LEFT ", 1 << DKEY_LEFT },
831 { "RIGHT ", 1 << DKEY_RIGHT },
832 { "TRIANGLE", 1 << DKEY_TRIANGLE },
833 { "CIRCLE ", 1 << DKEY_CIRCLE },
834 { "CROSS ", 1 << DKEY_CROSS },
835 { "SQUARE ", 1 << DKEY_SQUARE },
836 { "L1 ", 1 << DKEY_L1 },
837 { "R1 ", 1 << DKEY_R1 },
838 { "L2 ", 1 << DKEY_L2 },
839 { "R2 ", 1 << DKEY_R2 },
840 { "L3 ", 1 << DKEY_L3 },
841 { "R3 ", 1 << DKEY_R3 },
842 { "START ", 1 << DKEY_START },
843 { "SELECT ", 1 << DKEY_SELECT },
847 me_bind_action emuctrl_actions[] =
849 { "Save State ", 1 << SACTION_SAVE_STATE },
850 { "Load State ", 1 << SACTION_LOAD_STATE },
851 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
852 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
853 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
854 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
855 { "Show/Hide FPS ", 1 << SACTION_TOGGLE_FPS },
856 #ifndef HAVE_PRE_ARMV7
857 { "Switch Renderer ", 1 << SACTION_SWITCH_DISPMODE },
859 { "Fast Forward ", 1 << SACTION_FAST_FORWARD },
860 #if MENU_SHOW_MINIMIZE
861 { "Minimize ", 1 << SACTION_MINIMIZE },
863 #if MENU_SHOW_FULLSCREEN
864 { "Toggle fullscreen", 1 << SACTION_TOGGLE_FULLSCREEN },
866 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
867 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
868 { "Gun A button ", 1 << SACTION_GUN_A },
869 { "Gun B button ", 1 << SACTION_GUN_B },
870 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
872 { "Volume Up ", 1 << SACTION_VOLUME_UP },
873 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
878 static char *mystrip(char *str)
883 for (i = 0; i < len; i++)
884 if (str[i] != ' ') break;
885 if (i > 0) memmove(str, str + i, len - i + 1);
888 for (i = len - 1; i >= 0; i--)
889 if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
895 static void get_line(char *d, size_t size, const char *s)
900 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
909 static void keys_write_all(FILE *f)
913 for (d = 0; d < IN_MAX_DEVS; d++)
915 const int *binds = in_get_dev_binds(d);
916 const char *name = in_get_dev_name(d, 0, 0);
919 if (binds == NULL || name == NULL)
922 fprintf(f, "binddev = %s\n", name);
923 in_get_config(d, IN_CFG_BIND_COUNT, &count);
925 for (k = 0; k < count; k++)
930 act[0] = act[31] = 0;
931 name = in_get_key_name(d, k);
933 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
934 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
935 mask = me_ctrl_actions[i].mask;
937 strncpy(act, me_ctrl_actions[i].name, 31);
938 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
941 mask = me_ctrl_actions[i].mask << 16;
943 strncpy(act, me_ctrl_actions[i].name, 31);
944 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
949 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
950 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
951 mask = emuctrl_actions[i].mask;
953 strncpy(act, emuctrl_actions[i].name, 31);
954 fprintf(f, "bind %s = %s\n", name, mystrip(act));
960 for (k = 0; k < array_size(in_adev); k++)
963 fprintf(f, "bind_analog = %d\n", k);
968 static int parse_bind_val(const char *val, int *type)
972 *type = IN_BINDTYPE_NONE;
976 if (strncasecmp(val, "player", 6) == 0)
978 int player, shift = 0;
979 player = atoi(val + 6) - 1;
981 if ((unsigned int)player > 1)
986 *type = IN_BINDTYPE_PLAYER12;
987 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
988 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
989 return me_ctrl_actions[i].mask << shift;
992 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
993 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
994 *type = IN_BINDTYPE_EMU;
995 return emuctrl_actions[i].mask;
1002 static void keys_load_all(const char *cfg)
1004 char dev[256], key[128], *act;
1010 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
1013 // don't strip 'dev' because there are weird devices
1014 // with names with space at the end
1015 get_line(dev, sizeof(dev), p);
1017 dev_id = in_config_parse_dev(dev);
1019 printf("input: can't handle dev: %s\n", dev);
1023 in_unbind_all(dev_id, -1, -1);
1024 while ((p = strstr(p, "bind"))) {
1025 if (strncmp(p, "binddev = ", 10) == 0)
1028 if (strncmp(p, "bind_analog", 11) == 0) {
1029 ret = sscanf(p, "bind_analog = %d", &bind);
1032 printf("input: parse error: %16s..\n", p);
1035 if ((unsigned int)bind >= array_size(in_adev)) {
1036 printf("input: analog id %d out of range\n", bind);
1039 in_adev[bind] = dev_id;
1045 printf("input: parse error: %16s..\n", p);
1049 get_line(key, sizeof(key), p);
1050 act = strchr(key, '=');
1052 printf("parse failed: %16s..\n", p);
1060 bind = parse_bind_val(act, &bindtype);
1061 if (bind != -1 && bind != 0) {
1062 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
1063 in_config_bind_key(dev_id, key, bind, bindtype);
1066 lprintf("config: unhandled action \"%s\"\n", act);
1072 static int key_config_loop_wrap(int id, int keys)
1075 case MA_CTRL_PLAYER1:
1076 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
1078 case MA_CTRL_PLAYER2:
1079 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
1082 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
1090 static const char h_nubmode[] = "Maps nub-like analog controls to PSX ones better\n"
1091 "Might cause problems with real analog sticks";
1092 static const char *adevnames[IN_MAX_DEVS + 2];
1093 static int stick_sel[2];
1095 static menu_entry e_menu_keyconfig_analog[] =
1097 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
1098 mee_range (" X axis", 0, in_adev_axis[0][0], 0, 7),
1099 mee_range (" Y axis", 0, in_adev_axis[0][1], 0, 7),
1100 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[0], 1, h_nubmode),
1101 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
1102 mee_range (" X axis", 0, in_adev_axis[1][0], 0, 7),
1103 mee_range (" Y axis", 0, in_adev_axis[1][1], 0, 7),
1104 mee_onoff_h(" nub mode", 0, in_adev_is_nublike[1], 1, h_nubmode),
1108 static int key_config_analog(int id, int keys)
1110 int i, d, count, sel = 0;
1111 int sel2dev_map[IN_MAX_DEVS];
1113 memset(adevnames, 0, sizeof(adevnames));
1114 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1115 memset(stick_sel, 0, sizeof(stick_sel));
1117 adevnames[0] = "None";
1119 for (d = 0; d < IN_MAX_DEVS; d++)
1121 const char *name = in_get_dev_name(d, 0, 1);
1126 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1130 if (in_adev[0] == d) stick_sel[0] = i;
1131 if (in_adev[1] == d) stick_sel[1] = i;
1133 adevnames[i++] = name;
1135 adevnames[i] = NULL;
1137 me_loop(e_menu_keyconfig_analog, &sel);
1139 in_adev[0] = sel2dev_map[stick_sel[0]];
1140 in_adev[1] = sel2dev_map[stick_sel[1]];
1145 static const char *mgn_dev_name(int id, int *offs)
1147 const char *name = NULL;
1150 if (id == MA_CTRL_DEV_FIRST)
1153 for (; it < IN_MAX_DEVS; it++) {
1154 name = in_get_dev_name(it, 1, 1);
1163 static const char *mgn_saveloadcfg(int id, int *offs)
1168 static int mh_savecfg(int id, int keys)
1170 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1171 menu_update_msg("config saved");
1173 menu_update_msg("failed to write config");
1178 static int mh_input_rescan(int id, int keys)
1180 //menu_sync_config();
1182 menu_update_msg("rescan complete.");
1187 static const char *men_in_type_sel[] = {
1188 "Standard (SCPH-1080)",
1189 "Analog (SCPH-1150)",
1193 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1194 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1195 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1197 static menu_entry e_menu_keyconfig[] =
1199 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1200 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1201 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
1202 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
1204 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1205 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
1206 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
1207 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
1208 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
1209 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1210 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1211 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1212 mee_handler ("Rescan devices:", mh_input_rescan),
1214 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
1215 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1216 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1217 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1218 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1219 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1220 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1224 static int menu_loop_keyconfig(int id, int keys)
1228 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1229 me_loop(e_menu_keyconfig, &sel);
1233 // ------------ gfx options menu ------------
1235 static const char *men_scaler[] = {
1236 "1x1", "integer scaled 2x", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL
1238 static const char *men_soft_filter[] = { "None",
1240 "scale2x", "eagle2x",
1243 static const char *men_dummy[] = { NULL };
1244 static const char h_scaler[] = "int. 2x - scales w. or h. 2x if it fits on screen\n"
1245 "int. 4:3 - uses integer if possible, else fractional";
1246 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1247 "using d-pad or move it using R+d-pad";
1248 static const char h_overlay[] = "Overlay provides hardware accelerated scaling";
1249 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1250 static const char h_scanline_l[] = "Scanline brightness, 0-100%";
1251 static const char h_gamma[] = "Gamma/brightness adjustment (default 100)";
1253 static int menu_loop_cscaler(int id, int keys)
1257 g_scaler = SCALE_CUSTOM;
1259 plat_gvideo_open(Config.PsxType);
1263 menu_draw_begin(0, 1);
1264 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1265 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1266 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1269 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1270 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1271 if (inp & PBTN_UP) g_layer_y--;
1272 if (inp & PBTN_DOWN) g_layer_y++;
1273 if (inp & PBTN_LEFT) g_layer_x--;
1274 if (inp & PBTN_RIGHT) g_layer_x++;
1275 if (!(inp & PBTN_R)) {
1276 if (inp & PBTN_UP) g_layer_h += 2;
1277 if (inp & PBTN_DOWN) g_layer_h -= 2;
1278 if (inp & PBTN_LEFT) g_layer_w += 2;
1279 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1281 if (inp & (PBTN_MOK|PBTN_MBACK))
1284 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1285 if (g_layer_x < 0) g_layer_x = 0;
1286 if (g_layer_x > 640) g_layer_x = 640;
1287 if (g_layer_y < 0) g_layer_y = 0;
1288 if (g_layer_y > 420) g_layer_y = 420;
1289 if (g_layer_w < 160) g_layer_w = 160;
1290 if (g_layer_h < 60) g_layer_h = 60;
1291 if (g_layer_x + g_layer_w > 800)
1292 g_layer_w = 800 - g_layer_x;
1293 if (g_layer_y + g_layer_h > 480)
1294 g_layer_h = 480 - g_layer_y;
1296 plat_gvideo_open(Config.PsxType);
1300 plat_gvideo_close();
1305 static menu_entry e_menu_gfx_options[] =
1307 mee_enum_h ("Scaler", MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1308 mee_enum ("Video output mode", MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1309 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1310 mee_enum ("Hardware Filter", MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1311 mee_enum_h ("Software Filter", MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1313 mee_onoff ("Scanlines", MA_OPT_SCANLINES, scanlines, 1),
1314 mee_range_h ("Scanline brightness", MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1316 mee_range_h ("Gamma adjustment", MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1317 // mee_onoff ("Vsync", 0, vsync, 1),
1318 mee_cust_h ("Setup custom scaler", MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1322 static int menu_loop_gfx_options(int id, int keys)
1326 me_loop(e_menu_gfx_options, &sel);
1331 // ------------ bios/plugins ------------
1335 static const char h_gpu_neon[] =
1336 "Configure built-in NEON GPU plugin";
1337 static const char h_gpu_neon_enhanced[] =
1338 "Renders in double resolution at the cost of lower performance\n"
1339 "(not available for high resolution games)";
1340 static const char h_gpu_neon_enhanced_hack[] =
1341 "Speed hack for above option (glitches some games)";
1342 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1344 static menu_entry e_menu_plugin_gpu_neon[] =
1346 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1347 mee_onoff_h ("Enhanced resolution (slow)", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1348 mee_onoff_h ("Enhanced res. speed hack", 0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1352 static int menu_loop_plugin_gpu_neon(int id, int keys)
1355 me_loop(e_menu_plugin_gpu_neon, &sel);
1361 static menu_entry e_menu_plugin_gpu_unai[] =
1363 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1364 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1365 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1366 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1370 static int menu_loop_plugin_gpu_unai(int id, int keys)
1373 me_loop(e_menu_plugin_gpu_unai, &sel);
1377 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1378 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1379 static const char h_gpu_1[] = "Capcom fighting games";
1380 static const char h_gpu_2[] = "Black screens in Lunar";
1381 static const char h_gpu_3[] = "Compatibility mode";
1382 static const char h_gpu_6[] = "Pandemonium 2";
1383 //static const char h_gpu_7[] = "Skip every second frame";
1384 static const char h_gpu_8[] = "Needed by Dark Forces";
1385 static const char h_gpu_9[] = "better g-colors, worse textures";
1386 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1388 static menu_entry e_menu_plugin_gpu_peops[] =
1390 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1391 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1392 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1393 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1394 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1395 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1396 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1397 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1398 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1399 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1403 static int menu_loop_plugin_gpu_peops(int id, int keys)
1406 me_loop(e_menu_plugin_gpu_peops, &sel);
1410 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1411 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1412 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1414 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1416 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1417 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1418 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1419 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1420 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1421 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1422 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1423 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1424 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1425 mee_label ("Fixes/hacks:"),
1426 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1427 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1428 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1429 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1430 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1431 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1432 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1433 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1434 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1435 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1436 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1440 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1443 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1447 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1448 static const char h_spu_volboost[] = "Large values cause distortion";
1449 static const char h_spu_tempo[] = "Slows down audio if emu is too slow\n"
1450 "This is inaccurate and breaks games";
1452 static menu_entry e_menu_plugin_spu[] =
1454 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1455 mee_onoff ("Reverb", 0, spu_config.iUseReverb, 1),
1456 mee_enum ("Interpolation", 0, spu_config.iUseInterpolation, men_spu_interp),
1457 mee_onoff ("Adjust XA pitch", 0, spu_config.iXAPitch, 1),
1458 mee_onoff_h ("Adjust tempo", 0, spu_config.iTempo, 1, h_spu_tempo),
1462 static int menu_loop_plugin_spu(int id, int keys)
1465 me_loop(e_menu_plugin_spu, &sel);
1469 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1470 "savestates and can't be changed there. Must save\n"
1471 "config and reload the game for change to take effect";
1472 static const char h_plugin_gpu[] =
1474 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1476 "gpu_peops is Pete's soft GPU, slow but accurate\n"
1477 "gpu_unai is GPU from PCSX4ALL, fast but glitchy\n"
1478 "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1479 "must save config and reload the game if changed";
1480 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1481 "must save config and reload the game if changed";
1482 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1483 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1484 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team GPU plugin";
1485 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1487 static menu_entry e_menu_plugin_options[] =
1489 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1490 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1491 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1493 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1495 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1496 mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1497 mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1498 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1502 static menu_entry e_menu_main2[];
1504 static int menu_loop_plugin_options(int id, int keys)
1507 me_loop(e_menu_plugin_options, &sel);
1509 // sync BIOS/plugins
1510 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1511 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1512 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1513 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1518 // ------------ adv options menu ------------
1520 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1521 "(lower value - less work for the emu, may be faster)";
1522 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1523 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1524 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1526 static menu_entry e_menu_speed_hacks[] =
1528 mee_range_h ("PSX CPU clock, %%", 0, psx_clock, 1, 500, h_cfg_psxclk),
1529 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1530 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1531 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1535 static int menu_loop_speed_hacks(int id, int keys)
1538 me_loop(e_menu_speed_hacks, &sel);
1542 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1543 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1544 "(green: normal, red: fmod, blue: noise)";
1545 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1546 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1547 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1548 "(proper .cue/.bin dump is needed otherwise)";
1549 static const char h_cfg_sio[] = "You should not need this, breaks games";
1550 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1551 static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1552 "(timing hack, breaks other games)";
1553 static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix\n"
1554 "(timing hack, breaks other games)";
1555 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1556 "Might be useful to overcome some dynarec bugs";
1557 static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
1558 "must reload game for any change to take effect";
1560 static menu_entry e_menu_adv_options[] =
1562 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1563 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1564 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1565 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1566 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1567 //mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1568 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1569 //mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1570 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
1571 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1572 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1576 static int menu_loop_adv_options(int id, int keys)
1579 me_loop(e_menu_adv_options, &sel);
1583 // ------------ options menu ------------
1585 static int mh_restore_defaults(int id, int keys)
1587 menu_set_defconfig();
1588 menu_update_msg("defaults restored");
1592 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1593 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1595 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1596 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1597 "loading state or both";
1599 static const char h_restore_def[] = "Switches back to default / recommended\n"
1601 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1603 static menu_entry e_menu_options[] =
1605 // mee_range ("Save slot", 0, state_slot, 0, 9),
1606 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1607 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1608 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1609 mee_enum ("Region", 0, region, men_region),
1610 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1612 mee_onoff ("Use C64x DSP for sound", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1614 mee_onoff ("Threaded SPU", MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1616 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1617 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1618 mee_handler ("[Advanced]", menu_loop_adv_options),
1619 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1620 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1621 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1625 static int menu_loop_options(int id, int keys)
1629 me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1630 me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1631 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1633 me_loop(e_menu_options, &sel);
1638 // ------------ debug menu ------------
1640 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1642 int w = min(g_menuscreen_w, 1024);
1643 int h = min(g_menuscreen_h, 512);
1644 u16 *d = g_menuscreen_ptr;
1645 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1649 gpuf->ulFreezeVersion = 1;
1650 if (GPU_freeze != NULL)
1651 GPU_freeze(1, gpuf);
1653 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1654 bgr555_to_rgb565(d, s, w * 2);
1656 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1657 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1658 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1659 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1660 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1663 static void debug_menu_loop(void)
1665 int inp, df_x = 0, df_y = 0;
1668 gpuf = malloc(sizeof(*gpuf));
1674 menu_draw_begin(0, 1);
1675 draw_frame_debug(gpuf, df_x, df_y);
1678 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1679 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1680 if (inp & PBTN_MBACK) break;
1681 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1682 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1683 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x -= 2; }
1684 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1690 // --------- memcard manager ---------
1692 static void draw_mc_icon(int dx, int dy, const u16 *s)
1697 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1699 for (y = 0; y < 16; y++, s += 16) {
1700 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1701 for (x = 0; x < 16; x++) {
1703 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1704 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1710 static void draw_mc_bg(void)
1712 McdBlock *blocks1, *blocks2;
1716 blocks1 = malloc(15 * sizeof(blocks1[0]));
1717 blocks2 = malloc(15 * sizeof(blocks1[0]));
1718 if (blocks1 == NULL || blocks2 == NULL)
1721 for (i = 0; i < 15; i++) {
1722 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1723 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1726 menu_draw_begin(1, 1);
1728 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1730 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1734 maxicons = g_menuscreen_h / 32;
1737 row2 = g_menuscreen_w / 2;
1738 for (i = 0; i < maxicons; i++) {
1739 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1740 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1742 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1743 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1746 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1754 static void handle_memcard_sel(void)
1756 strcpy(Config.Mcd1, "none");
1757 if (memcard1_sel != 0)
1758 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1759 strcpy(Config.Mcd2, "none");
1760 if (memcard2_sel != 0)
1761 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1762 LoadMcds(Config.Mcd1, Config.Mcd2);
1766 static menu_entry e_memcard_options[] =
1768 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1769 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1773 static int menu_loop_memcards(int id, int keys)
1779 memcard1_sel = memcard2_sel = 0;
1780 p = strrchr(Config.Mcd1, '/');
1782 for (i = 0; memcards[i] != NULL; i++)
1783 if (strcmp(p + 1, memcards[i]) == 0)
1784 { memcard1_sel = i; break; }
1785 p = strrchr(Config.Mcd2, '/');
1787 for (i = 0; memcards[i] != NULL; i++)
1788 if (strcmp(p + 1, memcards[i]) == 0)
1789 { memcard2_sel = i; break; }
1791 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1793 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1798 // ------------ cheats menu ------------
1800 static void draw_cheatlist(int sel)
1802 int max_cnt, start, i, pos, active;
1804 max_cnt = g_menuscreen_h / me_sfont_h;
1805 start = max_cnt / 2 - sel;
1807 menu_draw_begin(1, 1);
1809 for (i = 0; i < NumCheats; i++) {
1811 if (pos < 0) continue;
1812 if (pos >= max_cnt) break;
1813 active = Cheats[i].Enabled;
1814 smalltext_out16(14, pos * me_sfont_h,
1815 active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1816 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1817 Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1821 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1823 text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1827 static void menu_loop_cheats(void)
1829 static int menu_sel = 0;
1834 draw_cheatlist(menu_sel);
1835 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1836 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1837 if (inp & PBTN_UP ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1838 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1839 if (inp &(PBTN_LEFT|PBTN_L)) { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1840 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1841 if (inp & PBTN_MOK) { // action
1842 if (menu_sel < NumCheats)
1843 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1846 if (inp & PBTN_MBACK)
1851 // --------- main menu help ----------
1853 static void menu_bios_warn(void)
1856 static const char msg[] =
1857 "You don't seem to have copied any BIOS\n"
1859 MENU_BIOS_PATH "\n\n"
1861 "While many games work fine with fake\n"
1862 "(HLE) BIOS, others (like MGS and FF8)\n"
1863 "require BIOS to work.\n"
1864 "After copying the file, you'll also need\n"
1865 "to select it in the emu's menu:\n"
1866 "options->[BIOS/Plugins]\n\n"
1867 "The file is usually named SCPH1001.BIN,\n"
1868 "but other not compressed files can be\n"
1870 "Press %s or %s to continue";
1871 char tmp_msg[sizeof(msg) + 64];
1873 snprintf(tmp_msg, sizeof(tmp_msg), msg,
1874 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1877 draw_menu_message(tmp_msg, NULL);
1879 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
1880 if (inp & (PBTN_MBACK|PBTN_MOK))
1885 // ------------ main menu ------------
1887 static menu_entry e_menu_main[];
1889 static void draw_frame_main(void)
1898 if (CdromId[0] != 0) {
1899 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1900 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1901 Config.HLE ? "HLE" : "BIOS");
1902 smalltext_out16(4, 1, buff, 0x105f);
1906 capacity = plat_target_bat_capacity_get();
1908 tmp = localtime(<ime);
1909 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1910 if (capacity >= 0) {
1911 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1916 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1920 static void draw_frame_credits(void)
1922 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1925 static const char credits_text[] =
1927 "(C) 1999-2003 PCSX Team\n"
1928 "(C) 2005-2009 PCSX-df Team\n"
1929 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1930 "ARM recompiler (C) 2009-2011 Ari64\n"
1932 "ARM NEON GPU (c) 2011-2012 Exophase\n"
1934 "PEOpS GPU and SPU by Pete Bernert\n"
1935 " and the P.E.Op.S. team\n"
1936 "PCSX4ALL plugin by PCSX4ALL team\n"
1937 " Chui, Franxis, Unai\n\n"
1938 "integration, optimization and\n"
1939 " frontend (C) 2010-2015 notaz\n";
1941 static int reset_game(void)
1944 if (bios_sel == 0 && !Config.HLE)
1950 if (CheckCdrom() != -1) {
1956 static int reload_plugins(const char *cdimg)
1962 set_cd_image(cdimg);
1964 pcnt_hook_plugins();
1966 if (OpenPlugins() == -1) {
1967 menu_update_msg("failed to open plugins");
1970 plugin_call_rearmed_cbs();
1972 cdrIsoMultidiskCount = 1;
1974 CdromLabel[0] = '\0';
1979 static int run_bios(void)
1985 if (reload_plugins(NULL) != 0)
1993 static int run_exe(void)
1995 const char *exts[] = { "exe", NULL };
1998 fname = menu_loop_romsel(last_selected_fname,
1999 sizeof(last_selected_fname), exts, NULL);
2004 if (reload_plugins(NULL) != 0)
2008 if (Load(fname) != 0) {
2009 menu_update_msg("exe load failed, bad file?");
2018 static int run_cd_image(const char *fname)
2020 int autoload_state = g_autostateld_opt;
2023 reload_plugins(fname);
2025 // always autodetect, menu_sync_config will override as needed
2028 if (CheckCdrom() == -1) {
2029 // Only check the CD if we are starting the console with a CD
2031 menu_update_msg("unsupported/invalid CD image");
2037 // Read main executable directly from CDRom and start it
2038 if (LoadCdrom() == -1) {
2040 menu_update_msg("failed to load CD image");
2047 if (autoload_state) {
2048 unsigned int newest = 0;
2049 int time, slot, newest_slot = -1;
2051 for (slot = 0; slot < 10; slot++) {
2052 if (emu_check_save_file(slot, &time)) {
2053 if ((unsigned int)time > newest) {
2060 if (newest_slot >= 0) {
2061 lprintf("autoload slot %d\n", newest_slot);
2062 emu_load_state(newest_slot);
2065 lprintf("no save to autoload.\n");
2072 static int romsel_run(void)
2074 int prev_gpu, prev_spu;
2077 fname = menu_loop_romsel(last_selected_fname,
2078 sizeof(last_selected_fname), filter_exts,
2079 optional_cdimg_filter);
2083 printf("selected file: %s\n", fname);
2085 new_dynarec_clear_full();
2087 if (run_cd_image(fname) != 0)
2090 prev_gpu = gpu_plugsel;
2091 prev_spu = spu_plugsel;
2092 if (menu_load_config(1) != 0)
2093 menu_load_config(0);
2095 // check for plugin changes, have to repeat
2096 // loading if game config changed plugins to reload them
2097 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2098 printf("plugin change detected, reloading plugins..\n");
2099 if (run_cd_image(fname) != 0)
2103 strcpy(last_selected_fname, fname);
2104 menu_do_last_cd_img(0);
2108 static int swap_cd_image(void)
2112 fname = menu_loop_romsel(last_selected_fname,
2113 sizeof(last_selected_fname), filter_exts,
2114 optional_cdimg_filter);
2118 printf("selected file: %s\n", fname);
2121 CdromLabel[0] = '\0';
2123 set_cd_image(fname);
2124 if (ReloadCdromPlugin() < 0) {
2125 menu_update_msg("failed to load cdr plugin");
2128 if (CDR_open() < 0) {
2129 menu_update_msg("failed to open cdr plugin");
2133 SetCdOpenCaseTime(time(NULL) + 2);
2136 strcpy(last_selected_fname, fname);
2140 static int swap_cd_multidisk(void)
2142 cdrIsoMultidiskSelect++;
2144 CdromLabel[0] = '\0';
2147 if (CDR_open() < 0) {
2148 menu_update_msg("failed to open cdr plugin");
2152 SetCdOpenCaseTime(time(NULL) + 2);
2158 static void load_pcsx_cht(void)
2160 static const char *exts[] = { "cht", NULL };
2164 fname = menu_loop_romsel(last_selected_fname,
2165 sizeof(last_selected_fname), exts, NULL);
2169 printf("selected cheat file: %s\n", fname);
2172 if (NumCheats == 0 && NumCodes == 0)
2173 menu_update_msg("failed to load cheats");
2175 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2176 menu_update_msg(msg);
2178 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2181 static int main_menu_handler(int id, int keys)
2185 case MA_MAIN_RESUME_GAME:
2189 case MA_MAIN_SAVE_STATE:
2191 return menu_loop_savestate(0);
2193 case MA_MAIN_LOAD_STATE:
2195 return menu_loop_savestate(1);
2197 case MA_MAIN_RESET_GAME:
2198 if (ready_to_go && reset_game() == 0)
2201 case MA_MAIN_LOAD_ROM:
2202 if (romsel_run() == 0)
2205 case MA_MAIN_SWAP_CD:
2206 if (swap_cd_image() == 0)
2209 case MA_MAIN_SWAP_CD_MULTI:
2210 if (swap_cd_multidisk() == 0)
2213 case MA_MAIN_RUN_BIOS:
2214 if (run_bios() == 0)
2217 case MA_MAIN_RUN_EXE:
2221 case MA_MAIN_CHEATS:
2224 case MA_MAIN_LOAD_CHEATS:
2227 case MA_MAIN_CREDITS:
2228 draw_menu_message(credits_text, draw_frame_credits);
2229 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2232 emu_core_ask_exit();
2235 lprintf("%s: something unknown selected\n", __FUNCTION__);
2242 static menu_entry e_menu_main2[] =
2244 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
2245 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2246 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
2247 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
2248 mee_handler ("Memcard manager", menu_loop_memcards),
2249 mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS, main_menu_handler),
2253 static int main_menu2_handler(int id, int keys)
2257 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
2258 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2259 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2260 me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2262 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2265 static const char h_extra[] = "Change CD, manage memcards..\n";
2267 static menu_entry e_menu_main[] =
2271 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
2272 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
2273 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
2274 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
2275 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
2276 mee_handler ("Options", menu_loop_options),
2277 mee_handler ("Controls", menu_loop_keyconfig),
2278 mee_handler_id("Cheats", MA_MAIN_CHEATS, main_menu_handler),
2279 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
2280 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
2281 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
2285 // ----------------------------
2287 static void menu_leave_emu(void);
2289 void menu_loop(void)
2291 static int warned_about_bios = 0;
2296 if (config_save_counter == 0) {
2298 if (bioses[1] != NULL) {
2299 // autoselect BIOS to make user's life easier
2300 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2303 else if (!warned_about_bios) {
2305 warned_about_bios = 1;
2309 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2310 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
2311 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
2312 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
2313 me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2315 in_set_config_int(0, IN_CFG_BLOCKING, 1);
2318 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2319 } while (!ready_to_go && !g_emu_want_quit);
2321 /* wait until menu, ok, back is released */
2322 while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2325 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2330 static int qsort_strcmp(const void *p1, const void *p2)
2332 char * const *s1 = (char * const *)p1;
2333 char * const *s2 = (char * const *)p2;
2334 return strcasecmp(*s1, *s2);
2337 static void scan_bios_plugins(void)
2339 char fname[MAXPATHLEN];
2341 int bios_i, gpu_i, spu_i, mc_i;
2346 gpu_plugins[0] = "builtin_gpu";
2347 spu_plugins[0] = "builtin_spu";
2348 memcards[0] = "(none)";
2349 bios_i = gpu_i = spu_i = mc_i = 1;
2351 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2352 dir = opendir(fname);
2354 perror("scan_bios_plugins bios opendir");
2369 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2372 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2373 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2374 printf("bad BIOS file: %s\n", ent->d_name);
2378 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2379 bioses[bios_i++] = strdup(ent->d_name);
2383 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2389 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2390 dir = opendir(fname);
2392 perror("scan_bios_plugins plugins opendir");
2406 p = strstr(ent->d_name, ".so");
2410 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2411 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2413 fprintf(stderr, "%s\n", dlerror());
2417 // now what do we have here?
2418 tmp = dlsym(h, "GPUinit");
2421 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2422 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2426 tmp = dlsym(h, "SPUinit");
2429 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2430 spu_plugins[spu_i++] = strdup(ent->d_name);
2434 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2441 dir = opendir("." MEMCARD_DIR);
2443 perror("scan_bios_plugins memcards opendir");
2458 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2461 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2462 if (stat(fname, &st) != 0) {
2463 printf("bad memcard file: %s\n", ent->d_name);
2467 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2468 memcards[mc_i++] = strdup(ent->d_name);
2472 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2476 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2481 void menu_init(void)
2483 char buff[MAXPATHLEN];
2486 cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2488 scan_bios_plugins();
2491 menu_set_defconfig();
2492 menu_load_config(0);
2493 menu_do_last_cd_img(1);
2498 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2499 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2500 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2501 fprintf(stderr, "OOM\n");
2505 emu_make_path(buff, "skin/background.png", sizeof(buff));
2506 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2508 i = plat_target.cpu_clock_set != NULL
2509 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2510 me_enable(e_menu_gfx_options, MA_OPT_CPU_CLOCKS, i);
2512 i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2513 e_menu_gfx_options[i].data = plat_target.vout_methods;
2514 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2515 plat_target.vout_methods != NULL);
2517 i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2518 e_menu_gfx_options[i].data = plat_target.hwfilters;
2519 me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2520 plat_target.hwfilters != NULL);
2522 me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2523 plat_target.gamma_set != NULL);
2525 #ifdef HAVE_PRE_ARMV7
2526 me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2528 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2529 me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2530 me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2531 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2532 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2533 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2534 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2537 void menu_notify_mode_change(int w, int h, int bpp)
2541 last_vout_bpp = bpp;
2544 static void menu_leave_emu(void)
2546 if (GPU_close != NULL) {
2547 int ret = GPU_close();
2549 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2552 plat_video_menu_enter(ready_to_go);
2554 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2555 if (pl_vout_buf != NULL && ready_to_go) {
2556 int x = max(0, g_menuscreen_w - last_vout_w);
2557 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2558 int w = min(g_menuscreen_w, last_vout_w);
2559 int h = min(g_menuscreen_h, last_vout_h);
2560 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2561 char *s = pl_vout_buf;
2563 if (last_vout_bpp == 16) {
2564 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2565 menu_darken_bg(d, s, w, 0);
2568 for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2569 rgb888_to_rgb565(d, s, w * 3);
2570 menu_darken_bg(d, d, w, 0);
2576 cpu_clock = plat_target_cpu_clock_get();
2579 void menu_prepare_emu(void)
2581 R3000Acpu *prev_cpu = psxCpu;
2583 plat_video_menu_leave();
2585 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2586 if (psxCpu != prev_cpu) {
2587 prev_cpu->Shutdown();
2589 // note that this does not really reset, just clears drc caches
2593 // core doesn't care about Config.Cdda changes,
2594 // so handle them manually here
2600 plat_target_cpu_clock_set(cpu_clock);
2602 // push config to GPU plugin
2603 plugin_call_rearmed_cbs();
2605 if (GPU_open != NULL) {
2606 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2608 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2614 void menu_update_msg(const char *msg)
2616 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2617 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2619 menu_error_time = plat_get_ticks_ms();
2620 lprintf("msg: %s\n", menu_error_msg);
2623 void menu_finish(void)
2625 if (cpu_clock_st > 0)
2626 plat_target_cpu_clock_set(cpu_clock_st);