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;
112 static const char *bioses[24];
113 static const char *gpu_plugins[16];
114 static const char *spu_plugins[16];
115 static const char *memcards[32];
116 static int bios_sel, gpu_plugsel, spu_plugsel;
119 static int min(int x, int y) { return x < y ? x : y; }
120 static int max(int x, int y) { return x > y ? x : y; }
122 void emu_make_path(char *buff, const char *end, int size)
126 end_len = strlen(end);
127 pos = plat_get_root_dir(buff, size);
128 strncpy(buff + pos, end, size - pos);
130 if (pos + end_len > size - 1)
131 printf("Warning: path truncated: %s\n", buff);
134 static int emu_check_save_file(int slot, int *time)
136 char fname[MAXPATHLEN];
140 ret = emu_check_state(slot);
141 if (ret != 0 || time == NULL)
142 return ret == 0 ? 1 : 0;
144 ret = get_state_filename(fname, sizeof(fname), slot);
148 ret = stat(fname, &status);
152 if (status.st_mtime < REARMED_BIRTHDAY_TIME)
153 return 1; // probably bad rtc like on some Caanoos
155 *time = status.st_mtime;
160 static int emu_save_load_game(int load, int unused)
165 ret = emu_load_state(state_slot);
167 // reflect hle/bios mode from savestate
170 else if (bios_sel == 0 && bioses[1] != NULL)
171 // XXX: maybe find the right bios instead
175 ret = emu_save_state(state_slot);
180 // propagate menu settings to the emu vars
181 static void menu_sync_config(void)
183 static int allow_abs_only_old;
188 Config.PsxType = region - 1;
190 cycle_multiplier = 10000 / psx_clock;
192 switch (in_type_sel1) {
193 case 1: in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
194 case 2: in_type1 = PSE_PAD_TYPE_GUNCON; break;
195 default: in_type1 = PSE_PAD_TYPE_STANDARD;
197 switch (in_type_sel2) {
198 case 1: in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
199 case 2: in_type2 = PSE_PAD_TYPE_GUNCON; break;
200 default: in_type2 = PSE_PAD_TYPE_STANDARD;
202 if (in_evdev_allow_abs_only != allow_abs_only_old) {
204 allow_abs_only_old = in_evdev_allow_abs_only;
207 iVolume = 768 + 128 * volume_boost;
208 pl_rearmed_cbs.frameskip = frameskip - 1;
209 pl_timing_prepare(Config.PsxType);
212 static void menu_set_defconfig(void)
214 emu_set_default_config();
220 analog_deadzone = 50;
222 psx_clock = DEFAULT_PSX_CLOCK;
225 in_type_sel1 = in_type_sel2 = 0;
226 in_evdev_allow_abs_only = 0;
231 #define CE_CONFIG_STR(val) \
232 { #val, 0, Config.val }
234 #define CE_CONFIG_VAL(val) \
235 { #val, sizeof(Config.val), &Config.val }
237 #define CE_STR(val) \
240 #define CE_INTVAL(val) \
241 { #val, sizeof(val), &val }
243 #define CE_INTVAL_P(val) \
244 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
246 // 'versioned' var, used when defaults change
247 #define CE_CONFIG_STR_V(val, ver) \
248 { #val #ver, 0, Config.val }
250 #define CE_INTVAL_V(val, ver) \
251 { #val #ver, sizeof(val), &val }
253 #define CE_INTVAL_PV(val, ver) \
254 { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
256 static const struct {
262 CE_CONFIG_STR_V(Gpu, 2),
264 // CE_CONFIG_STR(Cdr),
269 CE_CONFIG_VAL(Debug),
270 CE_CONFIG_VAL(PsxOut),
271 CE_CONFIG_VAL(SpuIrq),
272 CE_CONFIG_VAL(RCntFix),
273 CE_CONFIG_VAL(VSyncWA),
275 CE_CONFIG_VAL(CdrReschedule),
277 CE_INTVAL_V(scaling, 2),
278 CE_INTVAL(g_layer_x),
279 CE_INTVAL(g_layer_y),
280 CE_INTVAL(g_layer_w),
281 CE_INTVAL(g_layer_h),
283 CE_INTVAL(state_slot),
284 CE_INTVAL(cpu_clock),
286 CE_INTVAL(in_type_sel1),
287 CE_INTVAL(in_type_sel2),
288 CE_INTVAL(analog_deadzone),
289 CE_INTVAL_V(frameskip, 3),
290 CE_INTVAL_P(gpu_peops.iUseDither),
291 CE_INTVAL_P(gpu_peops.dwActFixes),
292 CE_INTVAL_P(gpu_unai.lineskip),
293 CE_INTVAL_P(gpu_unai.abe_hack),
294 CE_INTVAL_P(gpu_unai.no_light),
295 CE_INTVAL_P(gpu_unai.no_blend),
296 CE_INTVAL_P(gpu_neon.allow_interlace),
297 CE_INTVAL_P(gpu_peopsgl.bDrawDither),
298 CE_INTVAL_P(gpu_peopsgl.iFilterType),
299 CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
300 CE_INTVAL_P(gpu_peopsgl.iUseMask),
301 CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
302 CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
303 CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
304 CE_INTVAL_P(gpu_peopsgl.iVRamSize),
305 CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
306 CE_INTVAL_P(gpu_peopsgl.dwActFixes),
307 CE_INTVAL_V(iUseReverb, 3),
308 CE_INTVAL_V(iXAPitch, 3),
309 CE_INTVAL_V(iUseInterpolation, 3),
310 CE_INTVAL_V(iSPUIRQWait, 3),
311 CE_INTVAL(warned_about_bios),
312 CE_INTVAL(in_evdev_allow_abs_only),
313 CE_INTVAL(volume_boost),
314 CE_INTVAL(psx_clock),
315 CE_INTVAL(new_dynarec_hacks),
316 CE_INTVAL(in_enable_vibration),
319 static char *get_cd_label(void)
321 static char trimlabel[33];
324 strncpy(trimlabel, CdromLabel, 32);
326 for (j = 31; j >= 0; j--)
327 if (trimlabel[j] == ' ')
333 static void make_cfg_fname(char *buf, size_t size, int is_game)
336 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
338 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
341 static void keys_write_all(FILE *f);
343 static int menu_write_config(int is_game)
345 char cfgfile[MAXPATHLEN];
349 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
350 f = fopen(cfgfile, "w");
352 printf("menu_write_config: failed to open: %s\n", cfgfile);
356 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
357 fprintf(f, "%s = ", config_data[i].name);
358 switch (config_data[i].len) {
360 fprintf(f, "%s\n", (char *)config_data[i].val);
363 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
366 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
369 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
372 printf("menu_write_config: unhandled len %d for %s\n",
373 config_data[i].len, config_data[i].name);
379 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
387 static void parse_str_val(char *cval, const char *src)
390 strncpy(cval, src, MAXPATHLEN);
391 cval[MAXPATHLEN - 1] = 0;
392 tmp = strchr(cval, '\n');
394 tmp = strchr(cval, '\r');
399 static void keys_load_all(const char *cfg);
401 static int menu_load_config(int is_game)
403 char cfgfile[MAXPATHLEN];
409 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
410 f = fopen(cfgfile, "r");
412 printf("menu_load_config: failed to open: %s\n", cfgfile);
416 fseek(f, 0, SEEK_END);
419 printf("bad size %ld: %s\n", size, cfgfile);
423 cfg = malloc(size + 1);
427 fseek(f, 0, SEEK_SET);
428 if (fread(cfg, 1, size, f) != size) {
429 printf("failed to read: %s\n", cfgfile);
434 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
438 tmp = strstr(cfg, config_data[i].name);
441 tmp += strlen(config_data[i].name);
442 if (strncmp(tmp, " = ", 3) != 0)
446 if (config_data[i].len == 0) {
447 parse_str_val(config_data[i].val, tmp);
452 val = strtoul(tmp, &tmp2, 16);
453 if (tmp2 == NULL || tmp == tmp2)
454 continue; // parse failed
456 switch (config_data[i].len) {
458 *(u8 *)config_data[i].val = val;
461 *(u16 *)config_data[i].val = val;
464 *(u32 *)config_data[i].val = val;
467 printf("menu_load_config: unhandled len %d for %s\n",
468 config_data[i].len, config_data[i].name);
474 char *tmp = strstr(cfg, "lastcdimg = ");
477 parse_str_val(last_selected_fname, tmp);
491 // caanoo old config compat hack
492 if (strcmp(Config.Gpu, "gpuPCSX4ALL.so") == 0)
493 strcpy(Config.Gpu, "gpu_unai.so");
496 for (i = bios_sel = 0; bioses[i] != NULL; i++)
497 if (strcmp(Config.Bios, bioses[i]) == 0)
498 { bios_sel = i; break; }
500 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
501 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
502 { gpu_plugsel = i; break; }
504 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
505 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
506 { spu_plugsel = i; break; }
511 // rrrr rggg gggb bbbb
512 static unsigned short fname2color(const char *fname)
514 static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z",
515 ".bz", ".znx", ".pbp", ".cbn" };
516 static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub",
517 ".table", ".index", ".sbi" };
518 const char *ext = strrchr(fname, '.');
523 for (i = 0; i < array_size(cdimg_exts); i++)
524 if (strcasecmp(ext, cdimg_exts[i]) == 0)
526 for (i = 0; i < array_size(other_exts); i++)
527 if (strcasecmp(ext, other_exts[i]) == 0)
532 static void draw_savestate_bg(int slot);
534 static const char *filter_exts[] = {
535 ".mp3", ".MP3", ".txt", ".htm", "html", ".jpg", ".pnd"
538 #define MENU_ALIGN_LEFT
539 #ifdef __ARM_ARCH_7A__ // assume hires device
545 #define menu_init menu_init_common
546 #include "common/menu.c"
549 // a bit of black magic here
550 static void draw_savestate_bg(int slot)
552 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
554 char fname[MAXPATHLEN];
561 ret = get_state_filename(fname, sizeof(fname), slot);
565 f = gzopen(fname, "rb");
569 if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
570 fprintf(stderr, "gzseek failed\n");
575 gpu = malloc(sizeof(*gpu));
581 ret = gzread(f, gpu, sizeof(*gpu));
583 if (ret != sizeof(*gpu)) {
584 fprintf(stderr, "gzread failed\n");
588 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
590 if (gpu->ulStatus & 0x800000)
591 goto out; // disabled
593 x = gpu->ulControl[5] & 0x3ff;
594 y = (gpu->ulControl[5] >> 10) & 0x1ff;
595 s = (u16 *)gpu->psxVRam + y * 1024 + (x & ~1);
596 w = psx_widths[(gpu->ulStatus >> 16) & 7];
597 tmp = gpu->ulControl[7];
598 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
599 if (gpu->ulStatus & 0x80000) // doubleheight
602 x = max(0, g_menuscreen_w - w) & ~3;
603 y = max(0, g_menuscreen_h / 2 - h / 2);
604 w = min(g_menuscreen_w, w);
605 h = min(g_menuscreen_h, h);
606 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
608 for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
609 if (gpu->ulStatus & 0x200000)
610 bgr888_to_rgb565(d, s, w * 3);
612 bgr555_to_rgb565(d, s, w * 2);
614 // darken this so that menu text is visible
615 if (g_menuscreen_w - w < 320)
616 menu_darken_bg(d, d, w * 2, 0);
623 // -------------- key config --------------
625 me_bind_action me_ctrl_actions[] =
627 { "UP ", 1 << DKEY_UP},
628 { "DOWN ", 1 << DKEY_DOWN },
629 { "LEFT ", 1 << DKEY_LEFT },
630 { "RIGHT ", 1 << DKEY_RIGHT },
631 { "TRIANGLE", 1 << DKEY_TRIANGLE },
632 { "CIRCLE ", 1 << DKEY_CIRCLE },
633 { "CROSS ", 1 << DKEY_CROSS },
634 { "SQUARE ", 1 << DKEY_SQUARE },
635 { "L1 ", 1 << DKEY_L1 },
636 { "R1 ", 1 << DKEY_R1 },
637 { "L2 ", 1 << DKEY_L2 },
638 { "R2 ", 1 << DKEY_R2 },
639 { "L3 ", 1 << DKEY_L3 },
640 { "R3 ", 1 << DKEY_R3 },
641 { "START ", 1 << DKEY_START },
642 { "SELECT ", 1 << DKEY_SELECT },
646 me_bind_action emuctrl_actions[] =
648 { "Save State ", 1 << SACTION_SAVE_STATE },
649 { "Load State ", 1 << SACTION_LOAD_STATE },
650 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
651 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
652 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
653 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
654 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
655 #ifdef __ARM_ARCH_7A__ /* XXX */
656 { "Minimize ", 1 << SACTION_MINIMIZE },
658 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
659 { "Gun A button ", 1 << SACTION_GUN_A },
660 { "Gun B button ", 1 << SACTION_GUN_B },
661 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
662 #ifndef __ARM_ARCH_7A__ /* XXX */
663 { "Volume Up ", 1 << SACTION_VOLUME_UP },
664 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
669 static char *mystrip(char *str)
674 for (i = 0; i < len; i++)
675 if (str[i] != ' ') break;
676 if (i > 0) memmove(str, str + i, len - i + 1);
679 for (i = len - 1; i >= 0; i--)
680 if (str[i] != ' ') break;
686 static void get_line(char *d, size_t size, const char *s)
691 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
702 static void keys_write_all(FILE *f)
706 for (d = 0; d < IN_MAX_DEVS; d++)
708 const int *binds = in_get_dev_binds(d);
709 const char *name = in_get_dev_name(d, 0, 0);
712 if (binds == NULL || name == NULL)
715 fprintf(f, "binddev = %s\n", name);
716 in_get_config(d, IN_CFG_BIND_COUNT, &count);
718 for (k = 0; k < count; k++)
723 act[0] = act[31] = 0;
724 name = in_get_key_name(d, k);
726 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
727 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
728 mask = me_ctrl_actions[i].mask;
730 strncpy(act, me_ctrl_actions[i].name, 31);
731 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
734 mask = me_ctrl_actions[i].mask << 16;
736 strncpy(act, me_ctrl_actions[i].name, 31);
737 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
742 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
743 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
744 mask = emuctrl_actions[i].mask;
746 strncpy(act, emuctrl_actions[i].name, 31);
747 fprintf(f, "bind %s = %s\n", name, mystrip(act));
753 for (k = 0; k < array_size(in_adev); k++)
756 fprintf(f, "bind_analog = %d\n", k);
761 static int parse_bind_val(const char *val, int *type)
765 *type = IN_BINDTYPE_NONE;
769 if (strncasecmp(val, "player", 6) == 0)
771 int player, shift = 0;
772 player = atoi(val + 6) - 1;
774 if ((unsigned int)player > 1)
779 *type = IN_BINDTYPE_PLAYER12;
780 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
781 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
782 return me_ctrl_actions[i].mask << shift;
785 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
786 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
787 *type = IN_BINDTYPE_EMU;
788 return emuctrl_actions[i].mask;
795 static void keys_load_all(const char *cfg)
797 char dev[256], key[128], *act;
803 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
806 get_line(dev, sizeof(dev), p);
807 dev_id = in_config_parse_dev(dev);
809 printf("input: can't handle dev: %s\n", dev);
813 in_unbind_all(dev_id, -1, -1);
814 while ((p = strstr(p, "bind"))) {
815 if (strncmp(p, "binddev = ", 10) == 0)
818 if (strncmp(p, "bind_analog", 11) == 0) {
819 ret = sscanf(p, "bind_analog = %d", &bind);
822 printf("input: parse error: %16s..\n", p);
825 if ((unsigned int)bind >= array_size(in_adev)) {
826 printf("input: analog id %d out of range\n", bind);
829 in_adev[bind] = dev_id;
835 printf("input: parse error: %16s..\n", p);
839 get_line(key, sizeof(key), p);
840 act = strchr(key, '=');
842 printf("parse failed: %16s..\n", p);
850 bind = parse_bind_val(act, &bindtype);
851 if (bind != -1 && bind != 0) {
852 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
853 in_config_bind_key(dev_id, key, bind, bindtype);
856 lprintf("config: unhandled action \"%s\"\n", act);
862 static int key_config_loop_wrap(int id, int keys)
865 case MA_CTRL_PLAYER1:
866 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
868 case MA_CTRL_PLAYER2:
869 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
872 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
880 static const char *adevnames[IN_MAX_DEVS + 2];
881 static int stick_sel[2];
883 static menu_entry e_menu_keyconfig_analog[] =
885 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
886 mee_range(" X axis", 0, in_adev_axis[0][0], 0, 7),
887 mee_range(" Y axis", 0, in_adev_axis[0][1], 0, 7),
888 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
889 mee_range(" X axis", 0, in_adev_axis[1][0], 0, 7),
890 mee_range(" Y axis", 0, in_adev_axis[1][1], 0, 7),
894 static int key_config_analog(int id, int keys)
896 int i, d, count, sel = 0;
897 int sel2dev_map[IN_MAX_DEVS];
899 memset(adevnames, 0, sizeof(adevnames));
900 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
901 memset(stick_sel, 0, sizeof(stick_sel));
903 adevnames[0] = "None";
905 for (d = 0; d < IN_MAX_DEVS; d++)
907 const char *name = in_get_dev_name(d, 0, 1);
912 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
916 if (in_adev[0] == d) stick_sel[0] = i;
917 if (in_adev[1] == d) stick_sel[1] = i;
919 adevnames[i++] = name;
923 me_loop(e_menu_keyconfig_analog, &sel);
925 in_adev[0] = sel2dev_map[stick_sel[0]];
926 in_adev[1] = sel2dev_map[stick_sel[1]];
931 static const char *mgn_dev_name(int id, int *offs)
933 const char *name = NULL;
936 if (id == MA_CTRL_DEV_FIRST)
939 for (; it < IN_MAX_DEVS; it++) {
940 name = in_get_dev_name(it, 1, 1);
949 static const char *mgn_saveloadcfg(int id, int *offs)
954 static int mh_savecfg(int id, int keys)
956 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
957 me_update_msg("config saved");
959 me_update_msg("failed to write config");
964 static int mh_input_rescan(int id, int keys)
966 //menu_sync_config();
968 me_update_msg("rescan complete.");
973 static const char *men_in_type_sel[] = {
974 "Standard (SCPH-1080)",
975 "Analog (SCPH-1150)",
979 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
980 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
981 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
983 static menu_entry e_menu_keyconfig[] =
985 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
986 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
987 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
988 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
990 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
991 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
992 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
993 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
994 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
995 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
996 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
997 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
998 mee_handler ("Rescan devices:", mh_input_rescan),
1000 mee_label_mk (MA_CTRL_DEV_FIRST, 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),
1005 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1006 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1010 static int menu_loop_keyconfig(int id, int keys)
1014 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1015 me_loop(e_menu_keyconfig, &sel);
1019 // ------------ gfx options menu ------------
1021 static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
1022 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1023 "using d-pad or move it using R+d-pad";
1024 static const char *men_dummy[] = { NULL };
1026 static int menu_loop_cscaler(int id, int keys)
1030 scaling = SCALE_CUSTOM;
1032 plat_gvideo_open(Config.PsxType);
1037 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1038 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1039 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1042 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
1043 if (inp & PBTN_UP) g_layer_y--;
1044 if (inp & PBTN_DOWN) g_layer_y++;
1045 if (inp & PBTN_LEFT) g_layer_x--;
1046 if (inp & PBTN_RIGHT) g_layer_x++;
1047 if (!(inp & PBTN_R)) {
1048 if (inp & PBTN_UP) g_layer_h += 2;
1049 if (inp & PBTN_DOWN) g_layer_h -= 2;
1050 if (inp & PBTN_LEFT) g_layer_w += 2;
1051 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1053 if (inp & (PBTN_MOK|PBTN_MBACK))
1056 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1057 if (g_layer_x < 0) g_layer_x = 0;
1058 if (g_layer_x > 640) g_layer_x = 640;
1059 if (g_layer_y < 0) g_layer_y = 0;
1060 if (g_layer_y > 420) g_layer_y = 420;
1061 if (g_layer_w < 160) g_layer_w = 160;
1062 if (g_layer_h < 60) g_layer_h = 60;
1063 if (g_layer_x + g_layer_w > 800)
1064 g_layer_w = 800 - g_layer_x;
1065 if (g_layer_y + g_layer_h > 480)
1066 g_layer_h = 480 - g_layer_y;
1068 plat_gvideo_open(Config.PsxType);
1072 plat_gvideo_close();
1077 static menu_entry e_menu_gfx_options[] =
1079 mee_enum ("Scaler", MA_OPT_SCALER, scaling, men_scaler),
1080 mee_onoff ("Software Scaling", MA_OPT_SCALER2, soft_scaling, 1),
1081 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
1082 // mee_onoff ("Vsync", 0, vsync, 1),
1083 mee_cust_h ("Setup custom scaler", MA_OPT_SCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1087 static int menu_loop_gfx_options(int id, int keys)
1091 me_loop(e_menu_gfx_options, &sel);
1097 void menu_set_filter_list(void *filters)
1101 i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
1102 e_menu_gfx_options[i].data = filters;
1103 me_enable(e_menu_gfx_options, MA_OPT_FILTERING, filters != NULL);
1106 // ------------ bios/plugins ------------
1110 static const char h_gpu_neon[] = "Configure built-in NEON GPU plugin";
1111 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1113 static menu_entry e_menu_plugin_gpu_neon[] =
1115 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1119 static int menu_loop_plugin_gpu_neon(int id, int keys)
1122 me_loop(e_menu_plugin_gpu_neon, &sel);
1128 static menu_entry e_menu_plugin_gpu_unai[] =
1130 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1131 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1132 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1133 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1137 static int menu_loop_plugin_gpu_unai(int id, int keys)
1140 me_loop(e_menu_plugin_gpu_unai, &sel);
1144 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1145 //static const char h_gpu_0[] = "Needed for Chrono Cross";
1146 static const char h_gpu_1[] = "Capcom fighting games";
1147 static const char h_gpu_2[] = "Black screens in Lunar";
1148 static const char h_gpu_3[] = "Compatibility mode";
1149 static const char h_gpu_6[] = "Pandemonium 2";
1150 //static const char h_gpu_7[] = "Skip every second frame";
1151 static const char h_gpu_8[] = "Needed by Dark Forces";
1152 static const char h_gpu_9[] = "better g-colors, worse textures";
1153 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1155 static menu_entry e_menu_plugin_gpu_peops[] =
1157 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1158 // mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1159 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1160 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1161 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1162 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1163 // mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1164 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1165 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1166 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1170 static int menu_loop_plugin_gpu_peops(int id, int keys)
1173 me_loop(e_menu_plugin_gpu_peops, &sel);
1177 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1178 "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1179 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1181 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1183 mee_onoff ("Dithering", 0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1184 mee_enum ("Texture Filtering", 0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1185 mee_enum ("Framebuffer Textures", 0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1186 mee_onoff ("Mask Detect", 0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1187 mee_onoff ("Opaque Pass", 0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1188 mee_onoff ("Advanced Blend", 0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1189 mee_onoff ("Use Fast Mdec", 0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1190 mee_range ("Texture RAM size (MB)", 0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1191 mee_onoff ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1192 mee_label ("Fixes/hacks:"),
1193 mee_onoff ("FF7 cursor", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1194 mee_onoff ("Direct FB updates", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1195 mee_onoff ("Black brightness", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1196 mee_onoff ("Swap front detection", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1197 mee_onoff ("Disable coord check", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1198 mee_onoff ("No blue glitches (LoD)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1199 mee_onoff ("Soft FB access", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1200 mee_onoff ("FF9 rect", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1201 mee_onoff ("No subtr. blending", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1202 mee_onoff ("Lazy upload (DW7)", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1203 mee_onoff ("Additional uploads", 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1207 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1210 me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1214 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1215 static const char h_spu_volboost[] = "Large values cause distortion";
1216 static const char h_spu_irq_wait[] = "Wait for CPU (recommended set to ON)";
1218 static menu_entry e_menu_plugin_spu[] =
1220 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1221 mee_onoff ("Reverb", 0, iUseReverb, 2),
1222 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
1223 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
1224 mee_onoff_h ("SPU IRQ Wait", 0, iSPUIRQWait, 1, h_spu_irq_wait),
1228 static int menu_loop_plugin_spu(int id, int keys)
1231 me_loop(e_menu_plugin_spu, &sel);
1235 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1236 "savestates and can't be changed there. Must save\n"
1237 "config and reload the game for change to take effect";
1238 static const char h_plugin_gpu[] =
1240 "builtin_gpu is the NEON GPU, very fast and accurate\n"
1245 "is Pete's soft GPU, slow but accurate\n"
1246 "gpuPCSX4ALL is GPU from PCSX4ALL, fast but glitchy\n"
1247 "gpuGLES Pete's hw GPU, uses 3D chip but is glitchy\n"
1248 "must save config and reload the game if changed";
1249 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1250 "must save config and reload the game if changed";
1251 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1252 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1253 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team GPU plugin";
1254 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1256 static menu_entry e_menu_plugin_options[] =
1258 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1259 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1260 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_spu),
1262 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1264 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1265 mee_handler_h ("Configure PCSX4ALL GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1266 mee_handler_h ("Configure GLES GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1267 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1271 static menu_entry e_menu_main2[];
1273 static int menu_loop_plugin_options(int id, int keys)
1276 me_loop(e_menu_plugin_options, &sel);
1278 // sync BIOS/plugins
1279 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1280 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1281 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1282 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1287 // ------------ adv options menu ------------
1289 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1290 "(lower value - less work for the emu, may be faster)";
1291 static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
1292 static const char h_cfg_gteunn[] = "May cause graphical glitches";
1293 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1295 static menu_entry e_menu_speed_hacks[] =
1297 mee_range_h ("PSX CPU clock, %%", 0, psx_clock, 1, 500, h_cfg_psxclk),
1298 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1299 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1300 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1304 static int menu_loop_speed_hacks(int id, int keys)
1307 me_loop(e_menu_speed_hacks, &sel);
1311 static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
1312 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1313 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1314 "(green: normal, red: fmod, blue: noise)";
1315 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1316 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1317 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1318 "(proper .cue/.bin dump is needed otherwise)";
1319 static const char h_cfg_sio[] = "You should not need this, breaks games";
1320 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1321 static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1322 "(timing hack, breaks other games)";
1323 static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix\n"
1324 "(timing hack, breaks other games)";
1325 static const char h_cfg_cdrr[] = "Compatibility tweak (CD timing hack, breaks FMVs)";
1326 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1327 "Might be useful to overcome some dynarec bugs";
1328 static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
1329 "must reload game for any change to take effect";
1331 static menu_entry e_menu_adv_options[] =
1333 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1334 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1335 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1336 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1337 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1338 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1339 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1340 //mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1341 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
1342 mee_enum_h ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
1343 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1344 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
1348 static int menu_loop_adv_options(int id, int keys)
1351 me_loop(e_menu_adv_options, &sel);
1355 // ------------ options menu ------------
1357 static int mh_restore_defaults(int id, int keys)
1359 menu_set_defconfig();
1360 me_update_msg("defaults restored");
1364 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1365 static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
1367 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1368 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1369 "loading state or both";
1371 static const char h_restore_def[] = "Switches back to default / recommended\n"
1373 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1375 static menu_entry e_menu_options[] =
1377 // mee_range ("Save slot", 0, state_slot, 0, 9),
1378 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1379 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
1380 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1381 mee_enum ("Region", 0, region, men_region),
1382 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1383 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1384 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1385 mee_handler ("[Advanced]", menu_loop_adv_options),
1386 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1387 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1388 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1392 static int menu_loop_options(int id, int keys)
1397 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1398 e_menu_options[i].enabled = cpu_clock_st > 0 ? 1 : 0;
1399 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1401 me_loop(e_menu_options, &sel);
1406 // ------------ debug menu ------------
1408 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1410 int w = min(g_menuscreen_w, 1024);
1411 int h = min(g_menuscreen_h, 512);
1412 u16 *d = g_menuscreen_ptr;
1413 u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1417 gpuf->ulFreezeVersion = 1;
1418 if (GPU_freeze != NULL)
1419 GPU_freeze(1, gpuf);
1421 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1422 bgr555_to_rgb565(d, s, w * 2);
1424 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1425 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1426 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1427 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1428 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1431 static void debug_menu_loop(void)
1433 int inp, df_x = 0, df_y = 0;
1436 gpuf = malloc(sizeof(*gpuf));
1443 draw_frame_debug(gpuf, df_x, df_y);
1446 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1447 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 10);
1448 if (inp & PBTN_MBACK) break;
1449 else if (inp & PBTN_UP) { if (df_y > 0) df_y--; }
1450 else if (inp & PBTN_DOWN) { if (df_y < 512 - g_menuscreen_h) df_y++; }
1451 else if (inp & PBTN_LEFT) { if (df_x > 0) df_x--; }
1452 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x++; }
1458 // --------- memcard manager ---------
1460 static void draw_mc_icon(int dx, int dy, const u16 *s)
1465 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1467 for (y = 0; y < 16; y++, s += 16) {
1468 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1469 for (x = 0; x < 16; x++) {
1471 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1472 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1478 static void draw_mc_bg(void)
1480 McdBlock *blocks1, *blocks2;
1484 blocks1 = malloc(15 * sizeof(blocks1[0]));
1485 blocks2 = malloc(15 * sizeof(blocks1[0]));
1486 if (blocks1 == NULL || blocks2 == NULL)
1489 for (i = 0; i < 15; i++) {
1490 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1491 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1496 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1498 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1502 maxicons = g_menuscreen_h / 32;
1505 row2 = g_menuscreen_w / 2;
1506 for (i = 0; i < maxicons; i++) {
1507 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1508 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1510 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1511 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1514 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1522 static void handle_memcard_sel(void)
1525 if (memcard1_sel != 0)
1526 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1528 if (memcard2_sel != 0)
1529 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1530 LoadMcds(Config.Mcd1, Config.Mcd2);
1534 static menu_entry e_memcard_options[] =
1536 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1537 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1541 static int menu_loop_memcards(int id, int keys)
1547 memcard1_sel = memcard2_sel = 0;
1548 p = strrchr(Config.Mcd1, '/');
1550 for (i = 0; memcards[i] != NULL; i++)
1551 if (strcmp(p + 1, memcards[i]) == 0)
1552 { memcard1_sel = i; break; }
1553 p = strrchr(Config.Mcd2, '/');
1555 for (i = 0; memcards[i] != NULL; i++)
1556 if (strcmp(p + 1, memcards[i]) == 0)
1557 { memcard2_sel = i; break; }
1559 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1561 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1566 // --------- main menu help ----------
1568 static void menu_bios_warn(void)
1571 static const char msg[] =
1572 "You don't seem to have copied any BIOS\n"
1574 #ifdef __ARM_ARCH_7A__ // XXX
1575 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1577 "pcsx_rearmed/bios/\n\n"
1579 "While many games work fine with fake\n"
1580 "(HLE) BIOS, others (like MGS and FF8)\n"
1581 "require BIOS to work.\n"
1582 "After copying the file, you'll also need\n"
1583 "to select it in the emu's menu:\n"
1584 "options->[BIOS/Plugins]\n\n"
1585 "The file is usually named SCPH1001.BIN,\n"
1586 "but other not compressed files can be\n"
1588 "Press %s or %s to continue";
1589 char tmp_msg[sizeof(msg) + 64];
1591 snprintf(tmp_msg, sizeof(tmp_msg), msg,
1592 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1595 draw_menu_message(tmp_msg, NULL);
1597 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1598 if (inp & (PBTN_MBACK|PBTN_MOK))
1603 // ------------ main menu ------------
1607 static void draw_frame_main(void)
1616 if (CdromId[0] != 0) {
1617 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1618 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1619 Config.HLE ? "HLE" : "BIOS");
1620 smalltext_out16(4, 1, buff, 0x105f);
1624 capacity = plat_get_bat_capacity();
1626 tmp = localtime(<ime);
1627 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1628 if (capacity >= 0) {
1629 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1634 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1638 static void draw_frame_credits(void)
1640 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1643 static const char credits_text[] =
1645 "(C) 1999-2003 PCSX Team\n"
1646 "(C) 2005-2009 PCSX-df Team\n"
1647 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1648 "ARM recompiler (C) 2009-2011 Ari64\n"
1650 "ARM NEON GPU (c) 2011-2012 Exophase\n"
1652 "PEOpS GPU and SPU by Pete Bernert\n"
1653 " and the P.E.Op.S. team\n"
1654 "PCSX4ALL plugin by PCSX4ALL team\n"
1655 " Chui, Franxis, Unai\n\n"
1656 "integration, optimization and\n"
1657 " frontend (C) 2010-2012 notaz\n";
1659 static int reset_game(void)
1662 if (bios_sel == 0 && !Config.HLE)
1668 if (CheckCdrom() != -1) {
1674 static int reload_plugins(const char *cdimg)
1680 set_cd_image(cdimg);
1682 pcnt_hook_plugins();
1684 if (OpenPlugins() == -1) {
1685 me_update_msg("failed to open plugins");
1688 plugin_call_rearmed_cbs();
1690 cdrIsoMultidiskCount = 1;
1692 CdromLabel[0] = '\0';
1697 static int run_bios(void)
1703 if (reload_plugins(NULL) != 0)
1711 static int run_exe(void)
1715 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1720 if (reload_plugins(NULL) != 0)
1724 if (Load(fname) != 0) {
1725 me_update_msg("exe load failed, bad file?");
1734 static int run_cd_image(const char *fname)
1737 reload_plugins(fname);
1739 // always autodetect, menu_sync_config will override as needed
1742 if (CheckCdrom() == -1) {
1743 // Only check the CD if we are starting the console with a CD
1745 me_update_msg("unsupported/invalid CD image");
1751 // Read main executable directly from CDRom and start it
1752 if (LoadCdrom() == -1) {
1754 me_update_msg("failed to load CD image");
1759 snprintf(hud_msg, sizeof(hud_msg), "Booting up...");
1764 static int romsel_run(void)
1766 int prev_gpu, prev_spu;
1769 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1773 printf("selected file: %s\n", fname);
1775 new_dynarec_clear_full();
1777 if (run_cd_image(fname) != 0)
1780 prev_gpu = gpu_plugsel;
1781 prev_spu = spu_plugsel;
1782 if (menu_load_config(1) != 0)
1783 menu_load_config(0);
1785 // check for plugin changes, have to repeat
1786 // loading if game config changed plugins to reload them
1787 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1788 printf("plugin change detected, reloading plugins..\n");
1789 if (run_cd_image(fname) != 0)
1794 printf("note: running without BIOS, expect compatibility problems\n");
1796 strcpy(last_selected_fname, rom_fname_reload);
1800 static int swap_cd_image(void)
1804 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1808 printf("selected file: %s\n", fname);
1811 CdromLabel[0] = '\0';
1813 set_cd_image(fname);
1814 if (ReloadCdromPlugin() < 0) {
1815 me_update_msg("failed to load cdr plugin");
1818 if (CDR_open() < 0) {
1819 me_update_msg("failed to open cdr plugin");
1823 SetCdOpenCaseTime(time(NULL) + 2);
1826 strcpy(last_selected_fname, rom_fname_reload);
1830 static int swap_cd_multidisk(void)
1832 cdrIsoMultidiskSelect++;
1834 CdromLabel[0] = '\0';
1837 if (CDR_open() < 0) {
1838 me_update_msg("failed to open cdr plugin");
1842 SetCdOpenCaseTime(time(NULL) + 2);
1848 static int main_menu_handler(int id, int keys)
1852 case MA_MAIN_RESUME_GAME:
1856 case MA_MAIN_SAVE_STATE:
1858 return menu_loop_savestate(0);
1860 case MA_MAIN_LOAD_STATE:
1862 return menu_loop_savestate(1);
1864 case MA_MAIN_RESET_GAME:
1865 if (ready_to_go && reset_game() == 0)
1868 case MA_MAIN_LOAD_ROM:
1869 if (romsel_run() == 0)
1872 case MA_MAIN_SWAP_CD:
1873 if (swap_cd_image() == 0)
1876 case MA_MAIN_SWAP_CD_MULTI:
1877 if (swap_cd_multidisk() == 0)
1880 case MA_MAIN_RUN_BIOS:
1881 if (run_bios() == 0)
1884 case MA_MAIN_RUN_EXE:
1888 case MA_MAIN_CREDITS:
1889 draw_menu_message(credits_text, draw_frame_credits);
1890 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1896 lprintf("%s: something unknown selected\n", __FUNCTION__);
1903 static menu_entry e_menu_main2[] =
1905 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1906 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
1907 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1908 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
1909 mee_handler ("Memcard manager", menu_loop_memcards),
1913 static int main_menu2_handler(int id, int keys)
1917 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
1918 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
1919 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1921 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1924 static const char h_extra[] = "Change CD, manage memcards..\n";
1926 static menu_entry e_menu_main[] =
1930 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1931 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1932 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1933 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1934 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1935 mee_handler ("Options", menu_loop_options),
1936 mee_handler ("Controls", menu_loop_keyconfig),
1937 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
1938 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1939 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1943 // ----------------------------
1945 static void menu_leave_emu(void);
1947 void menu_loop(void)
1953 if (bioses[1] == NULL && !warned_about_bios) {
1955 warned_about_bios = 1;
1958 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1959 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1960 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
1961 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1963 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1966 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
1967 } while (!ready_to_go);
1969 /* wait until menu, ok, back is released */
1970 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1973 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1978 static int qsort_strcmp(const void *p1, const void *p2)
1980 char * const *s1 = (char * const *)p1;
1981 char * const *s2 = (char * const *)p2;
1982 return strcasecmp(*s1, *s2);
1985 static void scan_bios_plugins(void)
1987 char fname[MAXPATHLEN];
1989 int bios_i, gpu_i, spu_i, mc_i;
1994 gpu_plugins[0] = "builtin_gpu";
1995 spu_plugins[0] = "builtin_spu";
1996 memcards[0] = "(none)";
1997 bios_i = gpu_i = spu_i = mc_i = 1;
1999 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2000 dir = opendir(fname);
2002 perror("scan_bios_plugins bios opendir");
2017 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2020 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2021 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2022 printf("bad BIOS file: %s\n", ent->d_name);
2026 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2027 bioses[bios_i++] = strdup(ent->d_name);
2031 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2037 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2038 dir = opendir(fname);
2040 perror("scan_bios_plugins plugins opendir");
2054 p = strstr(ent->d_name, ".so");
2058 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2059 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2061 fprintf(stderr, "%s\n", dlerror());
2065 // now what do we have here?
2066 tmp = dlsym(h, "GPUinit");
2069 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2070 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2074 tmp = dlsym(h, "SPUinit");
2077 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2078 spu_plugins[spu_i++] = strdup(ent->d_name);
2082 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2089 dir = opendir("." MEMCARD_DIR);
2091 perror("scan_bios_plugins memcards opendir");
2106 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2109 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2110 if (stat(fname, &st) != 0) {
2111 printf("bad memcard file: %s\n", ent->d_name);
2115 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2116 memcards[mc_i++] = strdup(ent->d_name);
2120 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2124 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2129 void menu_init(void)
2131 char buff[MAXPATHLEN];
2133 strcpy(last_selected_fname, "/media");
2135 cpu_clock_st = cpu_clock = plat_cpu_clock_get();
2137 scan_bios_plugins();
2140 menu_set_defconfig();
2141 menu_load_config(0);
2146 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2147 g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2148 if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2149 fprintf(stderr, "OOM\n");
2153 emu_make_path(buff, "skin/background.png", sizeof(buff));
2154 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2156 #ifndef __ARM_ARCH_7A__ /* XXX */
2157 me_enable(e_menu_gfx_options, MA_OPT_SCALER, 0);
2158 me_enable(e_menu_gfx_options, MA_OPT_FILTERING, 0);
2159 me_enable(e_menu_gfx_options, MA_OPT_SCALER_C, 0);
2160 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2162 me_enable(e_menu_gfx_options, MA_OPT_SCALER2, 0);
2163 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
2164 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2168 void menu_notify_mode_change(int w, int h, int bpp)
2177 // XXX: should really menu code cotrol the layer size?
2180 g_layer_w = w; g_layer_h = h;
2184 if (h > g_menuscreen_h || (240 < h && h <= 360))
2185 goto fractional_4_3;
2187 // 4:3 that prefers integer scaling
2188 imult = g_menuscreen_h / h;
2189 g_layer_w = w * imult;
2190 g_layer_h = h * imult;
2191 mult = (float)g_layer_w / (float)g_layer_h;
2192 if (mult < 1.25f || mult > 1.666f)
2193 g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2194 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2199 mult = 240.0f / (float)h * 4.0f / 3.0f;
2202 g_layer_w = mult * (float)g_menuscreen_h;
2203 g_layer_h = g_menuscreen_h;
2204 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2207 case SCALE_FULLSCREEN:
2208 g_layer_w = g_menuscreen_w;
2209 g_layer_h = g_menuscreen_h;
2216 g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2217 g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2218 if (g_layer_x < 0) g_layer_x = 0;
2219 if (g_layer_y < 0) g_layer_y = 0;
2220 if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2221 if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2224 static void menu_leave_emu(void)
2226 if (GPU_close != NULL) {
2227 int ret = GPU_close();
2229 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2232 plat_video_menu_enter(ready_to_go);
2234 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2235 if (pl_vout_buf != NULL && ready_to_go) {
2236 int x = max(0, g_menuscreen_w - last_psx_w);
2237 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2238 int w = min(g_menuscreen_w, last_psx_w);
2239 int h = min(g_menuscreen_h, last_psx_h);
2240 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2241 char *s = pl_vout_buf;
2243 if (last_psx_bpp == 16) {
2244 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w * 2)
2245 menu_darken_bg(d, s, w, 0);
2248 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w * 3) {
2249 bgr888_to_rgb565(d, s, w * 3);
2250 menu_darken_bg(d, d, w, 0);
2256 cpu_clock = plat_cpu_clock_get();
2259 void menu_prepare_emu(void)
2261 R3000Acpu *prev_cpu = psxCpu;
2263 plat_video_menu_leave();
2265 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2267 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2268 if (psxCpu != prev_cpu)
2269 // note that this does not really reset, just clears drc caches
2272 // core doesn't care about Config.Cdda changes,
2273 // so handle them manually here
2279 plat_cpu_clock_apply(cpu_clock);
2281 // push config to GPU plugin
2282 plugin_call_rearmed_cbs();
2284 if (GPU_open != NULL) {
2285 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2287 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2293 void me_update_msg(const char *msg)
2295 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2296 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2298 menu_error_time = plat_get_ticks_ms();
2299 lprintf("msg: %s\n", menu_error_msg);
2302 void menu_finish(void)
2304 plat_cpu_clock_apply(cpu_clock_st);