2 * (C) GraÅžvydas "notaz" Ignotas, 2010-2011
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.
16 #include <sys/types.h>
24 #include "plugin_lib.h"
27 #include "common/plat.h"
28 #include "common/input.h"
29 #include "linux/in_evdev.h"
30 #include "../libpcsxcore/misc.h"
31 #include "../libpcsxcore/cdrom.h"
32 #include "../libpcsxcore/cdriso.h"
33 #include "../libpcsxcore/psemu_plugin_defs.h"
34 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
35 #include "../plugins/dfinput/main.h"
36 #include "../plugins/gpulib/cspace.h"
39 #define REARMED_BIRTHDAY_TIME 1293306830 /* 25 Dec 2010 */
41 #define array_size(x) (sizeof(x) / sizeof(x[0]))
52 MA_MAIN_SWAP_CD_MULTI,
86 static int last_psx_w, last_psx_h, last_psx_bpp;
87 static int scaling, cpu_clock, cpu_clock_st, volume_boost, frameskip;
88 static char rom_fname_reload[MAXPATHLEN];
89 static char last_selected_fname[MAXPATHLEN];
90 static int warned_about_bios, region, in_type_sel1, in_type_sel2;
92 static int memcard1_sel, memcard2_sel;
94 int soft_scaling, analog_deadzone; // for Caanoo
97 #ifdef __ARM_ARCH_7A__
98 #define DEFAULT_PSX_CLOCK 57
99 #define DEFAULT_PSX_CLOCK_S "57"
101 #define DEFAULT_PSX_CLOCK 50
102 #define DEFAULT_PSX_CLOCK_S "50"
106 extern int iUseReverb;
107 extern int iUseInterpolation;
111 static const char *bioses[24];
112 static const char *gpu_plugins[16];
113 static const char *spu_plugins[16];
114 static const char *memcards[32];
115 static int bios_sel, gpu_plugsel, spu_plugsel;
118 static int min(int x, int y) { return x < y ? x : y; }
119 static int max(int x, int y) { return x > y ? x : y; }
121 void emu_make_path(char *buff, const char *end, int size)
125 end_len = strlen(end);
126 pos = plat_get_root_dir(buff, size);
127 strncpy(buff + pos, end, size - pos);
129 if (pos + end_len > size - 1)
130 printf("Warning: path truncated: %s\n", buff);
133 static int emu_check_save_file(int slot, int *time)
135 char fname[MAXPATHLEN];
139 ret = emu_check_state(slot);
140 if (ret != 0 || time == NULL)
141 return ret == 0 ? 1 : 0;
143 ret = get_state_filename(fname, sizeof(fname), slot);
147 ret = stat(fname, &status);
151 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
152 return 1; // probably bad rtc like on some Caanoos
154 *time = status.st_mtime;
159 static int emu_save_load_game(int load, int unused)
164 ret = emu_load_state(state_slot);
166 // reflect hle/bios mode from savestate
169 else if (bios_sel == 0 && bioses[1] != NULL)
170 // XXX: maybe find the right bios instead
174 ret = emu_save_state(state_slot);
179 // propagate menu settings to the emu vars
180 static void menu_sync_config(void)
182 static int allow_abs_only_old;
187 Config.PsxType = region - 1;
189 cycle_multiplier = 10000 / psx_clock;
191 switch (in_type_sel1) {
192 case 1: in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
193 case 2: in_type1 = PSE_PAD_TYPE_GUNCON; break;
194 default: in_type1 = PSE_PAD_TYPE_STANDARD;
196 switch (in_type_sel2) {
197 case 1: in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
198 case 2: in_type2 = PSE_PAD_TYPE_GUNCON; break;
199 default: in_type2 = PSE_PAD_TYPE_STANDARD;
201 if (in_evdev_allow_abs_only != allow_abs_only_old) {
203 allow_abs_only_old = in_evdev_allow_abs_only;
206 iVolume = 768 + 128 * volume_boost;
207 pl_rearmed_cbs.frameskip = frameskip - 1;
208 pl_timing_prepare(Config.PsxType);
211 static void menu_set_defconfig(void)
213 emu_set_default_config();
219 analog_deadzone = 50;
221 psx_clock = DEFAULT_PSX_CLOCK;
224 in_type_sel1 = in_type_sel2 = 0;
225 in_evdev_allow_abs_only = 0;
230 #define CE_CONFIG_STR(val) \
231 { #val, 0, Config.val }
233 #define CE_CONFIG_VAL(val) \
234 { #val, sizeof(Config.val), &Config.val }
236 #define CE_STR(val) \
239 #define CE_INTVAL(val) \
240 { #val, sizeof(val), &val }
242 #define CE_INTVAL_P(val) \
243 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
245 // 'versioned' var, used when defaults change
246 #define CE_CONFIG_STR_V(val, ver) \
247 { #val #ver, 0, Config.val }
249 #define CE_INTVAL_V(val, ver) \
250 { #val #ver, sizeof(val), &val }
252 #define CE_INTVAL_PV(val, ver) \
253 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
255 static const struct {
261 CE_CONFIG_STR_V(Gpu, 2),
263 // CE_CONFIG_STR(Cdr),
268 CE_CONFIG_VAL(Debug),
269 CE_CONFIG_VAL(PsxOut),
270 CE_CONFIG_VAL(SpuIrq),
271 CE_CONFIG_VAL(RCntFix),
272 CE_CONFIG_VAL(VSyncWA),
274 CE_CONFIG_VAL(CdrReschedule),
276 CE_INTVAL_V(scaling, 2),
277 CE_INTVAL(g_layer_x),
278 CE_INTVAL(g_layer_y),
279 CE_INTVAL(g_layer_w),
280 CE_INTVAL(g_layer_h),
282 CE_INTVAL(state_slot),
283 CE_INTVAL(cpu_clock),
285 CE_INTVAL(in_type_sel1),
286 CE_INTVAL(in_type_sel2),
287 CE_INTVAL(analog_deadzone),
288 CE_INTVAL_V(frameskip, 3),
289 CE_INTVAL_P(gpu_peops.iUseDither),
290 CE_INTVAL_P(gpu_peops.dwActFixes),
291 CE_INTVAL_P(gpu_unai.lineskip),
292 CE_INTVAL_P(gpu_unai.abe_hack),
293 CE_INTVAL_P(gpu_unai.no_light),
294 CE_INTVAL_P(gpu_unai.no_blend),
295 CE_INTVAL_P(gpu_neon.allow_interlace),
296 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
297 CE_INTVAL_P(gpu_peopsgl.iFilterType),
298 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
299 CE_INTVAL_P(gpu_peopsgl.iUseMask),
300 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
301 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
302 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
303 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
304 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
305 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
306 CE_INTVAL_V(iUseReverb, 3),
307 CE_INTVAL_V(iXAPitch, 3),
308 CE_INTVAL_V(iUseInterpolation, 3),
309 CE_INTVAL(warned_about_bios),
310 CE_INTVAL(in_evdev_allow_abs_only),
311 CE_INTVAL(volume_boost),
312 CE_INTVAL(psx_clock),
313 CE_INTVAL(new_dynarec_hacks),
314 CE_INTVAL(in_enable_vibration),
317 static char *get_cd_label(void)
319 static char trimlabel[33];
322 strncpy(trimlabel, CdromLabel, 32);
324 for (j = 31; j >= 0; j--)
325 if (trimlabel[j] == ' ')
331 static void make_cfg_fname(char *buf, size_t size, int is_game)
334 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
336 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
339 static void keys_write_all(FILE *f);
341 static int menu_write_config(int is_game)
343 char cfgfile[MAXPATHLEN];
347 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
348 f = fopen(cfgfile, "w");
350 printf("menu_write_config: failed to open: %s\n", cfgfile);
354 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
355 fprintf(f, "%s = ", config_data[i].name);
356 switch (config_data[i].len) {
358 fprintf(f, "%s\n", (char *)config_data[i].val);
361 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
364 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
367 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
370 printf("menu_write_config: unhandled len %d for %s\n",
371 config_data[i].len, config_data[i].name);
377 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
385 static void parse_str_val(char *cval, const char *src)
388 strncpy(cval, src, MAXPATHLEN);
389 cval[MAXPATHLEN - 1] = 0;
390 tmp = strchr(cval, '\n');
392 tmp = strchr(cval, '\r');
397 static void keys_load_all(const char *cfg);
399 static int menu_load_config(int is_game)
401 char cfgfile[MAXPATHLEN];
407 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
408 f = fopen(cfgfile, "r");
410 printf("menu_load_config: failed to open: %s\n", cfgfile);
414 fseek(f, 0, SEEK_END);
417 printf("bad size %ld: %s\n", size, cfgfile);
421 cfg = malloc(size + 1);
425 fseek(f, 0, SEEK_SET);
426 if (fread(cfg, 1, size, f) != size) {
427 printf("failed to read: %s\n", cfgfile);
432 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
436 tmp = strstr(cfg, config_data[i].name);
439 tmp += strlen(config_data[i].name);
440 if (strncmp(tmp, " = ", 3) != 0)
444 if (config_data[i].len == 0) {
445 parse_str_val(config_data[i].val, tmp);
450 val = strtoul(tmp, &tmp2, 16);
451 if (tmp2 == NULL || tmp == tmp2)
452 continue; // parse failed
454 switch (config_data[i].len) {
456 *(u8 *)config_data[i].val = val;
459 *(u16 *)config_data[i].val = val;
462 *(u32 *)config_data[i].val = val;
465 printf("menu_load_config: unhandled len %d for %s\n",
466 config_data[i].len, config_data[i].name);
472 char *tmp = strstr(cfg, "lastcdimg = ");
475 parse_str_val(last_selected_fname, tmp);
489 // caanoo old config compat hack
490 if (strcmp(Config.Gpu, "gpuPCSX4ALL.so") == 0)
491 strcpy(Config.Gpu, "gpu_unai.so");
494 for (i = bios_sel = 0; bioses[i] != NULL; i++)
495 if (strcmp(Config.Bios, bioses[i]) == 0)
496 { bios_sel = i; break; }
498 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
499 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
500 { gpu_plugsel = i; break; }
502 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
503 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
504 { spu_plugsel = i; break; }
509 // rrrr rggg gggb bbbb
510 static unsigned short fname2color(const char *fname)
512 static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z",
513 ".bz", ".znx", ".pbp", ".cbn" };
514 static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub",
515 ".table", ".index", ".sbi" };
516 const char *ext = strrchr(fname, '.');
521 for (i = 0; i < array_size(cdimg_exts); i++)
522 if (strcasecmp(ext, cdimg_exts[i]) == 0)
524 for (i = 0; i < array_size(other_exts); i++)
525 if (strcasecmp(ext, other_exts[i]) == 0)
530 static void draw_savestate_bg(int slot);
532 static const char *filter_exts[] = {
533 ".mp3", ".MP3", ".txt", ".htm", "html", ".jpg", ".pnd"
536 #define MENU_ALIGN_LEFT
537 #ifdef __ARM_ARCH_7A__ // assume hires device
543 #define menu_init menu_init_common
544 #include "common/menu.c"
547 // a bit of black magic here
548 static void draw_savestate_bg(int slot)
550 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
552 char fname[MAXPATHLEN];
559 ret = get_state_filename(fname, sizeof(fname), slot);
563 f = gzopen(fname, "rb");
567 if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
568 fprintf(stderr, "gzseek failed\n");
573 gpu = malloc(sizeof(*gpu));
579 ret = gzread(f, gpu, sizeof(*gpu));
581 if (ret != sizeof(*gpu)) {
582 fprintf(stderr, "gzread failed\n");
586 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
588 if (gpu->ulStatus & 0x800000)
589 goto out; // disabled
591 x = gpu->ulControl[5] & 0x3ff;
592 y = (gpu->ulControl[5] >> 10) & 0x1ff;
593 s = (u16 *)gpu->psxVRam + y * 1024 + (x & ~1);
594 w = psx_widths[(gpu->ulStatus >> 16) & 7];
595 tmp = gpu->ulControl[7];
596 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
597 if (gpu->ulStatus & 0x80000) // doubleheight
600 x = max(0, g_menuscreen_w - w) & ~3;
601 y = max(0, g_menuscreen_h / 2 - h / 2);
602 w = min(g_menuscreen_w, w);
603 h = min(g_menuscreen_h, h);
604 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
606 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
607 if (gpu->ulStatus & 0x200000)
608 bgr888_to_rgb565(d, s, w * 3);
610 bgr555_to_rgb565(d, s, w * 2);
612 // darken this so that menu text is visible
613 if (g_menuscreen_w - w < 320)
614 menu_darken_bg(d, d, w * 2, 0);
621 // -------------- key config --------------
623 me_bind_action me_ctrl_actions[] =
625 { "UP ", 1 << DKEY_UP},
626 { "DOWN ", 1 << DKEY_DOWN },
627 { "LEFT ", 1 << DKEY_LEFT },
628 { "RIGHT ", 1 << DKEY_RIGHT },
629 { "TRIANGLE", 1 << DKEY_TRIANGLE },
630 { "CIRCLE ", 1 << DKEY_CIRCLE },
631 { "CROSS ", 1 << DKEY_CROSS },
632 { "SQUARE ", 1 << DKEY_SQUARE },
633 { "L1 ", 1 << DKEY_L1 },
634 { "R1 ", 1 << DKEY_R1 },
635 { "L2 ", 1 << DKEY_L2 },
636 { "R2 ", 1 << DKEY_R2 },
637 { "L3 ", 1 << DKEY_L3 },
638 { "R3 ", 1 << DKEY_R3 },
639 { "START ", 1 << DKEY_START },
640 { "SELECT ", 1 << DKEY_SELECT },
644 me_bind_action emuctrl_actions[] =
646 { "Save State ", 1 << SACTION_SAVE_STATE },
647 { "Load State ", 1 << SACTION_LOAD_STATE },
648 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
649 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
650 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
651 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
652 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
653 #ifdef __ARM_ARCH_7A__ /* XXX */
654 { "Minimize ", 1 << SACTION_MINIMIZE },
656 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
657 { "Gun A button ", 1 << SACTION_GUN_A },
658 { "Gun B button ", 1 << SACTION_GUN_B },
659 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
660 #ifndef __ARM_ARCH_7A__ /* XXX */
661 { "Volume Up ", 1 << SACTION_VOLUME_UP },
662 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
667 static char *mystrip(char *str)
672 for (i = 0; i < len; i++)
673 if (str[i] != ' ') break;
674 if (i > 0) memmove(str, str + i, len - i + 1);
677 for (i = len - 1; i >= 0; i--)
678 if (str[i] != ' ') break;
684 static void get_line(char *d, size_t size, const char *s)
689 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
700 static void keys_write_all(FILE *f)
704 for (d = 0; d < IN_MAX_DEVS; d++)
706 const int *binds = in_get_dev_binds(d);
707 const char *name = in_get_dev_name(d, 0, 0);
710 if (binds == NULL || name == NULL)
713 fprintf(f, "binddev = %s\n", name);
714 in_get_config(d, IN_CFG_BIND_COUNT, &count);
716 for (k = 0; k < count; k++)
721 act[0] = act[31] = 0;
722 name = in_get_key_name(d, k);
724 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
725 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
726 mask = me_ctrl_actions[i].mask;
728 strncpy(act, me_ctrl_actions[i].name, 31);
729 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
732 mask = me_ctrl_actions[i].mask << 16;
734 strncpy(act, me_ctrl_actions[i].name, 31);
735 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
740 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
741 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
742 mask = emuctrl_actions[i].mask;
744 strncpy(act, emuctrl_actions[i].name, 31);
745 fprintf(f, "bind %s = %s\n", name, mystrip(act));
751 for (k = 0; k < array_size(in_adev); k++)
754 fprintf(f, "bind_analog = %d\n", k);
759 static int parse_bind_val(const char *val, int *type)
763 *type = IN_BINDTYPE_NONE;
767 if (strncasecmp(val, "player", 6) == 0)
769 int player, shift = 0;
770 player = atoi(val + 6) - 1;
772 if ((unsigned int)player > 1)
777 *type = IN_BINDTYPE_PLAYER12;
778 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
779 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
780 return me_ctrl_actions[i].mask << shift;
783 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
784 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
785 *type = IN_BINDTYPE_EMU;
786 return emuctrl_actions[i].mask;
793 static void keys_load_all(const char *cfg)
795 char dev[256], key[128], *act;
801 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
804 get_line(dev, sizeof(dev), p);
805 dev_id = in_config_parse_dev(dev);
807 printf("input: can't handle dev: %s\n", dev);
811 in_unbind_all(dev_id, -1, -1);
812 while ((p = strstr(p, "bind"))) {
813 if (strncmp(p, "binddev = ", 10) == 0)
816 if (strncmp(p, "bind_analog", 11) == 0) {
817 ret = sscanf(p, "bind_analog = %d", &bind);
820 printf("input: parse error: %16s..\n", p);
823 if ((unsigned int)bind >= array_size(in_adev)) {
824 printf("input: analog id %d out of range\n", bind);
827 in_adev[bind] = dev_id;
833 printf("input: parse error: %16s..\n", p);
837 get_line(key, sizeof(key), p);
838 act = strchr(key, '=');
840 printf("parse failed: %16s..\n", p);
848 bind = parse_bind_val(act, &bindtype);
849 if (bind != -1 && bind != 0) {
850 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
851 in_config_bind_key(dev_id, key, bind, bindtype);
854 lprintf("config: unhandled action \"%s\"\n", act);
860 static int key_config_loop_wrap(int id, int keys)
863 case MA_CTRL_PLAYER1:
864 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
866 case MA_CTRL_PLAYER2:
867 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
870 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
878 static const char *adevnames[IN_MAX_DEVS + 2];
879 static int stick_sel[2];
881 static menu_entry e_menu_keyconfig_analog[] =
883 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
884 mee_range(" X axis", 0, in_adev_axis[0][0], 0, 7),
885 mee_range(" Y axis", 0, in_adev_axis[0][1], 0, 7),
886 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
887 mee_range(" X axis", 0, in_adev_axis[1][0], 0, 7),
888 mee_range(" Y axis", 0, in_adev_axis[1][1], 0, 7),
892 static int key_config_analog(int id, int keys)
894 int i, d, count, sel = 0;
895 int sel2dev_map[IN_MAX_DEVS];
897 memset(adevnames, 0, sizeof(adevnames));
898 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
899 memset(stick_sel, 0, sizeof(stick_sel));
901 adevnames[0] = "None";
903 for (d = 0; d < IN_MAX_DEVS; d++)
905 const char *name = in_get_dev_name(d, 0, 1);
910 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
914 if (in_adev[0] == d) stick_sel[0] = i;
915 if (in_adev[1] == d) stick_sel[1] = i;
917 adevnames[i++] = name;
921 me_loop(e_menu_keyconfig_analog, &sel);
923 in_adev[0] = sel2dev_map[stick_sel[0]];
924 in_adev[1] = sel2dev_map[stick_sel[1]];
929 static const char *mgn_dev_name(int id, int *offs)
931 const char *name = NULL;
934 if (id == MA_CTRL_DEV_FIRST)
937 for (; it < IN_MAX_DEVS; it++) {
938 name = in_get_dev_name(it, 1, 1);
947 static const char *mgn_saveloadcfg(int id, int *offs)
952 static int mh_savecfg(int id, int keys)
954 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
955 me_update_msg("config saved");
957 me_update_msg("failed to write config");
962 static int mh_input_rescan(int id, int keys)
964 //menu_sync_config();
966 me_update_msg("rescan complete.");
971 static const char *men_in_type_sel[] = {
972 "Standard (SCPH-1080)",
973 "Analog (SCPH-1150)",
977 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
978 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
979 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
981 static menu_entry e_menu_keyconfig[] =
983 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
984 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
985 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
986 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
988 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
989 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
990 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
991 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
992 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
993 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
994 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
995 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
996 mee_handler ("Rescan devices:", mh_input_rescan),
998 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
999 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1000 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1001 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1002 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1003 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1004 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1008 static int menu_loop_keyconfig(int id, int keys)
1012 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1013 me_loop(e_menu_keyconfig, &sel);
1017 // ------------ gfx options menu ------------
1019 static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
1020 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1021 "using d-pad or move it using R+d-pad";
1022 static const char *men_dummy[] = { NULL };
1024 static int menu_loop_cscaler(int id, int keys)
1028 scaling = SCALE_CUSTOM;
1030 plat_gvideo_open(Config.PsxType);
1035 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1036 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1037 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1040 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
1041 if (inp & PBTN_UP) g_layer_y--;
1042 if (inp & PBTN_DOWN) g_layer_y++;
1043 if (inp & PBTN_LEFT) g_layer_x--;
1044 if (inp & PBTN_RIGHT) g_layer_x++;
1045 if (!(inp & PBTN_R)) {
1046 if (inp & PBTN_UP) g_layer_h += 2;
1047 if (inp & PBTN_DOWN) g_layer_h -= 2;
1048 if (inp & PBTN_LEFT) g_layer_w += 2;
1049 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1051 if (inp & (PBTN_MOK|PBTN_MBACK))
1054 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1055 if (g_layer_x < 0) g_layer_x = 0;
1056 if (g_layer_x > 640) g_layer_x = 640;
1057 if (g_layer_y < 0) g_layer_y = 0;
1058 if (g_layer_y > 420) g_layer_y = 420;
1059 if (g_layer_w < 160) g_layer_w = 160;
1060 if (g_layer_h < 60) g_layer_h = 60;
1061 if (g_layer_x + g_layer_w > 800)
1062 g_layer_w = 800 - g_layer_x;
1063 if (g_layer_y + g_layer_h > 480)
1064 g_layer_h = 480 - g_layer_y;
1066 plat_gvideo_open(Config.PsxType);
1070 plat_gvideo_close();
1075 static menu_entry e_menu_gfx_options[] =
1077 mee_enum ("Scaler", MA_OPT_SCALER, scaling, men_scaler),
1078 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1079 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
1080 // mee_onoff ("Vsync", 0, vsync, 1),
1081 mee_cust_h ("Setup custom scaler", MA_OPT_SCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1085 static int menu_loop_gfx_options(int id, int keys)
1089 me_loop(e_menu_gfx_options, &sel);
1095 void menu_set_filter_list(void *filters)
1099 i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
1100 e_menu_gfx_options[i].data = filters;
1101 me_enable(e_menu_gfx_options, MA_OPT_FILTERING, filters != NULL);
1104 // ------------ bios/plugins ------------
1108 static const char h_gpu_neon[] = "Configure built-in NEON GPU plugin";
1109 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1111 static menu_entry e_menu_plugin_gpu_neon[] =
1113 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1117 static int menu_loop_plugin_gpu_neon(int id, int keys)
1120 me_loop(e_menu_plugin_gpu_neon, &sel);
1126 static menu_entry e_menu_plugin_gpu_unai[] =
1128 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1129 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1130 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1131 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1135 static int menu_loop_plugin_gpu_unai(int id, int keys)
1138 me_loop(e_menu_plugin_gpu_unai, &sel);
1142 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1143 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1144 static const char h_gpu_1[] = "Capcom fighting games";
1145 static const char h_gpu_2[] = "Black screens in Lunar";
1146 static const char h_gpu_3[] = "Compatibility mode";
1147 static const char h_gpu_6[] = "Pandemonium 2";
1148 //static const char h_gpu_7[] = "Skip every second frame";
1149 static const char h_gpu_8[] = "Needed by Dark Forces";
1150 static const char h_gpu_9[] = "better g-colors, worse textures";
1151 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1153 static menu_entry e_menu_plugin_gpu_peops[] =
1155 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1156 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1157 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1158 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1159 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1160 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1161 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1162 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1163 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1164 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1168 static int menu_loop_plugin_gpu_peops(int id, int keys)
1171 me_loop(e_menu_plugin_gpu_peops, &sel);
1175 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1176 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1177 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1179 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1181 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1182 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1183 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1184 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1185 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1186 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1187 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1188 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1189 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1190 mee_label ("Fixes/hacks:"),
1191 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1192 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1193 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1194 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1195 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1196 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1197 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1198 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1199 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1200 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1201 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1205 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1208 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1212 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1213 static const char h_spu_volboost[] = "Large values cause distortion";
1215 static menu_entry e_menu_plugin_spu[] =
1217 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1218 mee_onoff ("Reverb", 0, iUseReverb, 2),
1219 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
1220 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
1224 static int menu_loop_plugin_spu(int id, int keys)
1227 me_loop(e_menu_plugin_spu, &sel);
1231 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1232 "savestates and can't be changed there. Must save\n"
1233 "config and reload the game for change to take effect";
1234 static const char h_plugin_gpu[] =
1236 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1241 "is Pete's soft GPU, slow but accurate\n"
1242 "gpuPCSX4ALL is GPU from PCSX4ALL, fast but glitchy\n"
1243 "gpuGLES Pete's hw GPU, uses 3D chip but is glitchy\n"
1244 "must save config and reload the game if changed";
1245 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1246 "must save config and reload the game if changed";
1247 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1248 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1249 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team GPU plugin";
1250 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1252 static menu_entry e_menu_plugin_options[] =
1254 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1255 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1256 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1258 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1260 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1261 mee_handler_h ("Configure PCSX4ALL GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1262 mee_handler_h ("Configure GLES GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1263 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1267 static menu_entry e_menu_main2[];
1269 static int menu_loop_plugin_options(int id, int keys)
1272 me_loop(e_menu_plugin_options, &sel);
1274 // sync BIOS/plugins
1275 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1276 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1277 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1278 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1283 // ------------ adv options menu ------------
1285 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1286 "(lower value - less work for the emu, may be faster)";
1287 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1288 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1289 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1291 static menu_entry e_menu_speed_hacks[] =
1293 mee_range_h ("PSX CPU clock, %%", 0, psx_clock, 1, 500, h_cfg_psxclk),
1294 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1295 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1296 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1300 static int menu_loop_speed_hacks(int id, int keys)
1303 me_loop(e_menu_speed_hacks, &sel);
1307 static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
1308 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1309 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1310 "(green: normal, red: fmod, blue: noise)";
1311 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1312 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1313 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1314 "(proper .cue/.bin dump is needed otherwise)";
1315 static const char h_cfg_sio[] = "You should not need this, breaks games";
1316 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1317 static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1318 "(timing hack, breaks other games)";
1319 static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix\n"
1320 "(timing hack, breaks other games)";
1321 static const char h_cfg_cdrr[] = "Compatibility tweak (CD timing hack, breaks FMVs)";
1322 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1323 "Might be useful to overcome some dynarec bugs";
1324 static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
1325 "must reload game for any change to take effect";
1327 static menu_entry e_menu_adv_options[] =
1329 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1330 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1331 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1332 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1333 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1334 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1335 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1336 //mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1337 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
1338 mee_enum_h ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
1339 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1340 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1344 static int menu_loop_adv_options(int id, int keys)
1347 me_loop(e_menu_adv_options, &sel);
1351 // ------------ options menu ------------
1353 static int mh_restore_defaults(int id, int keys)
1355 menu_set_defconfig();
1356 me_update_msg("defaults restored");
1360 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1361 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1363 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1364 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1365 "loading state or both";
1367 static const char h_restore_def[] = "Switches back to default / recommended\n"
1369 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1371 static menu_entry e_menu_options[] =
1373 // mee_range ("Save slot", 0, state_slot, 0, 9),
1374 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1375 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1376 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1377 mee_enum ("Region", 0, region, men_region),
1378 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1379 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1380 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1381 mee_handler ("[Advanced]", menu_loop_adv_options),
1382 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1383 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1384 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1388 static int menu_loop_options(int id, int keys)
1393 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1394 e_menu_options[i].enabled = cpu_clock_st > 0 ? 1 : 0;
1395 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1397 me_loop(e_menu_options, &sel);
1402 // ------------ debug menu ------------
1404 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1406 int w = min(g_menuscreen_w, 1024);
1407 int h = min(g_menuscreen_h, 512);
1408 u16 *d = g_menuscreen_ptr;
1409 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1413 gpuf->ulFreezeVersion = 1;
1414 if (GPU_freeze != NULL)
1415 GPU_freeze(1, gpuf);
1417 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1418 bgr555_to_rgb565(d, s, w * 2);
1420 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1421 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1422 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1423 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1424 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1427 static void debug_menu_loop(void)
1429 int inp, df_x = 0, df_y = 0;
1432 gpuf = malloc(sizeof(*gpuf));
1439 draw_frame_debug(gpuf, df_x, df_y);
1442 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1443 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 10);
1444 if (inp & PBTN_MBACK) break;
1445 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1446 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1447 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x--; }
1448 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x++; }
1454 // --------- memcard manager ---------
1456 static void draw_mc_icon(int dx, int dy, const u16 *s)
1461 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1463 for (y = 0; y < 16; y++, s += 16) {
1464 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1465 for (x = 0; x < 16; x++) {
1467 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1468 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1474 static void draw_mc_bg(void)
1476 McdBlock *blocks1, *blocks2;
1480 blocks1 = malloc(15 * sizeof(blocks1[0]));
1481 blocks2 = malloc(15 * sizeof(blocks1[0]));
1482 if (blocks1 == NULL || blocks2 == NULL)
1485 for (i = 0; i < 15; i++) {
1486 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1487 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1492 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1494 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1498 maxicons = g_menuscreen_h / 32;
1501 row2 = g_menuscreen_w / 2;
1502 for (i = 0; i < maxicons; i++) {
1503 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1504 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1506 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1507 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1510 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1518 static void handle_memcard_sel(void)
1521 if (memcard1_sel != 0)
1522 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1524 if (memcard2_sel != 0)
1525 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1526 LoadMcds(Config.Mcd1, Config.Mcd2);
1530 static menu_entry e_memcard_options[] =
1532 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1533 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1537 static int menu_loop_memcards(int id, int keys)
1543 memcard1_sel = memcard2_sel = 0;
1544 p = strrchr(Config.Mcd1, '/');
1546 for (i = 0; memcards[i] != NULL; i++)
1547 if (strcmp(p + 1, memcards[i]) == 0)
1548 { memcard1_sel = i; break; }
1549 p = strrchr(Config.Mcd2, '/');
1551 for (i = 0; memcards[i] != NULL; i++)
1552 if (strcmp(p + 1, memcards[i]) == 0)
1553 { memcard2_sel = i; break; }
1555 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1557 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1562 // --------- main menu help ----------
1564 static void menu_bios_warn(void)
1567 static const char msg[] =
1568 "You don't seem to have copied any BIOS\n"
1570 #ifdef __ARM_ARCH_7A__ // XXX
1571 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1573 "pcsx_rearmed/bios/\n\n"
1575 "While many games work fine with fake\n"
1576 "(HLE) BIOS, others (like MGS and FF8)\n"
1577 "require BIOS to work.\n"
1578 "After copying the file, you'll also need\n"
1579 "to select it in the emu's menu:\n"
1580 "options->[BIOS/Plugins]\n\n"
1581 "The file is usually named SCPH1001.BIN,\n"
1582 "but other not compressed files can be\n"
1584 "Press %s or %s to continue";
1585 char tmp_msg[sizeof(msg) + 64];
1587 snprintf(tmp_msg, sizeof(tmp_msg), msg,
1588 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1591 draw_menu_message(tmp_msg, NULL);
1593 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1594 if (inp & (PBTN_MBACK|PBTN_MOK))
1599 // ------------ main menu ------------
1603 static void draw_frame_main(void)
1612 if (CdromId[0] != 0) {
1613 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1614 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1615 Config.HLE ? "HLE" : "BIOS");
1616 smalltext_out16(4, 1, buff, 0x105f);
1620 capacity = plat_get_bat_capacity();
1622 tmp = localtime(<ime);
1623 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1624 if (capacity >= 0) {
1625 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1630 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1634 static void draw_frame_credits(void)
1636 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1639 static const char credits_text[] =
1641 "(C) 1999-2003 PCSX Team\n"
1642 "(C) 2005-2009 PCSX-df Team\n"
1643 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1644 "ARM recompiler (C) 2009-2011 Ari64\n"
1646 "ARM NEON GPU (c) 2011-2012 Exophase\n"
1648 "PEOpS GPU and SPU by Pete Bernert\n"
1649 " and the P.E.Op.S. team\n"
1650 "PCSX4ALL plugin by PCSX4ALL team\n"
1651 " Chui, Franxis, Unai\n\n"
1652 "integration, optimization and\n"
1653 " frontend (C) 2010-2012 notaz\n";
1655 static int reset_game(void)
1658 if (bios_sel == 0 && !Config.HLE)
1664 if (CheckCdrom() != -1) {
1670 static int reload_plugins(const char *cdimg)
1676 set_cd_image(cdimg);
1678 pcnt_hook_plugins();
1680 if (OpenPlugins() == -1) {
1681 me_update_msg("failed to open plugins");
1684 plugin_call_rearmed_cbs();
1686 cdrIsoMultidiskCount = 1;
1688 CdromLabel[0] = '\0';
1693 static int run_bios(void)
1699 if (reload_plugins(NULL) != 0)
1707 static int run_exe(void)
1711 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1716 if (reload_plugins(NULL) != 0)
1720 if (Load(fname) != 0) {
1721 me_update_msg("exe load failed, bad file?");
1730 static int run_cd_image(const char *fname)
1733 reload_plugins(fname);
1735 // always autodetect, menu_sync_config will override as needed
1738 if (CheckCdrom() == -1) {
1739 // Only check the CD if we are starting the console with a CD
1741 me_update_msg("unsupported/invalid CD image");
1747 // Read main executable directly from CDRom and start it
1748 if (LoadCdrom() == -1) {
1750 me_update_msg("failed to load CD image");
1755 snprintf(hud_msg, sizeof(hud_msg), "Booting up...");
1760 static int romsel_run(void)
1762 int prev_gpu, prev_spu;
1765 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1769 printf("selected file: %s\n", fname);
1771 new_dynarec_clear_full();
1773 if (run_cd_image(fname) != 0)
1776 prev_gpu = gpu_plugsel;
1777 prev_spu = spu_plugsel;
1778 if (menu_load_config(1) != 0)
1779 menu_load_config(0);
1781 // check for plugin changes, have to repeat
1782 // loading if game config changed plugins to reload them
1783 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1784 printf("plugin change detected, reloading plugins..\n");
1785 if (run_cd_image(fname) != 0)
1790 printf("note: running without BIOS, expect compatibility problems\n");
1792 strcpy(last_selected_fname, rom_fname_reload);
1796 static int swap_cd_image(void)
1800 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1804 printf("selected file: %s\n", fname);
1807 CdromLabel[0] = '\0';
1809 set_cd_image(fname);
1810 if (ReloadCdromPlugin() < 0) {
1811 me_update_msg("failed to load cdr plugin");
1814 if (CDR_open() < 0) {
1815 me_update_msg("failed to open cdr plugin");
1819 SetCdOpenCaseTime(time(NULL) + 2);
1822 strcpy(last_selected_fname, rom_fname_reload);
1826 static int swap_cd_multidisk(void)
1828 cdrIsoMultidiskSelect++;
1830 CdromLabel[0] = '\0';
1833 if (CDR_open() < 0) {
1834 me_update_msg("failed to open cdr plugin");
1838 SetCdOpenCaseTime(time(NULL) + 2);
1844 static int main_menu_handler(int id, int keys)
1848 case MA_MAIN_RESUME_GAME:
1852 case MA_MAIN_SAVE_STATE:
1854 return menu_loop_savestate(0);
1856 case MA_MAIN_LOAD_STATE:
1858 return menu_loop_savestate(1);
1860 case MA_MAIN_RESET_GAME:
1861 if (ready_to_go && reset_game() == 0)
1864 case MA_MAIN_LOAD_ROM:
1865 if (romsel_run() == 0)
1868 case MA_MAIN_SWAP_CD:
1869 if (swap_cd_image() == 0)
1872 case MA_MAIN_SWAP_CD_MULTI:
1873 if (swap_cd_multidisk() == 0)
1876 case MA_MAIN_RUN_BIOS:
1877 if (run_bios() == 0)
1880 case MA_MAIN_RUN_EXE:
1884 case MA_MAIN_CREDITS:
1885 draw_menu_message(credits_text, draw_frame_credits);
1886 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1892 lprintf("%s: something unknown selected\n", __FUNCTION__);
1899 static menu_entry e_menu_main2[] =
1901 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1902 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
1903 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1904 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
1905 mee_handler ("Memcard manager", menu_loop_memcards),
1909 static int main_menu2_handler(int id, int keys)
1913 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
1914 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
1915 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1917 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1920 static const char h_extra[] = "Change CD, manage memcards..\n";
1922 static menu_entry e_menu_main[] =
1926 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1927 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1928 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1929 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1930 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1931 mee_handler ("Options", menu_loop_options),
1932 mee_handler ("Controls", menu_loop_keyconfig),
1933 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
1934 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1935 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1939 // ----------------------------
1941 static void menu_leave_emu(void);
1943 void menu_loop(void)
1949 if (bioses[1] == NULL && !warned_about_bios) {
1951 warned_about_bios = 1;
1954 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1955 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1956 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
1957 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1959 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1962 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
1963 } while (!ready_to_go);
1965 /* wait until menu, ok, back is released */
1966 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1969 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1974 static int qsort_strcmp(const void *p1, const void *p2)
1976 char * const *s1 = (char * const *)p1;
1977 char * const *s2 = (char * const *)p2;
1978 return strcasecmp(*s1, *s2);
1981 static void scan_bios_plugins(void)
1983 char fname[MAXPATHLEN];
1985 int bios_i, gpu_i, spu_i, mc_i;
1990 gpu_plugins[0] = "builtin_gpu";
1991 spu_plugins[0] = "builtin_spu";
1992 memcards[0] = "(none)";
1993 bios_i = gpu_i = spu_i = mc_i = 1;
1995 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1996 dir = opendir(fname);
1998 perror("scan_bios_plugins bios opendir");
2013 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2016 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2017 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2018 printf("bad BIOS file: %s\n", ent->d_name);
2022 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2023 bioses[bios_i++] = strdup(ent->d_name);
2027 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2033 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2034 dir = opendir(fname);
2036 perror("scan_bios_plugins plugins opendir");
2050 p = strstr(ent->d_name, ".so");
2054 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2055 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2057 fprintf(stderr, "%s\n", dlerror());
2061 // now what do we have here?
2062 tmp = dlsym(h, "GPUinit");
2065 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2066 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2070 tmp = dlsym(h, "SPUinit");
2073 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2074 spu_plugins[spu_i++] = strdup(ent->d_name);
2078 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2085 dir = opendir("." MEMCARD_DIR);
2087 perror("scan_bios_plugins memcards opendir");
2102 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2105 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2106 if (stat(fname, &st) != 0) {
2107 printf("bad memcard file: %s\n", ent->d_name);
2111 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2112 memcards[mc_i++] = strdup(ent->d_name);
2116 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2120 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2125 void menu_init(void)
2127 char buff[MAXPATHLEN];
2129 strcpy(last_selected_fname, "/media");
2131 cpu_clock_st = cpu_clock = plat_cpu_clock_get();
2133 scan_bios_plugins();
2136 menu_set_defconfig();
2137 menu_load_config(0);
2142 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2143 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2144 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2145 fprintf(stderr, "OOM\n");
2149 emu_make_path(buff, "skin/background.png", sizeof(buff));
2150 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2152 #ifndef __ARM_ARCH_7A__ /* XXX */
2153 me_enable(e_menu_gfx_options, MA_OPT_SCALER, 0);
2154 me_enable(e_menu_gfx_options, MA_OPT_FILTERING, 0);
2155 me_enable(e_menu_gfx_options, MA_OPT_SCALER_C, 0);
2156 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2158 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, 0);
2159 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
2160 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2164 void menu_notify_mode_change(int w, int h, int bpp)
2173 // XXX: should really menu code cotrol the layer size?
2176 g_layer_w = w; g_layer_h = h;
2180 if (h > g_menuscreen_h || (240 < h && h <= 360))
2181 goto fractional_4_3;
2183 // 4:3 that prefers integer scaling
2184 imult = g_menuscreen_h / h;
2185 g_layer_w = w * imult;
2186 g_layer_h = h * imult;
2187 mult = (float)g_layer_w / (float)g_layer_h;
2188 if (mult < 1.25f || mult > 1.666f)
2189 g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2190 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2195 mult = 240.0f / (float)h * 4.0f / 3.0f;
2198 g_layer_w = mult * (float)g_menuscreen_h;
2199 g_layer_h = g_menuscreen_h;
2200 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2203 case SCALE_FULLSCREEN:
2204 g_layer_w = g_menuscreen_w;
2205 g_layer_h = g_menuscreen_h;
2212 g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2213 g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2214 if (g_layer_x < 0) g_layer_x = 0;
2215 if (g_layer_y < 0) g_layer_y = 0;
2216 if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2217 if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2220 static void menu_leave_emu(void)
2222 if (GPU_close != NULL) {
2223 int ret = GPU_close();
2225 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2228 plat_video_menu_enter(ready_to_go);
2230 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2231 if (pl_vout_buf != NULL && ready_to_go) {
2232 int x = max(0, g_menuscreen_w - last_psx_w);
2233 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2234 int w = min(g_menuscreen_w, last_psx_w);
2235 int h = min(g_menuscreen_h, last_psx_h);
2236 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2237 char *s = pl_vout_buf;
2239 if (last_psx_bpp == 16) {
2240 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w * 2)
2241 menu_darken_bg(d, s, w, 0);
2244 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w * 3) {
2245 bgr888_to_rgb565(d, s, w * 3);
2246 menu_darken_bg(d, d, w, 0);
2252 cpu_clock = plat_cpu_clock_get();
2255 void menu_prepare_emu(void)
2257 R3000Acpu *prev_cpu = psxCpu;
2259 plat_video_menu_leave();
2261 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2263 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2264 if (psxCpu != prev_cpu)
2265 // note that this does not really reset, just clears drc caches
2268 // core doesn't care about Config.Cdda changes,
2269 // so handle them manually here
2275 plat_cpu_clock_apply(cpu_clock);
2277 // push config to GPU plugin
2278 plugin_call_rearmed_cbs();
2280 if (GPU_open != NULL) {
2281 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2283 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2289 void me_update_msg(const char *msg)
2291 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2292 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2294 menu_error_time = plat_get_ticks_ms();
2295 lprintf("msg: %s\n", menu_error_msg);
2298 void menu_finish(void)
2300 plat_cpu_clock_apply(cpu_clock_st);