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/pad.h"
37 #define array_size(x) (sizeof(x) / sizeof(x[0]))
71 static int last_psx_w, last_psx_h, last_psx_bpp;
72 static int scaling, filter, cpu_clock, cpu_clock_st;
73 static char rom_fname_reload[MAXPATHLEN];
74 static char last_selected_fname[MAXPATHLEN];
75 static int warned_about_bios, region, in_type_sel;
78 // from softgpu plugin
79 extern int iUseDither;
80 extern int UseFrameSkip;
81 extern uint32_t dwActFixes;
82 extern float fFrameRateHz;
83 extern int dwFrameRateTicks;
86 extern int iUseReverb;
87 extern int iUseInterpolation;
89 extern int iSPUIRQWait;
92 static const char *bioses[24];
93 static const char *gpu_plugins[16];
94 static const char *spu_plugins[16];
95 static int bios_sel, gpu_plugsel, spu_plugsel;
98 static int min(int x, int y) { return x < y ? x : y; }
99 static int max(int x, int y) { return x > y ? x : y; }
101 void emu_make_path(char *buff, const char *end, int size)
105 end_len = strlen(end);
106 pos = plat_get_root_dir(buff, size);
107 strncpy(buff + pos, end, size - pos);
109 if (pos + end_len > size - 1)
110 printf("Warning: path truncated: %s\n", buff);
113 static int emu_check_save_file(int slot)
115 int ret = emu_check_state(slot);
116 return ret == 0 ? 1 : 0;
119 static int emu_save_load_game(int load, int unused)
124 ret = emu_load_state(state_slot);
126 // reflect hle/bios mode from savestate
129 else if (bios_sel == 0 && bioses[1] != NULL)
130 // XXX: maybe find the right bios instead
134 ret = emu_save_state(state_slot);
139 // propagate menu settings to the emu vars
140 static void menu_sync_config(void)
142 static int allow_abs_only_old;
147 Config.PsxType = region - 1;
149 in_type = in_type_sel ? PSE_PAD_TYPE_ANALOGPAD : PSE_PAD_TYPE_STANDARD;
150 if (in_evdev_allow_abs_only != allow_abs_only_old) {
151 pandora_rescan_inputs();
152 allow_abs_only_old = in_evdev_allow_abs_only;
155 pl_frame_interval = Config.PsxType ? 20000 : 16667;
156 // used by P.E.Op.S. frameskip code
157 fFrameRateHz = Config.PsxType ? 50.0f : 59.94f;
158 dwFrameRateTicks = (100000*100 / (unsigned long)(fFrameRateHz*100));
161 static void menu_set_defconfig(void)
168 in_evdev_allow_abs_only = 0;
169 Config.Xa = Config.Cdda = Config.Sio =
170 Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
177 iUseInterpolation = 1;
185 #define CE_CONFIG_STR(val) \
186 { #val, 0, Config.val }
188 #define CE_CONFIG_VAL(val) \
189 { #val, sizeof(Config.val), &Config.val }
191 #define CE_STR(val) \
194 #define CE_INTVAL(val) \
195 { #val, sizeof(val), &val }
197 // 'versioned' var, used when defaults change
198 #define CE_INTVAL_V(val, ver) \
199 { #val #ver, sizeof(val), &val }
201 static const struct {
209 // CE_CONFIG_STR(Cdr),
214 CE_CONFIG_VAL(Debug),
215 CE_CONFIG_VAL(PsxOut),
216 CE_CONFIG_VAL(SpuIrq),
217 CE_CONFIG_VAL(RCntFix),
218 CE_CONFIG_VAL(VSyncWA),
222 CE_INTVAL(g_layer_x),
223 CE_INTVAL(g_layer_y),
224 CE_INTVAL(g_layer_w),
225 CE_INTVAL(g_layer_h),
227 CE_INTVAL(state_slot),
228 CE_INTVAL(cpu_clock),
230 CE_INTVAL(in_type_sel),
231 CE_INTVAL(iUseDither),
232 CE_INTVAL(UseFrameSkip),
233 CE_INTVAL(dwActFixes),
234 CE_INTVAL(iUseReverb),
236 CE_INTVAL_V(iUseInterpolation, 2),
237 CE_INTVAL_V(iSPUIRQWait, 2),
238 CE_INTVAL(iUseTimer),
239 CE_INTVAL(warned_about_bios),
240 CE_INTVAL(in_evdev_allow_abs_only),
243 static char *get_cd_label(void)
245 static char trimlabel[33];
248 strncpy(trimlabel, CdromLabel, 32);
250 for (j = 31; j >= 0; j--)
251 if (trimlabel[j] == ' ')
257 static void make_cfg_fname(char *buf, size_t size, int is_game)
260 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
262 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
265 static void keys_write_all(FILE *f);
267 static int menu_write_config(int is_game)
269 char cfgfile[MAXPATHLEN];
273 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
274 f = fopen(cfgfile, "w");
276 printf("menu_write_config: failed to open: %s\n", cfgfile);
280 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
281 fprintf(f, "%s = ", config_data[i].name);
282 switch (config_data[i].len) {
284 fprintf(f, "%s\n", (char *)config_data[i].val);
287 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
290 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
293 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
296 printf("menu_write_config: unhandled len %d for %s\n",
297 config_data[i].len, config_data[i].name);
303 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
311 static void parse_str_val(char *cval, const char *src)
314 strncpy(cval, src, MAXPATHLEN);
315 cval[MAXPATHLEN - 1] = 0;
316 tmp = strchr(cval, '\n');
318 tmp = strchr(cval, '\r');
323 static void keys_load_all(const char *cfg);
325 static int menu_load_config(int is_game)
327 char cfgfile[MAXPATHLEN];
333 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
334 f = fopen(cfgfile, "r");
336 printf("menu_load_config: failed to open: %s\n", cfgfile);
340 fseek(f, 0, SEEK_END);
343 printf("bad size %ld: %s\n", size, cfgfile);
347 cfg = malloc(size + 1);
351 fseek(f, 0, SEEK_SET);
352 if (fread(cfg, 1, size, f) != size) {
353 printf("failed to read: %s\n", cfgfile);
358 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
362 tmp = strstr(cfg, config_data[i].name);
365 tmp += strlen(config_data[i].name);
366 if (strncmp(tmp, " = ", 3) != 0)
370 if (config_data[i].len == 0) {
371 parse_str_val(config_data[i].val, tmp);
376 val = strtoul(tmp, &tmp2, 16);
377 if (tmp2 == NULL || tmp == tmp2)
378 continue; // parse failed
380 switch (config_data[i].len) {
382 *(u8 *)config_data[i].val = val;
385 *(u16 *)config_data[i].val = val;
388 *(u32 *)config_data[i].val = val;
391 printf("menu_load_config: unhandled len %d for %s\n",
392 config_data[i].len, config_data[i].name);
398 char *tmp = strstr(cfg, "lastcdimg = ");
401 parse_str_val(last_selected_fname, tmp);
408 for (i = bios_sel = 0; bioses[i] != NULL; i++)
409 if (strcmp(Config.Bios, bioses[i]) == 0)
410 { bios_sel = i; break; }
412 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
413 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
414 { gpu_plugsel = i; break; }
416 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
417 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
418 { spu_plugsel = i; break; }
429 // rrrr rggg gggb bbbb
430 static unsigned short fname2color(const char *fname)
432 static const char *cdimg_exts[] = { ".bin", ".img", ".iso", ".cue", ".z", ".bz", ".znx", ".pbp" };
433 static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub", ".table", ".index", ".sbi" };
434 const char *ext = strrchr(fname, '.');
439 for (i = 0; i < array_size(cdimg_exts); i++)
440 if (strcasecmp(ext, cdimg_exts[i]) == 0)
442 for (i = 0; i < array_size(other_exts); i++)
443 if (strcasecmp(ext, other_exts[i]) == 0)
448 static void draw_savestate_bg(int slot);
450 #define MENU_ALIGN_LEFT
451 #define menu_init menu_init_common
452 #include "common/menu.c"
455 // a bit of black magic here
456 static void draw_savestate_bg(int slot)
458 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
460 char fname[MAXPATHLEN];
467 ret = get_state_filename(fname, sizeof(fname), slot);
471 f = gzopen(fname, "rb");
475 if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
476 fprintf(stderr, "gzseek failed\n");
481 gpu = malloc(sizeof(*gpu));
487 ret = gzread(f, gpu, sizeof(*gpu));
489 if (ret != sizeof(*gpu)) {
490 fprintf(stderr, "gzread failed\n");
494 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
496 if ((gpu->ulStatus & 0x800000) || (gpu->ulStatus & 0x200000))
497 goto out; // disabled || 24bpp (NYET)
499 x = gpu->ulControl[5] & 0x3ff;
500 y = (gpu->ulControl[5] >> 10) & 0x1ff;
501 s = (u16 *)gpu->psxVRam + y * 1024 + (x & ~3);
502 w = psx_widths[(gpu->ulStatus >> 16) & 7];
503 tmp = gpu->ulControl[7];
504 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
505 if (gpu->ulStatus & 0x80000) // doubleheight
508 x = max(0, g_menuscreen_w - w) & ~3;
509 y = max(0, g_menuscreen_h / 2 - h / 2);
510 w = min(g_menuscreen_w, w);
511 h = min(g_menuscreen_h, h);
512 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
514 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
515 bgr555_to_rgb565(d, s, w * 2);
521 // ---------- pandora specific -----------
523 static const char pnd_script_base[] = "sudo -n /usr/pandora/scripts";
524 static char **pnd_filter_list;
526 static int get_cpu_clock(void)
530 f = fopen("/proc/pandora/cpu_mhz_max", "r");
532 fscanf(f, "%d", &ret);
538 static void apply_cpu_clock(void)
542 if (cpu_clock != 0 && cpu_clock != get_cpu_clock()) {
543 snprintf(buf, sizeof(buf), "unset DISPLAY; echo y | %s/op_cpuspeed.sh %d",
544 pnd_script_base, cpu_clock);
549 static void apply_filter(int which)
555 if (pnd_filter_list == NULL || which == old)
558 for (i = 0; i < which; i++)
559 if (pnd_filter_list[i] == NULL)
562 if (pnd_filter_list[i] == NULL)
565 snprintf(buf, sizeof(buf), "%s/op_videofir.sh %s", pnd_script_base, pnd_filter_list[i]);
570 static menu_entry e_menu_gfx_options[];
572 static void pnd_menu_init(void)
580 cpu_clock_st = cpu_clock = get_cpu_clock();
582 dir = opendir("/etc/pandora/conf/dss_fir");
584 perror("filter opendir");
597 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
606 mfilters = calloc(count + 1, sizeof(mfilters[0]));
607 if (mfilters == NULL)
611 for (i = 0; (ent = readdir(dir)); ) {
614 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
617 len = strlen(ent->d_name);
619 // skip pre-HF5 extra files
620 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
622 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
625 // have to cut "_up_h" for pre-HF5
626 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
629 if (len > sizeof(buff) - 1)
632 strncpy(buff, ent->d_name, len);
634 mfilters[i] = strdup(buff);
635 if (mfilters[i] != NULL)
640 i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
641 e_menu_gfx_options[i].data = (void *)mfilters;
642 pnd_filter_list = mfilters;
645 void menu_finish(void)
647 cpu_clock = cpu_clock_st;
651 // -------------- key config --------------
653 me_bind_action me_ctrl_actions[] =
655 { "UP ", 1 << DKEY_UP},
656 { "DOWN ", 1 << DKEY_DOWN },
657 { "LEFT ", 1 << DKEY_LEFT },
658 { "RIGHT ", 1 << DKEY_RIGHT },
659 { "TRIANGLE", 1 << DKEY_TRIANGLE },
660 { "CIRCLE ", 1 << DKEY_CIRCLE },
661 { "CROSS ", 1 << DKEY_CROSS },
662 { "SQUARE ", 1 << DKEY_SQUARE },
663 { "L1 ", 1 << DKEY_L1 },
664 { "R1 ", 1 << DKEY_R1 },
665 { "L2 ", 1 << DKEY_L2 },
666 { "R2 ", 1 << DKEY_R2 },
667 { "L3 ", 1 << DKEY_L3 },
668 { "R3 ", 1 << DKEY_R3 },
669 { "START ", 1 << DKEY_START },
670 { "SELECT ", 1 << DKEY_SELECT },
674 me_bind_action emuctrl_actions[] =
676 { "Save State ", 1 << SACTION_SAVE_STATE },
677 { "Load State ", 1 << SACTION_LOAD_STATE },
678 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
679 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
680 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
681 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
682 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
686 static char *mystrip(char *str)
691 for (i = 0; i < len; i++)
692 if (str[i] != ' ') break;
693 if (i > 0) memmove(str, str + i, len - i + 1);
696 for (i = len - 1; i >= 0; i--)
697 if (str[i] != ' ') break;
703 static void get_line(char *d, size_t size, const char *s)
708 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
719 static void keys_write_all(FILE *f)
723 for (d = 0; d < IN_MAX_DEVS; d++)
725 const int *binds = in_get_dev_binds(d);
726 const char *name = in_get_dev_name(d, 0, 0);
729 if (binds == NULL || name == NULL)
732 fprintf(f, "binddev = %s\n", name);
733 in_get_config(d, IN_CFG_BIND_COUNT, &count);
735 for (k = 0; k < count; k++)
740 act[0] = act[31] = 0;
741 name = in_get_key_name(d, k);
743 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
744 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
745 mask = me_ctrl_actions[i].mask;
747 strncpy(act, me_ctrl_actions[i].name, 31);
748 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
751 mask = me_ctrl_actions[i].mask << 16;
753 strncpy(act, me_ctrl_actions[i].name, 31);
754 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
759 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
760 for (i = 0; kbinds && i < ARRAY_SIZE(emuctrl_actions) - 1; i++) {
761 mask = emuctrl_actions[i].mask;
763 strncpy(act, emuctrl_actions[i].name, 31);
764 fprintf(f, "bind %s = %s\n", name, mystrip(act));
772 static int parse_bind_val(const char *val, int *type)
776 *type = IN_BINDTYPE_NONE;
780 if (strncasecmp(val, "player", 6) == 0)
782 int player, shift = 0;
783 player = atoi(val + 6) - 1;
785 if ((unsigned int)player > 1)
790 *type = IN_BINDTYPE_PLAYER12;
791 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
792 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
793 return me_ctrl_actions[i].mask << shift;
796 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
797 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
798 *type = IN_BINDTYPE_EMU;
799 return emuctrl_actions[i].mask;
806 static void keys_load_all(const char *cfg)
808 char dev[256], key[128], *act;
814 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
817 get_line(dev, sizeof(dev), p);
818 dev_id = in_config_parse_dev(dev);
820 printf("input: can't handle dev: %s\n", dev);
824 in_unbind_all(dev_id, -1, -1);
825 while ((p = strstr(p, "bind"))) {
826 if (strncmp(p, "binddev = ", 10) == 0)
831 printf("input: parse error: %16s..\n", p);
835 get_line(key, sizeof(key), p);
836 act = strchr(key, '=');
838 printf("parse failed: %16s..\n", p);
846 bind = parse_bind_val(act, &bindtype);
847 if (bind != -1 && bind != 0) {
848 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
849 in_config_bind_key(dev_id, key, bind, bindtype);
852 lprintf("config: unhandled action \"%s\"\n", act);
858 static int key_config_loop_wrap(int id, int keys)
861 case MA_CTRL_PLAYER1:
862 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
864 case MA_CTRL_PLAYER2:
865 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
868 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
876 static const char *mgn_dev_name(int id, int *offs)
878 const char *name = NULL;
881 if (id == MA_CTRL_DEV_FIRST)
884 for (; it < IN_MAX_DEVS; it++) {
885 name = in_get_dev_name(it, 1, 1);
894 static const char *mgn_saveloadcfg(int id, int *offs)
899 static int mh_savecfg(int id, int keys)
901 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
902 me_update_msg("config saved");
904 me_update_msg("failed to write config");
909 static int mh_input_rescan(int id, int keys)
911 //menu_sync_config();
912 pandora_rescan_inputs();
913 me_update_msg("rescan complete.");
918 static const char *men_in_type_sel[] = { "Standard (SCPH-1080)", "Analog (SCPH-1150)", NULL };
919 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
921 static menu_entry e_menu_keyconfig[] =
923 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
924 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
925 mee_handler_id("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap),
927 mee_enum ("Controller", 0, in_type_sel, men_in_type_sel),
928 mee_onoff_h ("Nubs as buttons", 0, in_evdev_allow_abs_only, 1, h_nub_btns),
929 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
930 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
931 mee_handler ("Rescan devices", mh_input_rescan),
933 mee_label ("Input devices:"),
934 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
935 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
936 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
937 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
938 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
939 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
940 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
944 static int menu_loop_keyconfig(int id, int keys)
948 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
949 me_loop(e_menu_keyconfig, &sel, NULL);
953 // ------------ gfx options menu ------------
955 static const char *men_scaler[] = { "1x1", "scaled 4:3", "fullscreen", "custom", NULL };
956 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
957 "using d-pad or move it using R+d-pad";
958 static const char *men_dummy[] = { NULL };
960 static int menu_loop_cscaler(int id, int keys)
964 scaling = SCALE_CUSTOM;
966 omap_enable_layer(1);
971 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
972 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
973 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
976 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
977 if (inp & PBTN_UP) g_layer_y--;
978 if (inp & PBTN_DOWN) g_layer_y++;
979 if (inp & PBTN_LEFT) g_layer_x--;
980 if (inp & PBTN_RIGHT) g_layer_x++;
981 if (!(inp & PBTN_R)) {
982 if (inp & PBTN_UP) g_layer_h += 2;
983 if (inp & PBTN_DOWN) g_layer_h -= 2;
984 if (inp & PBTN_LEFT) g_layer_w += 2;
985 if (inp & PBTN_RIGHT) g_layer_w -= 2;
987 if (inp & (PBTN_MOK|PBTN_MBACK))
990 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
991 if (g_layer_x < 0) g_layer_x = 0;
992 if (g_layer_x > 640) g_layer_x = 640;
993 if (g_layer_y < 0) g_layer_y = 0;
994 if (g_layer_y > 420) g_layer_y = 420;
995 if (g_layer_w < 160) g_layer_w = 160;
996 if (g_layer_h < 60) g_layer_h = 60;
997 if (g_layer_x + g_layer_w > 800)
998 g_layer_w = 800 - g_layer_x;
999 if (g_layer_y + g_layer_h > 480)
1000 g_layer_h = 480 - g_layer_y;
1001 omap_enable_layer(1);
1005 omap_enable_layer(0);
1010 static menu_entry e_menu_gfx_options[] =
1012 mee_enum ("Scaler", 0, scaling, men_scaler),
1013 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
1014 // mee_onoff ("Vsync", 0, vsync, 1),
1015 mee_cust_h ("Setup custom scaler", 0, menu_loop_cscaler, NULL, h_cscaler),
1019 static int menu_loop_gfx_options(int id, int keys)
1023 me_loop(e_menu_gfx_options, &sel, NULL);
1028 // ------------ bios/plugins ------------
1030 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1031 static const char h_gpu_0[] = "Needed for Chrono Cross";
1032 static const char h_gpu_1[] = "Capcom fighting games";
1033 static const char h_gpu_2[] = "Black screens in Lunar";
1034 static const char h_gpu_3[] = "Compatibility mode";
1035 static const char h_gpu_6[] = "Pandemonium 2";
1036 static const char h_gpu_7[] = "Skip every second frame";
1037 static const char h_gpu_8[] = "Needed by Dark Forces";
1038 static const char h_gpu_9[] = "better g-colors, worse textures";
1039 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1041 static menu_entry e_menu_plugin_gpu[] =
1043 mee_enum ("Dithering", 0, iUseDither, men_gpu_dithering),
1044 mee_onoff_h ("Odd/even bit hack", 0, dwActFixes, 1<<0, h_gpu_0),
1045 mee_onoff_h ("Expand screen width", 0, dwActFixes, 1<<1, h_gpu_1),
1046 mee_onoff_h ("Ignore brightness color", 0, dwActFixes, 1<<2, h_gpu_2),
1047 mee_onoff_h ("Disable coordinate check", 0, dwActFixes, 1<<3, h_gpu_3),
1048 mee_onoff_h ("Lazy screen update", 0, dwActFixes, 1<<6, h_gpu_6),
1049 mee_onoff_h ("Old frame skipping", 0, dwActFixes, 1<<7, h_gpu_7),
1050 mee_onoff_h ("Repeated flat tex triangles ",0,dwActFixes, 1<<8, h_gpu_8),
1051 mee_onoff_h ("Draw quads with triangles", 0, dwActFixes, 1<<9, h_gpu_9),
1052 mee_onoff_h ("Fake 'gpu busy' states", 0, dwActFixes, 1<<10, h_gpu_10),
1056 static int menu_loop_plugin_gpu(int id, int keys)
1059 me_loop(e_menu_plugin_gpu, &sel, NULL);
1063 static const char *men_spu_reverb[] = { "Off", "Fake", "On", NULL };
1064 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1065 static const char h_spu_irq_wait[] = "Wait for CPU (recommended set to ON)";
1066 static const char h_spu_thread[] = "Run sound emulation in main thread (recommended)";
1068 static menu_entry e_menu_plugin_spu[] =
1070 mee_enum ("Reverb", 0, iUseReverb, men_spu_reverb),
1071 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
1072 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
1073 mee_onoff_h ("SPU IRQ Wait", 0, iSPUIRQWait, 1, h_spu_irq_wait),
1074 mee_onoff_h ("Sound in main thread", 0, iUseTimer, 2, h_spu_thread),
1078 static int menu_loop_plugin_spu(int id, int keys)
1081 me_loop(e_menu_plugin_spu, &sel, NULL);
1085 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in savestates\n"
1086 "and can't be changed there. Must save config and reload\n"
1087 "the game for change to take effect";
1088 static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1089 "for plugin change to take effect";
1090 static const char h_gpu[] = "Configure built-in P.E.Op.S. SoftGL Driver V1.17";
1091 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1093 static menu_entry e_menu_plugin_options[] =
1095 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1096 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1097 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_xpu),
1098 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu, h_gpu),
1099 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1103 static menu_entry e_menu_main[];
1105 static int menu_loop_plugin_options(int id, int keys)
1108 me_loop(e_menu_plugin_options, &sel, NULL);
1110 // sync BIOS/plugins
1111 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1112 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1113 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1114 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1119 // ------------ adv options menu ------------
1121 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1122 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1123 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1124 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1125 "(proper .cue/.bin dump is needed otherwise)";
1126 static const char h_cfg_sio[] = "This should be enabled for certain memcards/gamepads";
1127 static const char h_cfg_spuirq[] = "Compatibility tweak; should probably be left off";
1128 static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix";
1129 static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix";
1130 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1131 "Might be useful to overcome some dynarec bugs";
1133 static menu_entry e_menu_adv_options[] =
1135 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1136 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1137 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1138 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1139 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1140 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1141 mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1142 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
1143 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1147 static int menu_loop_adv_options(int id, int keys)
1150 me_loop(e_menu_adv_options, &sel, NULL);
1154 // ------------ options menu ------------
1156 static int mh_restore_defaults(int id, int keys)
1158 menu_set_defconfig();
1159 me_update_msg("defaults restored");
1163 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1165 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1166 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1167 "loading state or both";
1169 static const char h_restore_def[] = "Switches back to default / recommended\n"
1171 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1173 static menu_entry e_menu_options[] =
1175 // mee_range ("Save slot", 0, state_slot, 0, 9),
1176 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1177 mee_onoff_h ("Frameskip", 0, UseFrameSkip, 1, h_frameskip),
1178 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1179 mee_enum ("Region", 0, region, men_region),
1180 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1181 mee_handler ("[Display]", menu_loop_gfx_options),
1182 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1183 mee_handler ("[Advanced]", menu_loop_adv_options),
1184 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1185 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1186 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1190 static int menu_loop_options(int id, int keys)
1195 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1196 e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1197 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1199 me_loop(e_menu_options, &sel, NULL);
1204 // ------------ debug menu ------------
1206 static void draw_frame_debug(GPUFreeze_t *gpuf)
1208 int w = min(g_menuscreen_w, 1024);
1209 int h = min(g_menuscreen_h, 512);
1210 u16 *d = g_menuscreen_ptr;
1211 u16 *s = (u16 *)gpuf->psxVRam;
1215 gpuf->ulFreezeVersion = 1;
1216 if (GPU_freeze != NULL)
1217 GPU_freeze(1, gpuf);
1219 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1220 bgr555_to_rgb565(d, s, w * 2);
1222 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1223 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1224 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1225 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1226 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1229 static void debug_menu_loop(void)
1234 gpuf = malloc(sizeof(*gpuf));
1241 draw_frame_debug(gpuf);
1244 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1245 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1246 if (inp & PBTN_MBACK)
1253 // ------------ main menu ------------
1255 static void menu_bios_warn(void)
1258 static const char msg[] =
1259 "You don't seem to have copied any BIOS files to\n"
1260 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1261 "While many games work fine with fake (HLE) BIOS,\n"
1262 "others (like MGS and FF8) require BIOS to work.\n"
1263 "After copying the file, you'll also need to\n"
1264 "select it in the emu's options->[BIOS/Plugins]\n\n"
1265 "The file is usually named SCPH1001.BIN, but\n"
1266 "other not compressed files can be used too.\n\n"
1267 "Press (B) or (X) to continue";
1271 draw_menu_message(msg, NULL);
1273 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1274 if (inp & (PBTN_MBACK|PBTN_MOK))
1279 // ------------ main menu ------------
1283 static void draw_frame_main(void)
1285 if (CdromId[0] != 0) {
1287 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1288 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1289 Config.HLE ? "HLE" : "BIOS");
1290 smalltext_out16(4, 1, buff, 0x105f);
1294 static void draw_frame_credits(void)
1296 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1299 static const char credits_text[] =
1301 "(C) 1999-2003 PCSX Team\n"
1302 "(C) 2005-2009 PCSX-df Team\n"
1303 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1304 "GPU and SPU code by Pete Bernert\n"
1305 " and the P.E.Op.S. team\n"
1306 "ARM recompiler (C) 2009-2011 Ari64\n"
1307 "PCSX4ALL plugins by PCSX4ALL team\n"
1308 " Chui, Franxis, Unai\n\n"
1309 "integration, optimization and\n"
1310 " frontend (C) 2010-2011 notaz\n";
1312 static int reset_game(void)
1315 if (bios_sel == 0 && !Config.HLE)
1321 if (CheckCdrom() != -1) {
1327 static int run_bios(void)
1333 pl_fbdev_buf = NULL;
1338 pcnt_hook_plugins();
1340 if (OpenPlugins() == -1) {
1341 me_update_msg("failed to open plugins");
1344 plugin_call_rearmed_cbs();
1347 CdromLabel[0] = '\0';
1355 static int run_cd_image(const char *fname)
1358 pl_fbdev_buf = NULL;
1361 set_cd_image(fname);
1363 pcnt_hook_plugins();
1365 if (OpenPlugins() == -1) {
1366 me_update_msg("failed to open plugins");
1369 plugin_call_rearmed_cbs();
1371 if (CheckCdrom() == -1) {
1372 // Only check the CD if we are starting the console with a CD
1374 me_update_msg("unsupported/invalid CD image");
1380 // Read main executable directly from CDRom and start it
1381 if (LoadCdrom() == -1) {
1383 me_update_msg("failed to load CD image");
1391 static int romsel_run(void)
1393 int prev_gpu, prev_spu;
1396 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1400 printf("selected file: %s\n", fname);
1402 new_dynarec_clear_full();
1404 if (run_cd_image(fname) != 0)
1407 prev_gpu = gpu_plugsel;
1408 prev_spu = spu_plugsel;
1409 if (menu_load_config(1) != 0)
1410 menu_load_config(0);
1412 // check for plugin changes, have to repeat
1413 // loading if game config changed plugins to reload them
1414 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1415 printf("plugin change detected, reloading plugins..\n");
1416 if (run_cd_image(fname) != 0)
1420 strcpy(last_selected_fname, rom_fname_reload);
1424 static int swap_cd_image(void)
1428 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1432 printf("selected file: %s\n", fname);
1435 CdromLabel[0] = '\0';
1437 set_cd_image(fname);
1438 if (ReloadCdromPlugin() < 0) {
1439 me_update_msg("failed to load cdr plugin");
1442 if (CDR_open() < 0) {
1443 me_update_msg("failed to open cdr plugin");
1447 SetCdOpenCaseTime(time(NULL) + 2);
1450 strcpy(last_selected_fname, rom_fname_reload);
1454 static int main_menu_handler(int id, int keys)
1458 case MA_MAIN_RESUME_GAME:
1462 case MA_MAIN_SAVE_STATE:
1464 return menu_loop_savestate(0);
1466 case MA_MAIN_LOAD_STATE:
1468 return menu_loop_savestate(1);
1470 case MA_MAIN_RESET_GAME:
1471 if (ready_to_go && reset_game() == 0)
1474 case MA_MAIN_LOAD_ROM:
1475 if (romsel_run() == 0)
1478 case MA_MAIN_SWAP_CD:
1479 if (swap_cd_image() == 0)
1482 case MA_MAIN_RUN_BIOS:
1483 if (run_bios() == 0)
1486 case MA_MAIN_CREDITS:
1487 draw_menu_message(credits_text, draw_frame_credits);
1488 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1494 lprintf("%s: something unknown selected\n", __FUNCTION__);
1501 static menu_entry e_menu_main[] =
1505 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1506 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1507 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1508 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1509 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1510 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1511 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1512 mee_handler ("Options", menu_loop_options),
1513 mee_handler ("Controls", menu_loop_keyconfig),
1514 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1515 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1519 // ----------------------------
1521 static void menu_leave_emu(void);
1523 void menu_loop(void)
1529 if (bioses[1] == NULL && !warned_about_bios) {
1531 warned_about_bios = 1;
1534 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1535 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1536 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
1537 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1538 me_enable(e_menu_main, MA_MAIN_SWAP_CD, ready_to_go);
1539 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1541 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1544 me_loop(e_menu_main, &sel, draw_frame_main);
1545 } while (!ready_to_go);
1547 /* wait until menu, ok, back is released */
1548 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1551 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1556 static void scan_bios_plugins(void)
1558 char fname[MAXPATHLEN];
1560 int bios_i, gpu_i, spu_i;
1565 gpu_plugins[0] = "builtin_gpu";
1566 spu_plugins[0] = "builtin_spu";
1567 bios_i = gpu_i = spu_i = 1;
1569 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1570 dir = opendir(fname);
1572 perror("scan_bios_plugins bios opendir");
1587 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1590 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1591 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1592 printf("bad BIOS file: %s\n", ent->d_name);
1596 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1597 bioses[bios_i++] = strdup(ent->d_name);
1601 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1607 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1608 dir = opendir(fname);
1610 perror("scan_bios_plugins opendir");
1624 p = strstr(ent->d_name, ".so");
1628 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1629 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1631 fprintf(stderr, "%s\n", dlerror());
1635 // now what do we have here?
1636 tmp = dlsym(h, "GPUinit");
1639 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1640 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1644 tmp = dlsym(h, "SPUinit");
1647 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1648 spu_plugins[spu_i++] = strdup(ent->d_name);
1652 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1659 void menu_init(void)
1661 char buff[MAXPATHLEN];
1663 strcpy(last_selected_fname, "/media");
1665 scan_bios_plugins();
1669 menu_set_defconfig();
1670 menu_load_config(0);
1675 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1676 if (g_menubg_src_ptr == NULL)
1678 emu_make_path(buff, "skin/background.png", sizeof(buff));
1679 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1682 void menu_notify_mode_change(int w, int h, int bpp)
1688 if (scaling == SCALE_1_1) {
1689 g_layer_x = 800/2 - w/2; g_layer_y = 480/2 - h/2;
1690 g_layer_w = w; g_layer_h = h;
1694 static void menu_leave_emu(void)
1696 if (GPU_close != NULL) {
1697 int ret = GPU_close();
1699 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1702 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1703 if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
1704 int x = max(0, g_menuscreen_w - last_psx_w);
1705 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1706 int w = min(g_menuscreen_w, last_psx_w);
1707 int h = min(g_menuscreen_h, last_psx_h);
1708 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
1709 u16 *s = pl_fbdev_buf;
1711 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
1712 menu_darken_bg(d, s, w, 0);
1716 cpu_clock = get_cpu_clock();
1718 plat_video_menu_enter(ready_to_go);
1721 void menu_prepare_emu(void)
1723 R3000Acpu *prev_cpu = psxCpu;
1725 plat_video_menu_leave();
1729 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
1732 g_layer_x = 80; g_layer_y = 0;
1733 g_layer_w = 640; g_layer_h = 480;
1735 case SCALE_FULLSCREEN:
1736 g_layer_x = 0; g_layer_y = 0;
1737 g_layer_w = 800; g_layer_h = 480;
1742 apply_filter(filter);
1745 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1746 if (psxCpu != prev_cpu)
1747 // note that this does not really reset, just clears drc caches
1750 // core doesn't care about Config.Cdda changes,
1751 // so handle them manually here
1757 if (GPU_open != NULL) {
1758 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1760 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1763 dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
1766 void me_update_msg(const char *msg)
1768 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1769 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1771 menu_error_time = plat_get_ticks_ms();
1772 lprintf("msg: %s\n", menu_error_msg);