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.
21 #include "plugin_lib.h"
25 #include "arm_utils.h"
26 #include "common/plat.h"
27 #include "common/input.h"
28 #include "linux/in_evdev.h"
29 #include "../libpcsxcore/misc.h"
30 #include "../libpcsxcore/cdrom.h"
31 #include "../libpcsxcore/psemu_plugin_defs.h"
32 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
33 #include "../plugins/dfinput/main.h"
37 #define array_size(x) (sizeof(x) / sizeof(x[0]))
73 static int last_psx_w, last_psx_h, last_psx_bpp;
74 static int scaling, filter, cpu_clock, cpu_clock_st, volume_boost;
75 static char rom_fname_reload[MAXPATHLEN];
76 static char last_selected_fname[MAXPATHLEN];
77 static int warned_about_bios, region, in_type_sel1, in_type_sel2;
78 static int memcard1_sel, memcard2_sel;
82 extern int iUseReverb;
83 extern int iUseInterpolation;
85 extern int iSPUIRQWait;
89 static const char *bioses[24];
90 static const char *gpu_plugins[16];
91 static const char *spu_plugins[16];
92 static const char *memcards[32];
93 static int bios_sel, gpu_plugsel, spu_plugsel;
96 static int min(int x, int y) { return x < y ? x : y; }
97 static int max(int x, int y) { return x > y ? x : y; }
99 void emu_make_path(char *buff, const char *end, int size)
103 end_len = strlen(end);
104 pos = plat_get_root_dir(buff, size);
105 strncpy(buff + pos, end, size - pos);
107 if (pos + end_len > size - 1)
108 printf("Warning: path truncated: %s\n", buff);
111 static int emu_check_save_file(int slot)
113 int ret = emu_check_state(slot);
114 return ret == 0 ? 1 : 0;
117 static int emu_save_load_game(int load, int unused)
122 ret = emu_load_state(state_slot);
124 // reflect hle/bios mode from savestate
127 else if (bios_sel == 0 && bioses[1] != NULL)
128 // XXX: maybe find the right bios instead
132 ret = emu_save_state(state_slot);
137 // propagate menu settings to the emu vars
138 static void menu_sync_config(void)
140 static int allow_abs_only_old;
145 Config.PsxType = region - 1;
147 switch (in_type_sel1) {
148 case 1: in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
149 case 2: in_type1 = PSE_PAD_TYPE_GUNCON; break;
150 default: in_type1 = PSE_PAD_TYPE_STANDARD;
152 switch (in_type_sel2) {
153 case 1: in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
154 case 2: in_type2 = PSE_PAD_TYPE_GUNCON; break;
155 default: in_type2 = PSE_PAD_TYPE_STANDARD;
157 if (in_evdev_allow_abs_only != allow_abs_only_old) {
158 pandora_rescan_inputs();
159 allow_abs_only_old = in_evdev_allow_abs_only;
162 iVolume = 768 + 128 * volume_boost;
163 pl_timing_prepare(Config.PsxType);
166 static void menu_set_defconfig(void)
173 in_type_sel1 = in_type_sel2 = 0;
174 in_evdev_allow_abs_only = 0;
175 Config.Xa = Config.Cdda = Config.Sio =
176 Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
177 Config.CdrReschedule = 0;
179 pl_rearmed_cbs.frameskip = 0;
180 pl_rearmed_cbs.gpu_peops.iUseDither = 0;
181 pl_rearmed_cbs.gpu_peops.dwActFixes = 1<<7;
182 pl_rearmed_cbs.gpu_unai.abe_hack =
183 pl_rearmed_cbs.gpu_unai.no_light =
184 pl_rearmed_cbs.gpu_unai.no_blend = 0;
187 iUseInterpolation = 1;
195 #define CE_CONFIG_STR(val) \
196 { #val, 0, Config.val }
198 #define CE_CONFIG_VAL(val) \
199 { #val, sizeof(Config.val), &Config.val }
201 #define CE_STR(val) \
204 #define CE_INTVAL(val) \
205 { #val, sizeof(val), &val }
207 #define CE_INTVAL_P(val) \
208 { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
210 // 'versioned' var, used when defaults change
211 #define CE_INTVAL_V(val, ver) \
212 { #val #ver, sizeof(val), &val }
214 static const struct {
222 // CE_CONFIG_STR(Cdr),
227 CE_CONFIG_VAL(Debug),
228 CE_CONFIG_VAL(PsxOut),
229 CE_CONFIG_VAL(SpuIrq),
230 CE_CONFIG_VAL(RCntFix),
231 CE_CONFIG_VAL(VSyncWA),
233 CE_CONFIG_VAL(CdrReschedule),
235 CE_INTVAL_V(scaling, 2),
236 CE_INTVAL(g_layer_x),
237 CE_INTVAL(g_layer_y),
238 CE_INTVAL(g_layer_w),
239 CE_INTVAL(g_layer_h),
241 CE_INTVAL(state_slot),
242 CE_INTVAL(cpu_clock),
244 CE_INTVAL(in_type_sel1),
245 CE_INTVAL(in_type_sel2),
246 CE_INTVAL_P(frameskip),
247 CE_INTVAL_P(gpu_peops.iUseDither),
248 CE_INTVAL_P(gpu_peops.dwActFixes),
249 CE_INTVAL_P(gpu_unai.abe_hack),
250 CE_INTVAL_P(gpu_unai.no_light),
251 CE_INTVAL_P(gpu_unai.no_blend),
252 CE_INTVAL(iUseReverb),
254 CE_INTVAL_V(iUseInterpolation, 2),
255 CE_INTVAL_V(iSPUIRQWait, 2),
256 CE_INTVAL(iUseTimer),
257 CE_INTVAL(warned_about_bios),
258 CE_INTVAL(in_evdev_allow_abs_only),
259 CE_INTVAL(volume_boost),
262 static char *get_cd_label(void)
264 static char trimlabel[33];
267 strncpy(trimlabel, CdromLabel, 32);
269 for (j = 31; j >= 0; j--)
270 if (trimlabel[j] == ' ')
276 static void make_cfg_fname(char *buf, size_t size, int is_game)
279 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
281 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
284 static void keys_write_all(FILE *f);
286 static int menu_write_config(int is_game)
288 char cfgfile[MAXPATHLEN];
292 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
293 f = fopen(cfgfile, "w");
295 printf("menu_write_config: failed to open: %s\n", cfgfile);
299 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
300 fprintf(f, "%s = ", config_data[i].name);
301 switch (config_data[i].len) {
303 fprintf(f, "%s\n", (char *)config_data[i].val);
306 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
309 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
312 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
315 printf("menu_write_config: unhandled len %d for %s\n",
316 config_data[i].len, config_data[i].name);
322 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
330 static void parse_str_val(char *cval, const char *src)
333 strncpy(cval, src, MAXPATHLEN);
334 cval[MAXPATHLEN - 1] = 0;
335 tmp = strchr(cval, '\n');
337 tmp = strchr(cval, '\r');
342 static void keys_load_all(const char *cfg);
344 static int menu_load_config(int is_game)
346 char cfgfile[MAXPATHLEN];
352 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
353 f = fopen(cfgfile, "r");
355 printf("menu_load_config: failed to open: %s\n", cfgfile);
359 fseek(f, 0, SEEK_END);
362 printf("bad size %ld: %s\n", size, cfgfile);
366 cfg = malloc(size + 1);
370 fseek(f, 0, SEEK_SET);
371 if (fread(cfg, 1, size, f) != size) {
372 printf("failed to read: %s\n", cfgfile);
377 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
381 tmp = strstr(cfg, config_data[i].name);
384 tmp += strlen(config_data[i].name);
385 if (strncmp(tmp, " = ", 3) != 0)
389 if (config_data[i].len == 0) {
390 parse_str_val(config_data[i].val, tmp);
395 val = strtoul(tmp, &tmp2, 16);
396 if (tmp2 == NULL || tmp == tmp2)
397 continue; // parse failed
399 switch (config_data[i].len) {
401 *(u8 *)config_data[i].val = val;
404 *(u16 *)config_data[i].val = val;
407 *(u32 *)config_data[i].val = val;
410 printf("menu_load_config: unhandled len %d for %s\n",
411 config_data[i].len, config_data[i].name);
417 char *tmp = strstr(cfg, "lastcdimg = ");
420 parse_str_val(last_selected_fname, tmp);
427 for (i = bios_sel = 0; bioses[i] != NULL; i++)
428 if (strcmp(Config.Bios, bioses[i]) == 0)
429 { bios_sel = i; break; }
431 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
432 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
433 { gpu_plugsel = i; break; }
435 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
436 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
437 { spu_plugsel = i; break; }
448 // rrrr rggg gggb bbbb
449 static unsigned short fname2color(const char *fname)
451 static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z", ".bz", ".znx", ".pbp" };
452 static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub", ".table", ".index", ".sbi" };
453 const char *ext = strrchr(fname, '.');
458 for (i = 0; i < array_size(cdimg_exts); i++)
459 if (strcasecmp(ext, cdimg_exts[i]) == 0)
461 for (i = 0; i < array_size(other_exts); i++)
462 if (strcasecmp(ext, other_exts[i]) == 0)
467 static void draw_savestate_bg(int slot);
469 static const char *filter_exts[] = {
470 ".mp3", ".MP3", ".txt", ".htm", "html", ".jpg", ".pnd"
473 #define MENU_ALIGN_LEFT
474 #define menu_init menu_init_common
475 #include "common/menu.c"
478 // a bit of black magic here
479 static void draw_savestate_bg(int slot)
481 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
483 char fname[MAXPATHLEN];
490 ret = get_state_filename(fname, sizeof(fname), slot);
494 f = gzopen(fname, "rb");
498 if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
499 fprintf(stderr, "gzseek failed\n");
504 gpu = malloc(sizeof(*gpu));
510 ret = gzread(f, gpu, sizeof(*gpu));
512 if (ret != sizeof(*gpu)) {
513 fprintf(stderr, "gzread failed\n");
517 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
519 if (gpu->ulStatus & 0x800000)
520 goto out; // disabled
522 x = gpu->ulControl[5] & 0x3ff;
523 y = (gpu->ulControl[5] >> 10) & 0x1ff;
524 s = (u16 *)gpu->psxVRam + y * 1024 + (x & ~1);
525 w = psx_widths[(gpu->ulStatus >> 16) & 7];
526 tmp = gpu->ulControl[7];
527 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
528 if (gpu->ulStatus & 0x80000) // doubleheight
531 x = max(0, g_menuscreen_w - w) & ~3;
532 y = max(0, g_menuscreen_h / 2 - h / 2);
533 w = min(g_menuscreen_w, w);
534 h = min(g_menuscreen_h, h);
535 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
537 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
538 if (gpu->ulStatus & 0x200000)
539 bgr888_to_rgb565(d, s, w * 3);
541 bgr555_to_rgb565(d, s, w * 2);
547 // ---------- pandora specific -----------
549 static const char pnd_script_base[] = "sudo -n /usr/pandora/scripts";
550 static char **pnd_filter_list;
552 static int get_cpu_clock(void)
556 f = fopen("/proc/pandora/cpu_mhz_max", "r");
558 fscanf(f, "%d", &ret);
564 static void apply_cpu_clock(void)
568 if (cpu_clock != 0 && cpu_clock != get_cpu_clock()) {
569 snprintf(buf, sizeof(buf), "unset DISPLAY; echo y | %s/op_cpuspeed.sh %d",
570 pnd_script_base, cpu_clock);
575 static void apply_filter(int which)
581 if (pnd_filter_list == NULL || which == old)
584 for (i = 0; i < which; i++)
585 if (pnd_filter_list[i] == NULL)
588 if (pnd_filter_list[i] == NULL)
591 snprintf(buf, sizeof(buf), "%s/op_videofir.sh %s", pnd_script_base, pnd_filter_list[i]);
596 static void apply_lcdrate(int pal)
604 snprintf(buf, sizeof(buf), "%s/op_lcdrate.sh %d",
605 pnd_script_base, pal ? 50 : 60);
610 static int get_bat_capacity(void)
614 f = fopen("/sys/class/power_supply/bq27500-0/capacity", "r");
616 fscanf(f, "%d", &ret);
622 static menu_entry e_menu_gfx_options[];
624 static void pnd_menu_init(void)
632 cpu_clock_st = cpu_clock = get_cpu_clock();
634 dir = opendir("/etc/pandora/conf/dss_fir");
636 perror("filter opendir");
649 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
658 mfilters = calloc(count + 1, sizeof(mfilters[0]));
659 if (mfilters == NULL)
663 for (i = 0; (ent = readdir(dir)); ) {
666 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
669 len = strlen(ent->d_name);
671 // skip pre-HF5 extra files
672 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
674 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
677 // have to cut "_up_h" for pre-HF5
678 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
681 if (len > sizeof(buff) - 1)
684 strncpy(buff, ent->d_name, len);
686 mfilters[i] = strdup(buff);
687 if (mfilters[i] != NULL)
692 i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
693 e_menu_gfx_options[i].data = (void *)mfilters;
694 pnd_filter_list = mfilters;
697 void menu_finish(void)
699 cpu_clock = cpu_clock_st;
703 // -------------- key config --------------
705 me_bind_action me_ctrl_actions[] =
707 { "UP ", 1 << DKEY_UP},
708 { "DOWN ", 1 << DKEY_DOWN },
709 { "LEFT ", 1 << DKEY_LEFT },
710 { "RIGHT ", 1 << DKEY_RIGHT },
711 { "TRIANGLE", 1 << DKEY_TRIANGLE },
712 { "CIRCLE ", 1 << DKEY_CIRCLE },
713 { "CROSS ", 1 << DKEY_CROSS },
714 { "SQUARE ", 1 << DKEY_SQUARE },
715 { "L1 ", 1 << DKEY_L1 },
716 { "R1 ", 1 << DKEY_R1 },
717 { "L2 ", 1 << DKEY_L2 },
718 { "R2 ", 1 << DKEY_R2 },
719 { "L3 ", 1 << DKEY_L3 },
720 { "R3 ", 1 << DKEY_R3 },
721 { "START ", 1 << DKEY_START },
722 { "SELECT ", 1 << DKEY_SELECT },
726 me_bind_action emuctrl_actions[] =
728 { "Save State ", 1 << SACTION_SAVE_STATE },
729 { "Load State ", 1 << SACTION_LOAD_STATE },
730 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
731 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
732 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
733 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
734 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
735 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
736 { "Gun A button ", 1 << SACTION_GUN_A },
737 { "Gun B button ", 1 << SACTION_GUN_B },
738 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
742 static char *mystrip(char *str)
747 for (i = 0; i < len; i++)
748 if (str[i] != ' ') break;
749 if (i > 0) memmove(str, str + i, len - i + 1);
752 for (i = len - 1; i >= 0; i--)
753 if (str[i] != ' ') break;
759 static void get_line(char *d, size_t size, const char *s)
764 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
775 static void keys_write_all(FILE *f)
779 for (d = 0; d < IN_MAX_DEVS; d++)
781 const int *binds = in_get_dev_binds(d);
782 const char *name = in_get_dev_name(d, 0, 0);
785 if (binds == NULL || name == NULL)
788 fprintf(f, "binddev = %s\n", name);
789 in_get_config(d, IN_CFG_BIND_COUNT, &count);
791 for (k = 0; k < count; k++)
796 act[0] = act[31] = 0;
797 name = in_get_key_name(d, k);
799 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
800 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
801 mask = me_ctrl_actions[i].mask;
803 strncpy(act, me_ctrl_actions[i].name, 31);
804 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
807 mask = me_ctrl_actions[i].mask << 16;
809 strncpy(act, me_ctrl_actions[i].name, 31);
810 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
815 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
816 for (i = 0; kbinds && i < ARRAY_SIZE(emuctrl_actions) - 1; i++) {
817 mask = emuctrl_actions[i].mask;
819 strncpy(act, emuctrl_actions[i].name, 31);
820 fprintf(f, "bind %s = %s\n", name, mystrip(act));
828 static int parse_bind_val(const char *val, int *type)
832 *type = IN_BINDTYPE_NONE;
836 if (strncasecmp(val, "player", 6) == 0)
838 int player, shift = 0;
839 player = atoi(val + 6) - 1;
841 if ((unsigned int)player > 1)
846 *type = IN_BINDTYPE_PLAYER12;
847 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
848 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
849 return me_ctrl_actions[i].mask << shift;
852 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
853 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
854 *type = IN_BINDTYPE_EMU;
855 return emuctrl_actions[i].mask;
862 static void keys_load_all(const char *cfg)
864 char dev[256], key[128], *act;
870 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
873 get_line(dev, sizeof(dev), p);
874 dev_id = in_config_parse_dev(dev);
876 printf("input: can't handle dev: %s\n", dev);
880 in_unbind_all(dev_id, -1, -1);
881 while ((p = strstr(p, "bind"))) {
882 if (strncmp(p, "binddev = ", 10) == 0)
887 printf("input: parse error: %16s..\n", p);
891 get_line(key, sizeof(key), p);
892 act = strchr(key, '=');
894 printf("parse failed: %16s..\n", p);
902 bind = parse_bind_val(act, &bindtype);
903 if (bind != -1 && bind != 0) {
904 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
905 in_config_bind_key(dev_id, key, bind, bindtype);
908 lprintf("config: unhandled action \"%s\"\n", act);
914 static int key_config_loop_wrap(int id, int keys)
917 case MA_CTRL_PLAYER1:
918 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
920 case MA_CTRL_PLAYER2:
921 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
924 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
932 static const char *mgn_dev_name(int id, int *offs)
934 const char *name = NULL;
937 if (id == MA_CTRL_DEV_FIRST)
940 for (; it < IN_MAX_DEVS; it++) {
941 name = in_get_dev_name(it, 1, 1);
950 static const char *mgn_saveloadcfg(int id, int *offs)
955 static int mh_savecfg(int id, int keys)
957 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
958 me_update_msg("config saved");
960 me_update_msg("failed to write config");
965 static int mh_input_rescan(int id, int keys)
967 //menu_sync_config();
968 pandora_rescan_inputs();
969 me_update_msg("rescan complete.");
974 static const char *men_in_type_sel[] = {
975 "Standard (SCPH-1080)",
976 "Analog (SCPH-1150)",
980 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
981 static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
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("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
989 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
990 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
991 mee_onoff_h ("Nubs as buttons", 0, in_evdev_allow_abs_only, 1, h_nub_btns),
992 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
993 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
994 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
995 mee_handler ("Rescan devices", mh_input_rescan),
997 mee_label ("Input devices:"),
998 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
999 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1000 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1001 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1002 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1003 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1004 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1008 static int menu_loop_keyconfig(int id, int keys)
1012 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1013 me_loop(e_menu_keyconfig, &sel);
1017 // ------------ gfx options menu ------------
1019 static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
1020 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1021 "using d-pad or move it using R+d-pad";
1022 static const char *men_dummy[] = { NULL };
1024 static int menu_loop_cscaler(int id, int keys)
1028 scaling = SCALE_CUSTOM;
1030 omap_enable_layer(1);
1035 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1036 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1037 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1040 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
1041 if (inp & PBTN_UP) g_layer_y--;
1042 if (inp & PBTN_DOWN) g_layer_y++;
1043 if (inp & PBTN_LEFT) g_layer_x--;
1044 if (inp & PBTN_RIGHT) g_layer_x++;
1045 if (!(inp & PBTN_R)) {
1046 if (inp & PBTN_UP) g_layer_h += 2;
1047 if (inp & PBTN_DOWN) g_layer_h -= 2;
1048 if (inp & PBTN_LEFT) g_layer_w += 2;
1049 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1051 if (inp & (PBTN_MOK|PBTN_MBACK))
1054 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1055 if (g_layer_x < 0) g_layer_x = 0;
1056 if (g_layer_x > 640) g_layer_x = 640;
1057 if (g_layer_y < 0) g_layer_y = 0;
1058 if (g_layer_y > 420) g_layer_y = 420;
1059 if (g_layer_w < 160) g_layer_w = 160;
1060 if (g_layer_h < 60) g_layer_h = 60;
1061 if (g_layer_x + g_layer_w > 800)
1062 g_layer_w = 800 - g_layer_x;
1063 if (g_layer_y + g_layer_h > 480)
1064 g_layer_h = 480 - g_layer_y;
1065 omap_enable_layer(1);
1069 omap_enable_layer(0);
1074 static menu_entry e_menu_gfx_options[] =
1076 mee_enum ("Scaler", 0, scaling, men_scaler),
1077 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
1078 // mee_onoff ("Vsync", 0, vsync, 1),
1079 mee_cust_h ("Setup custom scaler", 0, menu_loop_cscaler, NULL, h_cscaler),
1083 static int menu_loop_gfx_options(int id, int keys)
1087 me_loop(e_menu_gfx_options, &sel);
1092 // ------------ bios/plugins ------------
1094 static menu_entry e_menu_plugin_gpu_unai[] =
1096 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1097 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1098 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1102 static int menu_loop_plugin_gpu_unai(int id, int keys)
1105 me_loop(e_menu_plugin_gpu_unai, &sel);
1109 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1110 static const char h_gpu_0[] = "Needed for Chrono Cross";
1111 static const char h_gpu_1[] = "Capcom fighting games";
1112 static const char h_gpu_2[] = "Black screens in Lunar";
1113 static const char h_gpu_3[] = "Compatibility mode";
1114 static const char h_gpu_6[] = "Pandemonium 2";
1115 static const char h_gpu_7[] = "Skip every second frame";
1116 static const char h_gpu_8[] = "Needed by Dark Forces";
1117 static const char h_gpu_9[] = "better g-colors, worse textures";
1118 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1120 static menu_entry e_menu_plugin_gpu_peops[] =
1122 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1123 mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1124 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1125 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1126 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1127 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1128 mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1129 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1130 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1131 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1135 static int menu_loop_plugin_gpu_peops(int id, int keys)
1138 me_loop(e_menu_plugin_gpu_peops, &sel);
1142 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1143 static const char h_spu_volboost[] = "Large values cause distortion";
1144 static const char h_spu_irq_wait[] = "Wait for CPU (recommended set to ON)";
1145 static const char h_spu_thread[] = "Run sound emulation in main thread (recommended)";
1147 static menu_entry e_menu_plugin_spu[] =
1149 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
1150 mee_onoff ("Reverb", 0, iUseReverb, 2),
1151 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
1152 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
1153 mee_onoff_h ("SPU IRQ Wait", 0, iSPUIRQWait, 1, h_spu_irq_wait),
1154 mee_onoff_h ("Sound in main thread", 0, iUseTimer, 2, h_spu_thread),
1158 static int menu_loop_plugin_spu(int id, int keys)
1161 me_loop(e_menu_plugin_spu, &sel);
1165 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in savestates\n"
1166 "and can't be changed there. Must save config and reload\n"
1167 "the game for change to take effect";
1168 static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1169 "for plugin change to take effect";
1170 static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1171 static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team GPU plugin";
1172 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1174 static menu_entry e_menu_plugin_options[] =
1176 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1177 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1178 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_xpu),
1179 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1180 mee_handler_h ("Configure PCSX4ALL GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1181 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1185 static menu_entry e_menu_main2[];
1187 static int menu_loop_plugin_options(int id, int keys)
1190 me_loop(e_menu_plugin_options, &sel);
1192 // sync BIOS/plugins
1193 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1194 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1195 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1196 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1201 // ------------ adv options menu ------------
1203 static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
1204 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1205 static const char h_cfg_spu[] = "Shows active SPU channels\n"
1206 "(green: normal, red: fmod, blue: noise)";
1207 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1208 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1209 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1210 "(proper .cue/.bin dump is needed otherwise)";
1211 static const char h_cfg_sio[] = "You should not need this, breaks games";
1212 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1213 static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1214 "(timing hack, breaks other games)";
1215 static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix\n"
1216 "(timing hack, breaks other games)";
1217 static const char h_cfg_cdrr[] = "Compatibility tweak (fixes Team Buddies, maybe more)\n"
1218 "(CD timing hack, breaks FMVs)";
1219 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1220 "Might be useful to overcome some dynarec bugs";
1222 static menu_entry e_menu_adv_options[] =
1224 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1225 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1226 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1227 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1228 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1229 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1230 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1231 mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1232 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
1233 mee_enum_h ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
1234 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1238 static int menu_loop_adv_options(int id, int keys)
1241 me_loop(e_menu_adv_options, &sel);
1245 // ------------ options menu ------------
1247 static int mh_restore_defaults(int id, int keys)
1249 menu_set_defconfig();
1250 me_update_msg("defaults restored");
1254 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1256 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1257 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1258 "loading state or both";
1260 static const char h_restore_def[] = "Switches back to default / recommended\n"
1262 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1264 static menu_entry e_menu_options[] =
1266 // mee_range ("Save slot", 0, state_slot, 0, 9),
1267 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1268 mee_onoff_h ("Frameskip", 0, pl_rearmed_cbs.frameskip, 1, h_frameskip),
1269 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1270 mee_enum ("Region", 0, region, men_region),
1271 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1272 mee_handler ("[Display]", menu_loop_gfx_options),
1273 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1274 mee_handler ("[Advanced]", menu_loop_adv_options),
1275 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1276 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1277 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1281 static int menu_loop_options(int id, int keys)
1286 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1287 e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1288 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1290 me_loop(e_menu_options, &sel);
1295 // ------------ debug menu ------------
1297 static void draw_frame_debug(GPUFreeze_t *gpuf)
1299 int w = min(g_menuscreen_w, 1024);
1300 int h = min(g_menuscreen_h, 512);
1301 u16 *d = g_menuscreen_ptr;
1302 u16 *s = (u16 *)gpuf->psxVRam;
1306 gpuf->ulFreezeVersion = 1;
1307 if (GPU_freeze != NULL)
1308 GPU_freeze(1, gpuf);
1310 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1311 bgr555_to_rgb565(d, s, w * 2);
1313 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1314 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1315 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1316 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1317 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1320 static void debug_menu_loop(void)
1325 gpuf = malloc(sizeof(*gpuf));
1332 draw_frame_debug(gpuf);
1335 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1336 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1337 if (inp & PBTN_MBACK)
1344 // --------- memcard manager ---------
1346 static void draw_mc_icon(int dx, int dy, const u16 *s)
1351 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1353 for (y = 0; y < 16; y++, s += 16) {
1354 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1355 for (x = 0; x < 16; x++) {
1357 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1358 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1364 static void draw_mc_bg(void)
1366 McdBlock *blocks1, *blocks2;
1370 blocks1 = malloc(15 * sizeof(blocks1[0]));
1371 blocks2 = malloc(15 * sizeof(blocks1[0]));
1372 if (blocks1 == NULL || blocks2 == NULL)
1375 for (i = 0; i < 15; i++) {
1376 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1377 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1382 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1384 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1388 maxicons = g_menuscreen_h / 32;
1391 row2 = g_menuscreen_w / 2;
1392 for (i = 0; i < maxicons; i++) {
1393 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1394 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1396 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1397 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1400 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1408 static void handle_memcard_sel(void)
1411 if (memcard1_sel != 0)
1412 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1414 if (memcard2_sel != 0)
1415 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1416 LoadMcds(Config.Mcd1, Config.Mcd2);
1420 static menu_entry e_memcard_options[] =
1422 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1423 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1427 static int menu_loop_memcards(int id, int keys)
1433 memcard1_sel = memcard2_sel = 0;
1434 p = strrchr(Config.Mcd1, '/');
1436 for (i = 0; memcards[i] != NULL; i++)
1437 if (strcmp(p + 1, memcards[i]) == 0)
1438 { memcard1_sel = i; break; }
1439 p = strrchr(Config.Mcd2, '/');
1441 for (i = 0; memcards[i] != NULL; i++)
1442 if (strcmp(p + 1, memcards[i]) == 0)
1443 { memcard2_sel = i; break; }
1445 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1447 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1452 // --------- main menu help ----------
1454 static void menu_bios_warn(void)
1457 static const char msg[] =
1458 "You don't seem to have copied any BIOS files to\n"
1459 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1460 "While many games work fine with fake (HLE) BIOS,\n"
1461 "others (like MGS and FF8) require BIOS to work.\n"
1462 "After copying the file, you'll also need to\n"
1463 "select it in the emu's options->[BIOS/Plugins]\n\n"
1464 "The file is usually named SCPH1001.BIN, but\n"
1465 "other not compressed files can be used too.\n\n"
1466 "Press (B) or (X) to continue";
1470 draw_menu_message(msg, NULL);
1472 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1473 if (inp & (PBTN_MBACK|PBTN_MOK))
1478 // ------------ main menu ------------
1482 static void draw_frame_main(void)
1489 if (CdromId[0] != 0) {
1490 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1491 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1492 Config.HLE ? "HLE" : "BIOS");
1493 smalltext_out16(4, 1, buff, 0x105f);
1498 tmp = localtime(<ime);
1499 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1500 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, get_bat_capacity());
1501 smalltext_out16(4, 1 + me_sfont_h, buff, 0x105f);
1505 static void draw_frame_credits(void)
1507 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1510 static const char credits_text[] =
1512 "(C) 1999-2003 PCSX Team\n"
1513 "(C) 2005-2009 PCSX-df Team\n"
1514 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1515 "GPU and SPU code by Pete Bernert\n"
1516 " and the P.E.Op.S. team\n"
1517 "ARM recompiler (C) 2009-2011 Ari64\n"
1518 "PCSX4ALL plugins by PCSX4ALL team\n"
1519 " Chui, Franxis, Unai\n\n"
1520 "integration, optimization and\n"
1521 " frontend (C) 2010-2011 notaz\n";
1523 static int reset_game(void)
1526 if (bios_sel == 0 && !Config.HLE)
1532 if (CheckCdrom() != -1) {
1538 static int reload_plugins(const char *cdimg)
1544 set_cd_image(cdimg);
1546 pcnt_hook_plugins();
1548 if (OpenPlugins() == -1) {
1549 me_update_msg("failed to open plugins");
1552 plugin_call_rearmed_cbs();
1555 CdromLabel[0] = '\0';
1560 static int run_bios(void)
1566 if (reload_plugins(NULL) != 0)
1574 static int run_exe(void)
1578 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1583 if (reload_plugins(NULL) != 0)
1587 if (Load(fname) != 0) {
1588 me_update_msg("exe load failed, bad file?");
1597 static int run_cd_image(const char *fname)
1600 reload_plugins(fname);
1602 if (CheckCdrom() == -1) {
1603 // Only check the CD if we are starting the console with a CD
1605 me_update_msg("unsupported/invalid CD image");
1611 // Read main executable directly from CDRom and start it
1612 if (LoadCdrom() == -1) {
1614 me_update_msg("failed to load CD image");
1622 static int romsel_run(void)
1624 int prev_gpu, prev_spu;
1627 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1631 printf("selected file: %s\n", fname);
1633 new_dynarec_clear_full();
1635 if (run_cd_image(fname) != 0)
1638 prev_gpu = gpu_plugsel;
1639 prev_spu = spu_plugsel;
1640 if (menu_load_config(1) != 0)
1641 menu_load_config(0);
1643 // check for plugin changes, have to repeat
1644 // loading if game config changed plugins to reload them
1645 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1646 printf("plugin change detected, reloading plugins..\n");
1647 if (run_cd_image(fname) != 0)
1651 strcpy(last_selected_fname, rom_fname_reload);
1655 static int swap_cd_image(void)
1659 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1663 printf("selected file: %s\n", fname);
1666 CdromLabel[0] = '\0';
1668 set_cd_image(fname);
1669 if (ReloadCdromPlugin() < 0) {
1670 me_update_msg("failed to load cdr plugin");
1673 if (CDR_open() < 0) {
1674 me_update_msg("failed to open cdr plugin");
1678 SetCdOpenCaseTime(time(NULL) + 2);
1681 strcpy(last_selected_fname, rom_fname_reload);
1685 static int main_menu_handler(int id, int keys)
1689 case MA_MAIN_RESUME_GAME:
1693 case MA_MAIN_SAVE_STATE:
1695 return menu_loop_savestate(0);
1697 case MA_MAIN_LOAD_STATE:
1699 return menu_loop_savestate(1);
1701 case MA_MAIN_RESET_GAME:
1702 if (ready_to_go && reset_game() == 0)
1705 case MA_MAIN_LOAD_ROM:
1706 if (romsel_run() == 0)
1709 case MA_MAIN_SWAP_CD:
1710 if (swap_cd_image() == 0)
1713 case MA_MAIN_RUN_BIOS:
1714 if (run_bios() == 0)
1717 case MA_MAIN_RUN_EXE:
1721 case MA_MAIN_CREDITS:
1722 draw_menu_message(credits_text, draw_frame_credits);
1723 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1729 lprintf("%s: something unknown selected\n", __FUNCTION__);
1736 static menu_entry e_menu_main2[] =
1738 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1739 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1740 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
1741 mee_handler ("Memcard manager", menu_loop_memcards),
1745 static int main_menu2_handler(int id, int keys)
1749 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
1750 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1752 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1755 static const char h_extra[] = "Change CD, manage memcards..\n";
1757 static menu_entry e_menu_main[] =
1761 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1762 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1763 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1764 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1765 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1766 mee_handler ("Options", menu_loop_options),
1767 mee_handler ("Controls", menu_loop_keyconfig),
1768 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
1769 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1770 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1774 // ----------------------------
1776 static void menu_leave_emu(void);
1778 void menu_loop(void)
1784 if (bioses[1] == NULL && !warned_about_bios) {
1786 warned_about_bios = 1;
1789 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1790 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1791 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
1792 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1794 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1797 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
1798 } while (!ready_to_go);
1800 /* wait until menu, ok, back is released */
1801 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1804 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1809 static int qsort_strcmp(const void *p1, const void *p2)
1811 char * const *s1 = (char * const *)p1;
1812 char * const *s2 = (char * const *)p2;
1813 return strcasecmp(*s1, *s2);
1816 static void scan_bios_plugins(void)
1818 char fname[MAXPATHLEN];
1820 int bios_i, gpu_i, spu_i, mc_i;
1825 gpu_plugins[0] = "builtin_gpu";
1826 spu_plugins[0] = "builtin_spu";
1827 memcards[0] = "(none)";
1828 bios_i = gpu_i = spu_i = mc_i = 1;
1830 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1831 dir = opendir(fname);
1833 perror("scan_bios_plugins bios opendir");
1848 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1851 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1852 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1853 printf("bad BIOS file: %s\n", ent->d_name);
1857 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1858 bioses[bios_i++] = strdup(ent->d_name);
1862 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1868 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1869 dir = opendir(fname);
1871 perror("scan_bios_plugins plugins opendir");
1885 p = strstr(ent->d_name, ".so");
1889 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1890 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1892 fprintf(stderr, "%s\n", dlerror());
1896 // now what do we have here?
1897 tmp = dlsym(h, "GPUinit");
1900 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1901 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1905 tmp = dlsym(h, "SPUinit");
1908 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1909 spu_plugins[spu_i++] = strdup(ent->d_name);
1913 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1920 dir = opendir("." MEMCARD_DIR);
1922 perror("scan_bios_plugins memcards opendir");
1937 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1940 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
1941 if (stat(fname, &st) != 0) {
1942 printf("bad memcard file: %s\n", ent->d_name);
1946 if (mc_i < ARRAY_SIZE(memcards) - 1) {
1947 memcards[mc_i++] = strdup(ent->d_name);
1951 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
1955 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
1960 void menu_init(void)
1962 char buff[MAXPATHLEN];
1964 strcpy(last_selected_fname, "/media");
1966 scan_bios_plugins();
1970 menu_set_defconfig();
1971 menu_load_config(0);
1976 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1977 if (g_menubg_src_ptr == NULL)
1979 emu_make_path(buff, "skin/background.png", sizeof(buff));
1980 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1983 void menu_notify_mode_change(int w, int h, int bpp)
1994 g_layer_w = w; g_layer_h = h;
1998 mult = 240.0f / (float)h * 4.0f / 3.0f;
2001 g_layer_w = mult * (float)g_menuscreen_h;
2002 g_layer_h = g_menuscreen_h;
2003 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2007 // 4:3 that prefers integer scaling
2008 imult = g_menuscreen_h / h;
2009 g_layer_w = w * imult;
2010 g_layer_h = h * imult;
2011 mult = (float)g_layer_w / (float)g_layer_h;
2012 if (mult < 1.25f || mult > 1.666f)
2013 g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2014 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2017 case SCALE_FULLSCREEN:
2018 g_layer_w = g_menuscreen_w;
2019 g_layer_h = g_menuscreen_h;
2026 g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2027 g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2028 if (g_layer_x < 0) g_layer_x = 0;
2029 if (g_layer_y < 0) g_layer_y = 0;
2030 if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2031 if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2034 static void menu_leave_emu(void)
2036 if (GPU_close != NULL) {
2037 int ret = GPU_close();
2039 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2042 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2043 if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
2044 int x = max(0, g_menuscreen_w - last_psx_w);
2045 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2046 int w = min(g_menuscreen_w, last_psx_w);
2047 int h = min(g_menuscreen_h, last_psx_h);
2048 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2049 u16 *s = pl_vout_buf;
2051 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
2052 menu_darken_bg(d, s, w, 0);
2056 cpu_clock = get_cpu_clock();
2058 plat_video_menu_enter(ready_to_go);
2061 void menu_prepare_emu(void)
2063 R3000Acpu *prev_cpu = psxCpu;
2065 plat_video_menu_leave();
2067 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2069 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2070 if (psxCpu != prev_cpu)
2071 // note that this does not really reset, just clears drc caches
2074 // core doesn't care about Config.Cdda changes,
2075 // so handle them manually here
2080 apply_lcdrate(Config.PsxType);
2081 apply_filter(filter);
2084 // push config to GPU plugin
2085 plugin_call_rearmed_cbs();
2087 if (GPU_open != NULL) {
2088 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2090 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2096 void me_update_msg(const char *msg)
2098 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2099 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2101 menu_error_time = plat_get_ticks_ms();
2102 lprintf("msg: %s\n", menu_error_msg);