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;
109 extern int iSPUIRQWait;
110 extern int iUseTimer;
113 static const char *bioses[24];
114 static const char *gpu_plugins[16];
115 static const char *spu_plugins[16];
116 static const char *memcards[32];
117 static int bios_sel, gpu_plugsel, spu_plugsel;
120 static int min(int x, int y) { return x < y ? x : y; }
121 static int max(int x, int y) { return x > y ? x : y; }
123 void emu_make_path(char *buff, const char *end, int size)
127 end_len = strlen(end);
128 pos = plat_get_root_dir(buff, size);
129 strncpy(buff + pos, end, size - pos);
131 if (pos + end_len > size - 1)
132 printf("Warning: path truncated: %s\n", buff);
135 static int emu_check_save_file(int slot, int *time)
137 char fname[MAXPATHLEN];
141 ret = emu_check_state(slot);
142 if (ret != 0 || time == NULL)
143 return ret == 0 ? 1 : 0;
145 ret = get_state_filename(fname, sizeof(fname), slot);
149 ret = stat(fname, &status);
153 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
154 return 1; // probably bad rtc like on some Caanoos
156 *time = status.st_mtime;
161 static int emu_save_load_game(int load, int unused)
166 ret = emu_load_state(state_slot);
168 // reflect hle/bios mode from savestate
171 else if (bios_sel == 0 && bioses[1] != NULL)
172 // XXX: maybe find the right bios instead
176 ret = emu_save_state(state_slot);
181 // propagate menu settings to the emu vars
182 static void menu_sync_config(void)
184 static int allow_abs_only_old;
189 Config.PsxType = region - 1;
191 cycle_multiplier = 10000 / psx_clock;
193 switch (in_type_sel1) {
194 case 1: in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
195 case 2: in_type1 = PSE_PAD_TYPE_GUNCON; break;
196 default: in_type1 = PSE_PAD_TYPE_STANDARD;
198 switch (in_type_sel2) {
199 case 1: in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
200 case 2: in_type2 = PSE_PAD_TYPE_GUNCON; break;
201 default: in_type2 = PSE_PAD_TYPE_STANDARD;
203 if (in_evdev_allow_abs_only != allow_abs_only_old) {
205 allow_abs_only_old = in_evdev_allow_abs_only;
208 iVolume = 768 + 128 * volume_boost;
209 pl_rearmed_cbs.frameskip = frameskip - 1;
210 pl_timing_prepare(Config.PsxType);
213 static void menu_set_defconfig(void)
215 emu_set_default_config();
221 analog_deadzone = 50;
223 psx_clock = DEFAULT_PSX_CLOCK;
226 in_type_sel1 = in_type_sel2 = 0;
227 in_evdev_allow_abs_only = 0;
232 #define CE_CONFIG_STR(val) \
233 { #val, 0, Config.val }
235 #define CE_CONFIG_VAL(val) \
236 { #val, sizeof(Config.val), &Config.val }
238 #define CE_STR(val) \
241 #define CE_INTVAL(val) \
242 { #val, sizeof(val), &val }
244 #define CE_INTVAL_P(val) \
245 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
247 // 'versioned' var, used when defaults change
248 #define CE_CONFIG_STR_V(val, ver) \
249 { #val #ver, 0, Config.val }
251 #define CE_INTVAL_V(val, ver) \
252 { #val #ver, sizeof(val), &val }
254 #define CE_INTVAL_PV(val, ver) \
255 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
257 static const struct {
263 CE_CONFIG_STR_V(Gpu, 2),
265 // CE_CONFIG_STR(Cdr),
270 CE_CONFIG_VAL(Debug),
271 CE_CONFIG_VAL(PsxOut),
272 CE_CONFIG_VAL(SpuIrq),
273 CE_CONFIG_VAL(RCntFix),
274 CE_CONFIG_VAL(VSyncWA),
276 CE_CONFIG_VAL(CdrReschedule),
278 CE_INTVAL_V(scaling, 2),
279 CE_INTVAL(g_layer_x),
280 CE_INTVAL(g_layer_y),
281 CE_INTVAL(g_layer_w),
282 CE_INTVAL(g_layer_h),
284 CE_INTVAL(state_slot),
285 CE_INTVAL(cpu_clock),
287 CE_INTVAL(in_type_sel1),
288 CE_INTVAL(in_type_sel2),
289 CE_INTVAL(analog_deadzone),
290 CE_INTVAL_V(frameskip, 3),
291 CE_INTVAL_P(gpu_peops.iUseDither),
292 CE_INTVAL_P(gpu_peops.dwActFixes),
293 CE_INTVAL_P(gpu_unai.lineskip),
294 CE_INTVAL_P(gpu_unai.abe_hack),
295 CE_INTVAL_P(gpu_unai.no_light),
296 CE_INTVAL_P(gpu_unai.no_blend),
297 CE_INTVAL_P(gpu_neon.allow_interlace),
298 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
299 CE_INTVAL_P(gpu_peopsgl.iFilterType),
300 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
301 CE_INTVAL_P(gpu_peopsgl.iUseMask),
302 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
303 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
304 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
305 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
306 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
307 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
308 CE_INTVAL_V(iUseReverb, 3),
309 CE_INTVAL_V(iXAPitch, 3),
310 CE_INTVAL_V(iUseInterpolation, 3),
311 CE_INTVAL_V(iSPUIRQWait, 3),
312 CE_INTVAL_V(iUseTimer, 3),
313 CE_INTVAL(warned_about_bios),
314 CE_INTVAL(in_evdev_allow_abs_only),
315 CE_INTVAL(volume_boost),
316 CE_INTVAL(psx_clock),
317 CE_INTVAL(new_dynarec_hacks),
318 CE_INTVAL(in_enable_vibration),
321 static char *get_cd_label(void)
323 static char trimlabel[33];
326 strncpy(trimlabel, CdromLabel, 32);
328 for (j = 31; j >= 0; j--)
329 if (trimlabel[j] == ' ')
335 static void make_cfg_fname(char *buf, size_t size, int is_game)
338 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
340 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
343 static void keys_write_all(FILE *f);
345 static int menu_write_config(int is_game)
347 char cfgfile[MAXPATHLEN];
351 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
352 f = fopen(cfgfile, "w");
354 printf("menu_write_config: failed to open: %s\n", cfgfile);
358 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
359 fprintf(f, "%s = ", config_data[i].name);
360 switch (config_data[i].len) {
362 fprintf(f, "%s\n", (char *)config_data[i].val);
365 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
368 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
371 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
374 printf("menu_write_config: unhandled len %d for %s\n",
375 config_data[i].len, config_data[i].name);
381 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
389 static void parse_str_val(char *cval, const char *src)
392 strncpy(cval, src, MAXPATHLEN);
393 cval[MAXPATHLEN - 1] = 0;
394 tmp = strchr(cval, '\n');
396 tmp = strchr(cval, '\r');
401 static void keys_load_all(const char *cfg);
403 static int menu_load_config(int is_game)
405 char cfgfile[MAXPATHLEN];
411 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
412 f = fopen(cfgfile, "r");
414 printf("menu_load_config: failed to open: %s\n", cfgfile);
418 fseek(f, 0, SEEK_END);
421 printf("bad size %ld: %s\n", size, cfgfile);
425 cfg = malloc(size + 1);
429 fseek(f, 0, SEEK_SET);
430 if (fread(cfg, 1, size, f) != size) {
431 printf("failed to read: %s\n", cfgfile);
436 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
440 tmp = strstr(cfg, config_data[i].name);
443 tmp += strlen(config_data[i].name);
444 if (strncmp(tmp, " = ", 3) != 0)
448 if (config_data[i].len == 0) {
449 parse_str_val(config_data[i].val, tmp);
454 val = strtoul(tmp, &tmp2, 16);
455 if (tmp2 == NULL || tmp == tmp2)
456 continue; // parse failed
458 switch (config_data[i].len) {
460 *(u8 *)config_data[i].val = val;
463 *(u16 *)config_data[i].val = val;
466 *(u32 *)config_data[i].val = val;
469 printf("menu_load_config: unhandled len %d for %s\n",
470 config_data[i].len, config_data[i].name);
476 char *tmp = strstr(cfg, "lastcdimg = ");
479 parse_str_val(last_selected_fname, tmp);
493 // caanoo old config compat hack
494 if (strcmp(Config.Gpu, "gpuPCSX4ALL.so") == 0)
495 strcpy(Config.Gpu, "gpu_unai.so");
498 for (i = bios_sel = 0; bioses[i] != NULL; i++)
499 if (strcmp(Config.Bios, bioses[i]) == 0)
500 { bios_sel = i; break; }
502 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
503 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
504 { gpu_plugsel = i; break; }
506 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
507 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
508 { spu_plugsel = i; break; }
513 // rrrr rggg gggb bbbb
514 static unsigned short fname2color(const char *fname)
516 static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z",
517 ".bz", ".znx", ".pbp", ".cbn" };
518 static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub",
519 ".table", ".index", ".sbi" };
520 const char *ext = strrchr(fname, '.');
525 for (i = 0; i < array_size(cdimg_exts); i++)
526 if (strcasecmp(ext, cdimg_exts[i]) == 0)
528 for (i = 0; i < array_size(other_exts); i++)
529 if (strcasecmp(ext, other_exts[i]) == 0)
534 static void draw_savestate_bg(int slot);
536 static const char *filter_exts[] = {
537 ".mp3", ".MP3", ".txt", ".htm", "html", ".jpg", ".pnd"
540 #define MENU_ALIGN_LEFT
541 #ifdef __ARM_ARCH_7A__ // assume hires device
547 #define menu_init menu_init_common
548 #include "common/menu.c"
551 // a bit of black magic here
552 static void draw_savestate_bg(int slot)
554 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
556 char fname[MAXPATHLEN];
563 ret = get_state_filename(fname, sizeof(fname), slot);
567 f = gzopen(fname, "rb");
571 if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
572 fprintf(stderr, "gzseek failed\n");
577 gpu = malloc(sizeof(*gpu));
583 ret = gzread(f, gpu, sizeof(*gpu));
585 if (ret != sizeof(*gpu)) {
586 fprintf(stderr, "gzread failed\n");
590 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
592 if (gpu->ulStatus & 0x800000)
593 goto out; // disabled
595 x = gpu->ulControl[5] & 0x3ff;
596 y = (gpu->ulControl[5] >> 10) & 0x1ff;
597 s = (u16 *)gpu->psxVRam + y * 1024 + (x & ~1);
598 w = psx_widths[(gpu->ulStatus >> 16) & 7];
599 tmp = gpu->ulControl[7];
600 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
601 if (gpu->ulStatus & 0x80000) // doubleheight
604 x = max(0, g_menuscreen_w - w) & ~3;
605 y = max(0, g_menuscreen_h / 2 - h / 2);
606 w = min(g_menuscreen_w, w);
607 h = min(g_menuscreen_h, h);
608 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
610 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
611 if (gpu->ulStatus & 0x200000)
612 bgr888_to_rgb565(d, s, w * 3);
614 bgr555_to_rgb565(d, s, w * 2);
616 // darken this so that menu text is visible
617 if (g_menuscreen_w - w < 320)
618 menu_darken_bg(d, d, w * 2, 0);
625 // -------------- key config --------------
627 me_bind_action me_ctrl_actions[] =
629 { "UP ", 1 << DKEY_UP},
630 { "DOWN ", 1 << DKEY_DOWN },
631 { "LEFT ", 1 << DKEY_LEFT },
632 { "RIGHT ", 1 << DKEY_RIGHT },
633 { "TRIANGLE", 1 << DKEY_TRIANGLE },
634 { "CIRCLE ", 1 << DKEY_CIRCLE },
635 { "CROSS ", 1 << DKEY_CROSS },
636 { "SQUARE ", 1 << DKEY_SQUARE },
637 { "L1 ", 1 << DKEY_L1 },
638 { "R1 ", 1 << DKEY_R1 },
639 { "L2 ", 1 << DKEY_L2 },
640 { "R2 ", 1 << DKEY_R2 },
641 { "L3 ", 1 << DKEY_L3 },
642 { "R3 ", 1 << DKEY_R3 },
643 { "START ", 1 << DKEY_START },
644 { "SELECT ", 1 << DKEY_SELECT },
648 me_bind_action emuctrl_actions[] =
650 { "Save State ", 1 << SACTION_SAVE_STATE },
651 { "Load State ", 1 << SACTION_LOAD_STATE },
652 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
653 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
654 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
655 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
656 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
657 #ifdef __ARM_ARCH_7A__ /* XXX */
658 { "Minimize ", 1 << SACTION_MINIMIZE },
660 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
661 { "Gun A button ", 1 << SACTION_GUN_A },
662 { "Gun B button ", 1 << SACTION_GUN_B },
663 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
664 #ifndef __ARM_ARCH_7A__ /* XXX */
665 { "Volume Up ", 1 << SACTION_VOLUME_UP },
666 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
671 static char *mystrip(char *str)
676 for (i = 0; i < len; i++)
677 if (str[i] != ' ') break;
678 if (i > 0) memmove(str, str + i, len - i + 1);
681 for (i = len - 1; i >= 0; i--)
682 if (str[i] != ' ') break;
688 static void get_line(char *d, size_t size, const char *s)
693 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
704 static void keys_write_all(FILE *f)
708 for (d = 0; d < IN_MAX_DEVS; d++)
710 const int *binds = in_get_dev_binds(d);
711 const char *name = in_get_dev_name(d, 0, 0);
714 if (binds == NULL || name == NULL)
717 fprintf(f, "binddev = %s\n", name);
718 in_get_config(d, IN_CFG_BIND_COUNT, &count);
720 for (k = 0; k < count; k++)
725 act[0] = act[31] = 0;
726 name = in_get_key_name(d, k);
728 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
729 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
730 mask = me_ctrl_actions[i].mask;
732 strncpy(act, me_ctrl_actions[i].name, 31);
733 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
736 mask = me_ctrl_actions[i].mask << 16;
738 strncpy(act, me_ctrl_actions[i].name, 31);
739 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
744 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
745 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
746 mask = emuctrl_actions[i].mask;
748 strncpy(act, emuctrl_actions[i].name, 31);
749 fprintf(f, "bind %s = %s\n", name, mystrip(act));
755 for (k = 0; k < array_size(in_adev); k++)
758 fprintf(f, "bind_analog = %d\n", k);
763 static int parse_bind_val(const char *val, int *type)
767 *type = IN_BINDTYPE_NONE;
771 if (strncasecmp(val, "player", 6) == 0)
773 int player, shift = 0;
774 player = atoi(val + 6) - 1;
776 if ((unsigned int)player > 1)
781 *type = IN_BINDTYPE_PLAYER12;
782 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
783 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
784 return me_ctrl_actions[i].mask << shift;
787 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
788 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
789 *type = IN_BINDTYPE_EMU;
790 return emuctrl_actions[i].mask;
797 static void keys_load_all(const char *cfg)
799 char dev[256], key[128], *act;
805 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
808 get_line(dev, sizeof(dev), p);
809 dev_id = in_config_parse_dev(dev);
811 printf("input: can't handle dev: %s\n", dev);
815 in_unbind_all(dev_id, -1, -1);
816 while ((p = strstr(p, "bind"))) {
817 if (strncmp(p, "binddev = ", 10) == 0)
820 if (strncmp(p, "bind_analog", 11) == 0) {
821 ret = sscanf(p, "bind_analog = %d", &bind);
824 printf("input: parse error: %16s..\n", p);
827 if ((unsigned int)bind >= array_size(in_adev)) {
828 printf("input: analog id %d out of range\n", bind);
831 in_adev[bind] = dev_id;
837 printf("input: parse error: %16s..\n", p);
841 get_line(key, sizeof(key), p);
842 act = strchr(key, '=');
844 printf("parse failed: %16s..\n", p);
852 bind = parse_bind_val(act, &bindtype);
853 if (bind != -1 && bind != 0) {
854 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
855 in_config_bind_key(dev_id, key, bind, bindtype);
858 lprintf("config: unhandled action \"%s\"\n", act);
864 static int key_config_loop_wrap(int id, int keys)
867 case MA_CTRL_PLAYER1:
868 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
870 case MA_CTRL_PLAYER2:
871 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
874 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
882 static const char *adevnames[IN_MAX_DEVS + 2];
883 static int stick_sel[2];
885 static menu_entry e_menu_keyconfig_analog[] =
887 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
888 mee_range(" X axis", 0, in_adev_axis[0][0], 0, 7),
889 mee_range(" Y axis", 0, in_adev_axis[0][1], 0, 7),
890 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
891 mee_range(" X axis", 0, in_adev_axis[1][0], 0, 7),
892 mee_range(" Y axis", 0, in_adev_axis[1][1], 0, 7),
896 static int key_config_analog(int id, int keys)
898 int i, d, count, sel = 0;
899 int sel2dev_map[IN_MAX_DEVS];
901 memset(adevnames, 0, sizeof(adevnames));
902 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
903 memset(stick_sel, 0, sizeof(stick_sel));
905 adevnames[0] = "None";
907 for (d = 0; d < IN_MAX_DEVS; d++)
909 const char *name = in_get_dev_name(d, 0, 1);
914 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
918 if (in_adev[0] == d) stick_sel[0] = i;
919 if (in_adev[1] == d) stick_sel[1] = i;
921 adevnames[i++] = name;
925 me_loop(e_menu_keyconfig_analog, &sel);
927 in_adev[0] = sel2dev_map[stick_sel[0]];
928 in_adev[1] = sel2dev_map[stick_sel[1]];
933 static const char *mgn_dev_name(int id, int *offs)
935 const char *name = NULL;
938 if (id == MA_CTRL_DEV_FIRST)
941 for (; it < IN_MAX_DEVS; it++) {
942 name = in_get_dev_name(it, 1, 1);
951 static const char *mgn_saveloadcfg(int id, int *offs)
956 static int mh_savecfg(int id, int keys)
958 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
959 me_update_msg("config saved");
961 me_update_msg("failed to write config");
966 static int mh_input_rescan(int id, int keys)
968 //menu_sync_config();
970 me_update_msg("rescan complete.");
975 static const char *men_in_type_sel[] = {
976 "Standard (SCPH-1080)",
977 "Analog (SCPH-1150)",
981 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
982 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
983 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
985 static menu_entry e_menu_keyconfig[] =
987 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
988 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
989 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
990 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
992 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
993 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
994 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
995 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
996 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
997 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
998 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
999 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1000 mee_handler ("Rescan devices:", mh_input_rescan),
1002 mee_label_mk (MA_CTRL_DEV_FIRST, 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),
1005 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1006 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1007 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1008 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1012 static int menu_loop_keyconfig(int id, int keys)
1016 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1017 me_loop(e_menu_keyconfig, &sel);
1021 // ------------ gfx options menu ------------
1023 static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
1024 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1025 "using d-pad or move it using R+d-pad";
1026 static const char *men_dummy[] = { NULL };
1028 static int menu_loop_cscaler(int id, int keys)
1032 scaling = SCALE_CUSTOM;
1034 plat_gvideo_open(Config.PsxType);
1039 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1040 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1041 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1044 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
1045 if (inp & PBTN_UP) g_layer_y--;
1046 if (inp & PBTN_DOWN) g_layer_y++;
1047 if (inp & PBTN_LEFT) g_layer_x--;
1048 if (inp & PBTN_RIGHT) g_layer_x++;
1049 if (!(inp & PBTN_R)) {
1050 if (inp & PBTN_UP) g_layer_h += 2;
1051 if (inp & PBTN_DOWN) g_layer_h -= 2;
1052 if (inp & PBTN_LEFT) g_layer_w += 2;
1053 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1055 if (inp & (PBTN_MOK|PBTN_MBACK))
1058 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1059 if (g_layer_x < 0) g_layer_x = 0;
1060 if (g_layer_x > 640) g_layer_x = 640;
1061 if (g_layer_y < 0) g_layer_y = 0;
1062 if (g_layer_y > 420) g_layer_y = 420;
1063 if (g_layer_w < 160) g_layer_w = 160;
1064 if (g_layer_h < 60) g_layer_h = 60;
1065 if (g_layer_x + g_layer_w > 800)
1066 g_layer_w = 800 - g_layer_x;
1067 if (g_layer_y + g_layer_h > 480)
1068 g_layer_h = 480 - g_layer_y;
1070 plat_gvideo_open(Config.PsxType);
1074 plat_gvideo_close();
1079 static menu_entry e_menu_gfx_options[] =
1081 mee_enum ("Scaler", MA_OPT_SCALER, scaling, men_scaler),
1082 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1083 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
1084 // mee_onoff ("Vsync", 0, vsync, 1),
1085 mee_cust_h ("Setup custom scaler", MA_OPT_SCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1089 static int menu_loop_gfx_options(int id, int keys)
1093 me_loop(e_menu_gfx_options, &sel);
1099 void menu_set_filter_list(void *filters)
1103 i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
1104 e_menu_gfx_options[i].data = filters;
1105 me_enable(e_menu_gfx_options, MA_OPT_FILTERING, filters != NULL);
1108 // ------------ bios/plugins ------------
1112 static const char h_gpu_neon[] = "Configure built-in NEON GPU plugin";
1113 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1115 static menu_entry e_menu_plugin_gpu_neon[] =
1117 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1121 static int menu_loop_plugin_gpu_neon(int id, int keys)
1124 me_loop(e_menu_plugin_gpu_neon, &sel);
1130 static menu_entry e_menu_plugin_gpu_unai[] =
1132 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1133 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1134 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1135 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1139 static int menu_loop_plugin_gpu_unai(int id, int keys)
1142 me_loop(e_menu_plugin_gpu_unai, &sel);
1146 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1147 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1148 static const char h_gpu_1[] = "Capcom fighting games";
1149 static const char h_gpu_2[] = "Black screens in Lunar";
1150 static const char h_gpu_3[] = "Compatibility mode";
1151 static const char h_gpu_6[] = "Pandemonium 2";
1152 //static const char h_gpu_7[] = "Skip every second frame";
1153 static const char h_gpu_8[] = "Needed by Dark Forces";
1154 static const char h_gpu_9[] = "better g-colors, worse textures";
1155 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1157 static menu_entry e_menu_plugin_gpu_peops[] =
1159 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1160 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1161 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1162 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1163 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1164 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1165 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1166 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1167 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1168 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1172 static int menu_loop_plugin_gpu_peops(int id, int keys)
1175 me_loop(e_menu_plugin_gpu_peops, &sel);
1179 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1180 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1181 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1183 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1185 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1186 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1187 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1188 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1189 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1190 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1191 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1192 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1193 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1194 mee_label ("Fixes/hacks:"),
1195 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1196 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1197 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1198 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1199 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1200 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1201 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1202 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1203 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1204 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1205 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1209 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1212 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1216 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1217 static const char h_spu_volboost[] = "Large values cause distortion";
1218 static const char h_spu_irq_wait[] = "Wait for CPU (recommended set to ON)";
1219 static const char h_spu_thread[] = "Run sound emulation in main thread (recommended)";
1221 static menu_entry e_menu_plugin_spu[] =
1223 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1224 mee_onoff ("Reverb", 0, iUseReverb, 2),
1225 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
1226 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
1227 mee_onoff_h ("SPU IRQ Wait", 0, iSPUIRQWait, 1, h_spu_irq_wait),
1228 mee_onoff_h ("Sound in main thread", 0, iUseTimer, 2, h_spu_thread),
1232 static int menu_loop_plugin_spu(int id, int keys)
1235 me_loop(e_menu_plugin_spu, &sel);
1239 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1240 "savestates and can't be changed there. Must save\n"
1241 "config and reload the game for change to take effect";
1242 static const char h_plugin_gpu[] =
1244 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1249 "is Pete's soft GPU, slow but accurate\n"
1250 "gpuPCSX4ALL is GPU from PCSX4ALL, fast but glitchy\n"
1251 "gpuGLES Pete's hw GPU, uses 3D chip but is glitchy\n"
1252 "must save config and reload the game if changed";
1253 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1254 "must save config and reload the game if changed";
1255 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1256 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1257 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team GPU plugin";
1258 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1260 static menu_entry e_menu_plugin_options[] =
1262 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1263 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1264 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1266 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1268 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1269 mee_handler_h ("Configure PCSX4ALL GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1270 mee_handler_h ("Configure GLES GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1271 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1275 static menu_entry e_menu_main2[];
1277 static int menu_loop_plugin_options(int id, int keys)
1280 me_loop(e_menu_plugin_options, &sel);
1282 // sync BIOS/plugins
1283 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1284 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1285 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1286 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1291 // ------------ adv options menu ------------
1293 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1294 "(lower value - less work for the emu, may be faster)";
1295 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1296 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1297 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1299 static menu_entry e_menu_speed_hacks[] =
1301 mee_range_h ("PSX CPU clock, %%", 0, psx_clock, 1, 500, h_cfg_psxclk),
1302 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1303 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1304 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1308 static int menu_loop_speed_hacks(int id, int keys)
1311 me_loop(e_menu_speed_hacks, &sel);
1315 static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
1316 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1317 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1318 "(green: normal, red: fmod, blue: noise)";
1319 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1320 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1321 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1322 "(proper .cue/.bin dump is needed otherwise)";
1323 static const char h_cfg_sio[] = "You should not need this, breaks games";
1324 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1325 static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1326 "(timing hack, breaks other games)";
1327 static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix\n"
1328 "(timing hack, breaks other games)";
1329 static const char h_cfg_cdrr[] = "Compatibility tweak (CD timing hack, breaks FMVs)";
1330 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1331 "Might be useful to overcome some dynarec bugs";
1332 static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
1333 "must reload game for any change to take effect";
1335 static menu_entry e_menu_adv_options[] =
1337 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1338 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1339 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1340 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1341 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1342 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1343 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1344 //mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1345 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
1346 mee_enum_h ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
1347 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1348 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1352 static int menu_loop_adv_options(int id, int keys)
1355 me_loop(e_menu_adv_options, &sel);
1359 // ------------ options menu ------------
1361 static int mh_restore_defaults(int id, int keys)
1363 menu_set_defconfig();
1364 me_update_msg("defaults restored");
1368 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1369 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1371 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1372 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1373 "loading state or both";
1375 static const char h_restore_def[] = "Switches back to default / recommended\n"
1377 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1379 static menu_entry e_menu_options[] =
1381 // mee_range ("Save slot", 0, state_slot, 0, 9),
1382 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1383 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1384 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1385 mee_enum ("Region", 0, region, men_region),
1386 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1387 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1388 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1389 mee_handler ("[Advanced]", menu_loop_adv_options),
1390 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1391 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1392 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1396 static int menu_loop_options(int id, int keys)
1401 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1402 e_menu_options[i].enabled = cpu_clock_st > 0 ? 1 : 0;
1403 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1405 me_loop(e_menu_options, &sel);
1410 // ------------ debug menu ------------
1412 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1414 int w = min(g_menuscreen_w, 1024);
1415 int h = min(g_menuscreen_h, 512);
1416 u16 *d = g_menuscreen_ptr;
1417 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1421 gpuf->ulFreezeVersion = 1;
1422 if (GPU_freeze != NULL)
1423 GPU_freeze(1, gpuf);
1425 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1426 bgr555_to_rgb565(d, s, w * 2);
1428 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1429 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1430 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1431 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1432 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1435 static void debug_menu_loop(void)
1437 int inp, df_x = 0, df_y = 0;
1440 gpuf = malloc(sizeof(*gpuf));
1447 draw_frame_debug(gpuf, df_x, df_y);
1450 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1451 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 10);
1452 if (inp & PBTN_MBACK) break;
1453 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1454 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1455 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x--; }
1456 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x++; }
1462 // --------- memcard manager ---------
1464 static void draw_mc_icon(int dx, int dy, const u16 *s)
1469 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1471 for (y = 0; y < 16; y++, s += 16) {
1472 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1473 for (x = 0; x < 16; x++) {
1475 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1476 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1482 static void draw_mc_bg(void)
1484 McdBlock *blocks1, *blocks2;
1488 blocks1 = malloc(15 * sizeof(blocks1[0]));
1489 blocks2 = malloc(15 * sizeof(blocks1[0]));
1490 if (blocks1 == NULL || blocks2 == NULL)
1493 for (i = 0; i < 15; i++) {
1494 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1495 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1500 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1502 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1506 maxicons = g_menuscreen_h / 32;
1509 row2 = g_menuscreen_w / 2;
1510 for (i = 0; i < maxicons; i++) {
1511 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1512 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1514 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1515 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1518 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1526 static void handle_memcard_sel(void)
1529 if (memcard1_sel != 0)
1530 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1532 if (memcard2_sel != 0)
1533 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1534 LoadMcds(Config.Mcd1, Config.Mcd2);
1538 static menu_entry e_memcard_options[] =
1540 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1541 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1545 static int menu_loop_memcards(int id, int keys)
1551 memcard1_sel = memcard2_sel = 0;
1552 p = strrchr(Config.Mcd1, '/');
1554 for (i = 0; memcards[i] != NULL; i++)
1555 if (strcmp(p + 1, memcards[i]) == 0)
1556 { memcard1_sel = i; break; }
1557 p = strrchr(Config.Mcd2, '/');
1559 for (i = 0; memcards[i] != NULL; i++)
1560 if (strcmp(p + 1, memcards[i]) == 0)
1561 { memcard2_sel = i; break; }
1563 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1565 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1570 // --------- main menu help ----------
1572 static void menu_bios_warn(void)
1575 static const char msg[] =
1576 "You don't seem to have copied any BIOS\n"
1578 #ifdef __ARM_ARCH_7A__ // XXX
1579 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1581 "pcsx_rearmed/bios/\n\n"
1583 "While many games work fine with fake\n"
1584 "(HLE) BIOS, others (like MGS and FF8)\n"
1585 "require BIOS to work.\n"
1586 "After copying the file, you'll also need\n"
1587 "to select it in the emu's menu:\n"
1588 "options->[BIOS/Plugins]\n\n"
1589 "The file is usually named SCPH1001.BIN,\n"
1590 "but other not compressed files can be\n"
1592 "Press %s or %s to continue";
1593 char tmp_msg[sizeof(msg) + 64];
1595 snprintf(tmp_msg, sizeof(tmp_msg), msg,
1596 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1599 draw_menu_message(tmp_msg, NULL);
1601 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1602 if (inp & (PBTN_MBACK|PBTN_MOK))
1607 // ------------ main menu ------------
1611 static void draw_frame_main(void)
1620 if (CdromId[0] != 0) {
1621 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1622 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1623 Config.HLE ? "HLE" : "BIOS");
1624 smalltext_out16(4, 1, buff, 0x105f);
1628 capacity = plat_get_bat_capacity();
1630 tmp = localtime(<ime);
1631 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1632 if (capacity >= 0) {
1633 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1638 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1642 static void draw_frame_credits(void)
1644 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1647 static const char credits_text[] =
1649 "(C) 1999-2003 PCSX Team\n"
1650 "(C) 2005-2009 PCSX-df Team\n"
1651 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1652 "ARM recompiler (C) 2009-2011 Ari64\n"
1654 "ARM NEON GPU (c) 2011-2012 Exophase\n"
1656 "PEOpS GPU and SPU by Pete Bernert\n"
1657 " and the P.E.Op.S. team\n"
1658 "PCSX4ALL plugin by PCSX4ALL team\n"
1659 " Chui, Franxis, Unai\n\n"
1660 "integration, optimization and\n"
1661 " frontend (C) 2010-2012 notaz\n";
1663 static int reset_game(void)
1666 if (bios_sel == 0 && !Config.HLE)
1672 if (CheckCdrom() != -1) {
1678 static int reload_plugins(const char *cdimg)
1684 set_cd_image(cdimg);
1686 pcnt_hook_plugins();
1688 if (OpenPlugins() == -1) {
1689 me_update_msg("failed to open plugins");
1692 plugin_call_rearmed_cbs();
1694 cdrIsoMultidiskCount = 1;
1696 CdromLabel[0] = '\0';
1701 static int run_bios(void)
1707 if (reload_plugins(NULL) != 0)
1715 static int run_exe(void)
1719 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1724 if (reload_plugins(NULL) != 0)
1728 if (Load(fname) != 0) {
1729 me_update_msg("exe load failed, bad file?");
1738 static int run_cd_image(const char *fname)
1741 reload_plugins(fname);
1743 // always autodetect, menu_sync_config will override as needed
1746 if (CheckCdrom() == -1) {
1747 // Only check the CD if we are starting the console with a CD
1749 me_update_msg("unsupported/invalid CD image");
1755 // Read main executable directly from CDRom and start it
1756 if (LoadCdrom() == -1) {
1758 me_update_msg("failed to load CD image");
1763 snprintf(hud_msg, sizeof(hud_msg), "Booting up...");
1768 static int romsel_run(void)
1770 int prev_gpu, prev_spu;
1773 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1777 printf("selected file: %s\n", fname);
1779 new_dynarec_clear_full();
1781 if (run_cd_image(fname) != 0)
1784 prev_gpu = gpu_plugsel;
1785 prev_spu = spu_plugsel;
1786 if (menu_load_config(1) != 0)
1787 menu_load_config(0);
1789 // check for plugin changes, have to repeat
1790 // loading if game config changed plugins to reload them
1791 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1792 printf("plugin change detected, reloading plugins..\n");
1793 if (run_cd_image(fname) != 0)
1798 printf("note: running without BIOS, expect compatibility problems\n");
1800 strcpy(last_selected_fname, rom_fname_reload);
1804 static int swap_cd_image(void)
1808 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1812 printf("selected file: %s\n", fname);
1815 CdromLabel[0] = '\0';
1817 set_cd_image(fname);
1818 if (ReloadCdromPlugin() < 0) {
1819 me_update_msg("failed to load cdr plugin");
1822 if (CDR_open() < 0) {
1823 me_update_msg("failed to open cdr plugin");
1827 SetCdOpenCaseTime(time(NULL) + 2);
1830 strcpy(last_selected_fname, rom_fname_reload);
1834 static int swap_cd_multidisk(void)
1836 cdrIsoMultidiskSelect++;
1838 CdromLabel[0] = '\0';
1841 if (CDR_open() < 0) {
1842 me_update_msg("failed to open cdr plugin");
1846 SetCdOpenCaseTime(time(NULL) + 2);
1852 static int main_menu_handler(int id, int keys)
1856 case MA_MAIN_RESUME_GAME:
1860 case MA_MAIN_SAVE_STATE:
1862 return menu_loop_savestate(0);
1864 case MA_MAIN_LOAD_STATE:
1866 return menu_loop_savestate(1);
1868 case MA_MAIN_RESET_GAME:
1869 if (ready_to_go && reset_game() == 0)
1872 case MA_MAIN_LOAD_ROM:
1873 if (romsel_run() == 0)
1876 case MA_MAIN_SWAP_CD:
1877 if (swap_cd_image() == 0)
1880 case MA_MAIN_SWAP_CD_MULTI:
1881 if (swap_cd_multidisk() == 0)
1884 case MA_MAIN_RUN_BIOS:
1885 if (run_bios() == 0)
1888 case MA_MAIN_RUN_EXE:
1892 case MA_MAIN_CREDITS:
1893 draw_menu_message(credits_text, draw_frame_credits);
1894 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1900 lprintf("%s: something unknown selected\n", __FUNCTION__);
1907 static menu_entry e_menu_main2[] =
1909 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1910 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
1911 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1912 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
1913 mee_handler ("Memcard manager", menu_loop_memcards),
1917 static int main_menu2_handler(int id, int keys)
1921 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
1922 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
1923 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1925 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1928 static const char h_extra[] = "Change CD, manage memcards..\n";
1930 static menu_entry e_menu_main[] =
1934 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1935 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1936 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1937 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1938 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1939 mee_handler ("Options", menu_loop_options),
1940 mee_handler ("Controls", menu_loop_keyconfig),
1941 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
1942 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1943 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1947 // ----------------------------
1949 static void menu_leave_emu(void);
1951 void menu_loop(void)
1957 if (bioses[1] == NULL && !warned_about_bios) {
1959 warned_about_bios = 1;
1962 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1963 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1964 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
1965 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1967 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1970 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
1971 } while (!ready_to_go);
1973 /* wait until menu, ok, back is released */
1974 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1977 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1982 static int qsort_strcmp(const void *p1, const void *p2)
1984 char * const *s1 = (char * const *)p1;
1985 char * const *s2 = (char * const *)p2;
1986 return strcasecmp(*s1, *s2);
1989 static void scan_bios_plugins(void)
1991 char fname[MAXPATHLEN];
1993 int bios_i, gpu_i, spu_i, mc_i;
1998 gpu_plugins[0] = "builtin_gpu";
1999 spu_plugins[0] = "builtin_spu";
2000 memcards[0] = "(none)";
2001 bios_i = gpu_i = spu_i = mc_i = 1;
2003 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2004 dir = opendir(fname);
2006 perror("scan_bios_plugins bios opendir");
2021 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2024 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2025 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2026 printf("bad BIOS file: %s\n", ent->d_name);
2030 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2031 bioses[bios_i++] = strdup(ent->d_name);
2035 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2041 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2042 dir = opendir(fname);
2044 perror("scan_bios_plugins plugins opendir");
2058 p = strstr(ent->d_name, ".so");
2062 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2063 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2065 fprintf(stderr, "%s\n", dlerror());
2069 // now what do we have here?
2070 tmp = dlsym(h, "GPUinit");
2073 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2074 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2078 tmp = dlsym(h, "SPUinit");
2081 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2082 spu_plugins[spu_i++] = strdup(ent->d_name);
2086 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2093 dir = opendir("." MEMCARD_DIR);
2095 perror("scan_bios_plugins memcards opendir");
2110 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2113 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2114 if (stat(fname, &st) != 0) {
2115 printf("bad memcard file: %s\n", ent->d_name);
2119 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2120 memcards[mc_i++] = strdup(ent->d_name);
2124 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2128 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2133 void menu_init(void)
2135 char buff[MAXPATHLEN];
2137 strcpy(last_selected_fname, "/media");
2139 cpu_clock_st = cpu_clock = plat_cpu_clock_get();
2141 scan_bios_plugins();
2144 menu_set_defconfig();
2145 menu_load_config(0);
2150 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2151 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2152 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2153 fprintf(stderr, "OOM\n");
2157 emu_make_path(buff, "skin/background.png", sizeof(buff));
2158 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2160 #ifndef __ARM_ARCH_7A__ /* XXX */
2161 me_enable(e_menu_gfx_options, MA_OPT_SCALER, 0);
2162 me_enable(e_menu_gfx_options, MA_OPT_FILTERING, 0);
2163 me_enable(e_menu_gfx_options, MA_OPT_SCALER_C, 0);
2164 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2166 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, 0);
2167 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
2168 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2172 void menu_notify_mode_change(int w, int h, int bpp)
2181 // XXX: should really menu code cotrol the layer size?
2184 g_layer_w = w; g_layer_h = h;
2188 if (h > g_menuscreen_h || (240 < h && h <= 360))
2189 goto fractional_4_3;
2191 // 4:3 that prefers integer scaling
2192 imult = g_menuscreen_h / h;
2193 g_layer_w = w * imult;
2194 g_layer_h = h * imult;
2195 mult = (float)g_layer_w / (float)g_layer_h;
2196 if (mult < 1.25f || mult > 1.666f)
2197 g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2198 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2203 mult = 240.0f / (float)h * 4.0f / 3.0f;
2206 g_layer_w = mult * (float)g_menuscreen_h;
2207 g_layer_h = g_menuscreen_h;
2208 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2211 case SCALE_FULLSCREEN:
2212 g_layer_w = g_menuscreen_w;
2213 g_layer_h = g_menuscreen_h;
2220 g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2221 g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2222 if (g_layer_x < 0) g_layer_x = 0;
2223 if (g_layer_y < 0) g_layer_y = 0;
2224 if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2225 if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2228 static void menu_leave_emu(void)
2230 if (GPU_close != NULL) {
2231 int ret = GPU_close();
2233 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2236 plat_video_menu_enter(ready_to_go);
2238 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2239 if (pl_vout_buf != NULL && ready_to_go) {
2240 int x = max(0, g_menuscreen_w - last_psx_w);
2241 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2242 int w = min(g_menuscreen_w, last_psx_w);
2243 int h = min(g_menuscreen_h, last_psx_h);
2244 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2245 char *s = pl_vout_buf;
2247 if (last_psx_bpp == 16) {
2248 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w * 2)
2249 menu_darken_bg(d, s, w, 0);
2252 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w * 3) {
2253 bgr888_to_rgb565(d, s, w * 3);
2254 menu_darken_bg(d, d, w, 0);
2260 cpu_clock = plat_cpu_clock_get();
2263 void menu_prepare_emu(void)
2265 R3000Acpu *prev_cpu = psxCpu;
2267 plat_video_menu_leave();
2269 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2271 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2272 if (psxCpu != prev_cpu)
2273 // note that this does not really reset, just clears drc caches
2276 // core doesn't care about Config.Cdda changes,
2277 // so handle them manually here
2283 plat_cpu_clock_apply(cpu_clock);
2285 // push config to GPU plugin
2286 plugin_call_rearmed_cbs();
2288 if (GPU_open != NULL) {
2289 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2291 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2297 void me_update_msg(const char *msg)
2299 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2300 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2302 menu_error_time = plat_get_ticks_ms();
2303 lprintf("msg: %s\n", menu_error_msg);
2306 void menu_finish(void)
2308 plat_cpu_clock_apply(cpu_clock_st);