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 void apply_lcdrate(int pal)
578 snprintf(buf, sizeof(buf), "%s/op_lcdrate.sh %d",
579 pnd_script_base, pal ? 50 : 60);
584 static menu_entry e_menu_gfx_options[];
586 static void pnd_menu_init(void)
594 cpu_clock_st = cpu_clock = get_cpu_clock();
596 dir = opendir("/etc/pandora/conf/dss_fir");
598 perror("filter opendir");
611 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
620 mfilters = calloc(count + 1, sizeof(mfilters[0]));
621 if (mfilters == NULL)
625 for (i = 0; (ent = readdir(dir)); ) {
628 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
631 len = strlen(ent->d_name);
633 // skip pre-HF5 extra files
634 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
636 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
639 // have to cut "_up_h" for pre-HF5
640 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
643 if (len > sizeof(buff) - 1)
646 strncpy(buff, ent->d_name, len);
648 mfilters[i] = strdup(buff);
649 if (mfilters[i] != NULL)
654 i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
655 e_menu_gfx_options[i].data = (void *)mfilters;
656 pnd_filter_list = mfilters;
659 void menu_finish(void)
661 cpu_clock = cpu_clock_st;
665 // -------------- key config --------------
667 me_bind_action me_ctrl_actions[] =
669 { "UP ", 1 << DKEY_UP},
670 { "DOWN ", 1 << DKEY_DOWN },
671 { "LEFT ", 1 << DKEY_LEFT },
672 { "RIGHT ", 1 << DKEY_RIGHT },
673 { "TRIANGLE", 1 << DKEY_TRIANGLE },
674 { "CIRCLE ", 1 << DKEY_CIRCLE },
675 { "CROSS ", 1 << DKEY_CROSS },
676 { "SQUARE ", 1 << DKEY_SQUARE },
677 { "L1 ", 1 << DKEY_L1 },
678 { "R1 ", 1 << DKEY_R1 },
679 { "L2 ", 1 << DKEY_L2 },
680 { "R2 ", 1 << DKEY_R2 },
681 { "L3 ", 1 << DKEY_L3 },
682 { "R3 ", 1 << DKEY_R3 },
683 { "START ", 1 << DKEY_START },
684 { "SELECT ", 1 << DKEY_SELECT },
688 me_bind_action emuctrl_actions[] =
690 { "Save State ", 1 << SACTION_SAVE_STATE },
691 { "Load State ", 1 << SACTION_LOAD_STATE },
692 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
693 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
694 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
695 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
696 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
700 static char *mystrip(char *str)
705 for (i = 0; i < len; i++)
706 if (str[i] != ' ') break;
707 if (i > 0) memmove(str, str + i, len - i + 1);
710 for (i = len - 1; i >= 0; i--)
711 if (str[i] != ' ') break;
717 static void get_line(char *d, size_t size, const char *s)
722 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
733 static void keys_write_all(FILE *f)
737 for (d = 0; d < IN_MAX_DEVS; d++)
739 const int *binds = in_get_dev_binds(d);
740 const char *name = in_get_dev_name(d, 0, 0);
743 if (binds == NULL || name == NULL)
746 fprintf(f, "binddev = %s\n", name);
747 in_get_config(d, IN_CFG_BIND_COUNT, &count);
749 for (k = 0; k < count; k++)
754 act[0] = act[31] = 0;
755 name = in_get_key_name(d, k);
757 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
758 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
759 mask = me_ctrl_actions[i].mask;
761 strncpy(act, me_ctrl_actions[i].name, 31);
762 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
765 mask = me_ctrl_actions[i].mask << 16;
767 strncpy(act, me_ctrl_actions[i].name, 31);
768 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
773 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
774 for (i = 0; kbinds && i < ARRAY_SIZE(emuctrl_actions) - 1; i++) {
775 mask = emuctrl_actions[i].mask;
777 strncpy(act, emuctrl_actions[i].name, 31);
778 fprintf(f, "bind %s = %s\n", name, mystrip(act));
786 static int parse_bind_val(const char *val, int *type)
790 *type = IN_BINDTYPE_NONE;
794 if (strncasecmp(val, "player", 6) == 0)
796 int player, shift = 0;
797 player = atoi(val + 6) - 1;
799 if ((unsigned int)player > 1)
804 *type = IN_BINDTYPE_PLAYER12;
805 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
806 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
807 return me_ctrl_actions[i].mask << shift;
810 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
811 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
812 *type = IN_BINDTYPE_EMU;
813 return emuctrl_actions[i].mask;
820 static void keys_load_all(const char *cfg)
822 char dev[256], key[128], *act;
828 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
831 get_line(dev, sizeof(dev), p);
832 dev_id = in_config_parse_dev(dev);
834 printf("input: can't handle dev: %s\n", dev);
838 in_unbind_all(dev_id, -1, -1);
839 while ((p = strstr(p, "bind"))) {
840 if (strncmp(p, "binddev = ", 10) == 0)
845 printf("input: parse error: %16s..\n", p);
849 get_line(key, sizeof(key), p);
850 act = strchr(key, '=');
852 printf("parse failed: %16s..\n", p);
860 bind = parse_bind_val(act, &bindtype);
861 if (bind != -1 && bind != 0) {
862 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
863 in_config_bind_key(dev_id, key, bind, bindtype);
866 lprintf("config: unhandled action \"%s\"\n", act);
872 static int key_config_loop_wrap(int id, int keys)
875 case MA_CTRL_PLAYER1:
876 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
878 case MA_CTRL_PLAYER2:
879 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
882 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
890 static const char *mgn_dev_name(int id, int *offs)
892 const char *name = NULL;
895 if (id == MA_CTRL_DEV_FIRST)
898 for (; it < IN_MAX_DEVS; it++) {
899 name = in_get_dev_name(it, 1, 1);
908 static const char *mgn_saveloadcfg(int id, int *offs)
913 static int mh_savecfg(int id, int keys)
915 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
916 me_update_msg("config saved");
918 me_update_msg("failed to write config");
923 static int mh_input_rescan(int id, int keys)
925 //menu_sync_config();
926 pandora_rescan_inputs();
927 me_update_msg("rescan complete.");
932 static const char *men_in_type_sel[] = { "Standard (SCPH-1080)", "Analog (SCPH-1150)", NULL };
933 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
935 static menu_entry e_menu_keyconfig[] =
937 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
938 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
939 mee_handler_id("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap),
941 mee_enum ("Controller", 0, in_type_sel, men_in_type_sel),
942 mee_onoff_h ("Nubs as buttons", 0, in_evdev_allow_abs_only, 1, h_nub_btns),
943 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
944 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
945 mee_handler ("Rescan devices", mh_input_rescan),
947 mee_label ("Input devices:"),
948 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
949 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
950 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
951 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
952 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
953 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
954 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
958 static int menu_loop_keyconfig(int id, int keys)
962 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
963 me_loop(e_menu_keyconfig, &sel, NULL);
967 // ------------ gfx options menu ------------
969 static const char *men_scaler[] = { "1x1", "scaled 4:3", "fullscreen", "custom", NULL };
970 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
971 "using d-pad or move it using R+d-pad";
972 static const char *men_dummy[] = { NULL };
974 static int menu_loop_cscaler(int id, int keys)
978 scaling = SCALE_CUSTOM;
980 omap_enable_layer(1);
985 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
986 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
987 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
990 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
991 if (inp & PBTN_UP) g_layer_y--;
992 if (inp & PBTN_DOWN) g_layer_y++;
993 if (inp & PBTN_LEFT) g_layer_x--;
994 if (inp & PBTN_RIGHT) g_layer_x++;
995 if (!(inp & PBTN_R)) {
996 if (inp & PBTN_UP) g_layer_h += 2;
997 if (inp & PBTN_DOWN) g_layer_h -= 2;
998 if (inp & PBTN_LEFT) g_layer_w += 2;
999 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1001 if (inp & (PBTN_MOK|PBTN_MBACK))
1004 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1005 if (g_layer_x < 0) g_layer_x = 0;
1006 if (g_layer_x > 640) g_layer_x = 640;
1007 if (g_layer_y < 0) g_layer_y = 0;
1008 if (g_layer_y > 420) g_layer_y = 420;
1009 if (g_layer_w < 160) g_layer_w = 160;
1010 if (g_layer_h < 60) g_layer_h = 60;
1011 if (g_layer_x + g_layer_w > 800)
1012 g_layer_w = 800 - g_layer_x;
1013 if (g_layer_y + g_layer_h > 480)
1014 g_layer_h = 480 - g_layer_y;
1015 omap_enable_layer(1);
1019 omap_enable_layer(0);
1024 static menu_entry e_menu_gfx_options[] =
1026 mee_enum ("Scaler", 0, scaling, men_scaler),
1027 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
1028 // mee_onoff ("Vsync", 0, vsync, 1),
1029 mee_cust_h ("Setup custom scaler", 0, menu_loop_cscaler, NULL, h_cscaler),
1033 static int menu_loop_gfx_options(int id, int keys)
1037 me_loop(e_menu_gfx_options, &sel, NULL);
1042 // ------------ bios/plugins ------------
1044 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1045 static const char h_gpu_0[] = "Needed for Chrono Cross";
1046 static const char h_gpu_1[] = "Capcom fighting games";
1047 static const char h_gpu_2[] = "Black screens in Lunar";
1048 static const char h_gpu_3[] = "Compatibility mode";
1049 static const char h_gpu_6[] = "Pandemonium 2";
1050 static const char h_gpu_7[] = "Skip every second frame";
1051 static const char h_gpu_8[] = "Needed by Dark Forces";
1052 static const char h_gpu_9[] = "better g-colors, worse textures";
1053 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1055 static menu_entry e_menu_plugin_gpu[] =
1057 mee_enum ("Dithering", 0, iUseDither, men_gpu_dithering),
1058 mee_onoff_h ("Odd/even bit hack", 0, dwActFixes, 1<<0, h_gpu_0),
1059 mee_onoff_h ("Expand screen width", 0, dwActFixes, 1<<1, h_gpu_1),
1060 mee_onoff_h ("Ignore brightness color", 0, dwActFixes, 1<<2, h_gpu_2),
1061 mee_onoff_h ("Disable coordinate check", 0, dwActFixes, 1<<3, h_gpu_3),
1062 mee_onoff_h ("Lazy screen update", 0, dwActFixes, 1<<6, h_gpu_6),
1063 mee_onoff_h ("Old frame skipping", 0, dwActFixes, 1<<7, h_gpu_7),
1064 mee_onoff_h ("Repeated flat tex triangles ",0,dwActFixes, 1<<8, h_gpu_8),
1065 mee_onoff_h ("Draw quads with triangles", 0, dwActFixes, 1<<9, h_gpu_9),
1066 mee_onoff_h ("Fake 'gpu busy' states", 0, dwActFixes, 1<<10, h_gpu_10),
1070 static int menu_loop_plugin_gpu(int id, int keys)
1073 me_loop(e_menu_plugin_gpu, &sel, NULL);
1077 static const char *men_spu_reverb[] = { "Off", "Fake", "On", NULL };
1078 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1079 static const char h_spu_irq_wait[] = "Wait for CPU (recommended set to ON)";
1080 static const char h_spu_thread[] = "Run sound emulation in main thread (recommended)";
1082 static menu_entry e_menu_plugin_spu[] =
1084 mee_enum ("Reverb", 0, iUseReverb, men_spu_reverb),
1085 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
1086 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
1087 mee_onoff_h ("SPU IRQ Wait", 0, iSPUIRQWait, 1, h_spu_irq_wait),
1088 mee_onoff_h ("Sound in main thread", 0, iUseTimer, 2, h_spu_thread),
1092 static int menu_loop_plugin_spu(int id, int keys)
1095 me_loop(e_menu_plugin_spu, &sel, NULL);
1099 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in savestates\n"
1100 "and can't be changed there. Must save config and reload\n"
1101 "the game for change to take effect";
1102 static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1103 "for plugin change to take effect";
1104 static const char h_gpu[] = "Configure built-in P.E.Op.S. SoftGL Driver V1.17";
1105 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1107 static menu_entry e_menu_plugin_options[] =
1109 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1110 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1111 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_xpu),
1112 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu, h_gpu),
1113 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1117 static menu_entry e_menu_main[];
1119 static int menu_loop_plugin_options(int id, int keys)
1122 me_loop(e_menu_plugin_options, &sel, NULL);
1124 // sync BIOS/plugins
1125 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1126 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1127 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1128 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1133 // ------------ adv options menu ------------
1135 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1136 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1137 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1138 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1139 "(proper .cue/.bin dump is needed otherwise)";
1140 static const char h_cfg_sio[] = "This should be enabled for certain memcards/gamepads";
1141 static const char h_cfg_spuirq[] = "Compatibility tweak; should probably be left off";
1142 static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix";
1143 static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix";
1144 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1145 "Might be useful to overcome some dynarec bugs";
1147 static menu_entry e_menu_adv_options[] =
1149 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1150 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1151 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1152 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1153 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1154 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1155 mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1156 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
1157 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1161 static int menu_loop_adv_options(int id, int keys)
1164 me_loop(e_menu_adv_options, &sel, NULL);
1168 // ------------ options menu ------------
1170 static int mh_restore_defaults(int id, int keys)
1172 menu_set_defconfig();
1173 me_update_msg("defaults restored");
1177 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1179 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1180 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1181 "loading state or both";
1183 static const char h_restore_def[] = "Switches back to default / recommended\n"
1185 static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
1187 static menu_entry e_menu_options[] =
1189 // mee_range ("Save slot", 0, state_slot, 0, 9),
1190 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1191 mee_onoff_h ("Frameskip", 0, UseFrameSkip, 1, h_frameskip),
1192 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1193 mee_enum ("Region", 0, region, men_region),
1194 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1195 mee_handler ("[Display]", menu_loop_gfx_options),
1196 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1197 mee_handler ("[Advanced]", menu_loop_adv_options),
1198 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1199 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1200 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1204 static int menu_loop_options(int id, int keys)
1209 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1210 e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1211 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1213 me_loop(e_menu_options, &sel, NULL);
1218 // ------------ debug menu ------------
1220 static void draw_frame_debug(GPUFreeze_t *gpuf)
1222 int w = min(g_menuscreen_w, 1024);
1223 int h = min(g_menuscreen_h, 512);
1224 u16 *d = g_menuscreen_ptr;
1225 u16 *s = (u16 *)gpuf->psxVRam;
1229 gpuf->ulFreezeVersion = 1;
1230 if (GPU_freeze != NULL)
1231 GPU_freeze(1, gpuf);
1233 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1234 bgr555_to_rgb565(d, s, w * 2);
1236 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1237 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1238 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1239 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1240 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1243 static void debug_menu_loop(void)
1248 gpuf = malloc(sizeof(*gpuf));
1255 draw_frame_debug(gpuf);
1258 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1259 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1260 if (inp & PBTN_MBACK)
1267 // ------------ main menu ------------
1269 static void menu_bios_warn(void)
1272 static const char msg[] =
1273 "You don't seem to have copied any BIOS files to\n"
1274 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1275 "While many games work fine with fake (HLE) BIOS,\n"
1276 "others (like MGS and FF8) require BIOS to work.\n"
1277 "After copying the file, you'll also need to\n"
1278 "select it in the emu's options->[BIOS/Plugins]\n\n"
1279 "The file is usually named SCPH1001.BIN, but\n"
1280 "other not compressed files can be used too.\n\n"
1281 "Press (B) or (X) to continue";
1285 draw_menu_message(msg, NULL);
1287 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1288 if (inp & (PBTN_MBACK|PBTN_MOK))
1293 // ------------ main menu ------------
1297 static void draw_frame_main(void)
1299 if (CdromId[0] != 0) {
1301 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1302 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1303 Config.HLE ? "HLE" : "BIOS");
1304 smalltext_out16(4, 1, buff, 0x105f);
1308 static void draw_frame_credits(void)
1310 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1313 static const char credits_text[] =
1315 "(C) 1999-2003 PCSX Team\n"
1316 "(C) 2005-2009 PCSX-df Team\n"
1317 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1318 "GPU and SPU code by Pete Bernert\n"
1319 " and the P.E.Op.S. team\n"
1320 "ARM recompiler (C) 2009-2011 Ari64\n"
1321 "PCSX4ALL plugins by PCSX4ALL team\n"
1322 " Chui, Franxis, Unai\n\n"
1323 "integration, optimization and\n"
1324 " frontend (C) 2010-2011 notaz\n";
1326 static int reset_game(void)
1329 if (bios_sel == 0 && !Config.HLE)
1335 if (CheckCdrom() != -1) {
1341 static int run_bios(void)
1347 pl_fbdev_buf = NULL;
1352 pcnt_hook_plugins();
1354 if (OpenPlugins() == -1) {
1355 me_update_msg("failed to open plugins");
1358 plugin_call_rearmed_cbs();
1361 CdromLabel[0] = '\0';
1369 static int run_cd_image(const char *fname)
1372 pl_fbdev_buf = NULL;
1375 set_cd_image(fname);
1377 pcnt_hook_plugins();
1379 if (OpenPlugins() == -1) {
1380 me_update_msg("failed to open plugins");
1383 plugin_call_rearmed_cbs();
1385 if (CheckCdrom() == -1) {
1386 // Only check the CD if we are starting the console with a CD
1388 me_update_msg("unsupported/invalid CD image");
1394 // Read main executable directly from CDRom and start it
1395 if (LoadCdrom() == -1) {
1397 me_update_msg("failed to load CD image");
1405 static int romsel_run(void)
1407 int prev_gpu, prev_spu;
1410 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1414 printf("selected file: %s\n", fname);
1416 new_dynarec_clear_full();
1418 if (run_cd_image(fname) != 0)
1421 prev_gpu = gpu_plugsel;
1422 prev_spu = spu_plugsel;
1423 if (menu_load_config(1) != 0)
1424 menu_load_config(0);
1426 // check for plugin changes, have to repeat
1427 // loading if game config changed plugins to reload them
1428 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1429 printf("plugin change detected, reloading plugins..\n");
1430 if (run_cd_image(fname) != 0)
1434 strcpy(last_selected_fname, rom_fname_reload);
1438 static int swap_cd_image(void)
1442 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1446 printf("selected file: %s\n", fname);
1449 CdromLabel[0] = '\0';
1451 set_cd_image(fname);
1452 if (ReloadCdromPlugin() < 0) {
1453 me_update_msg("failed to load cdr plugin");
1456 if (CDR_open() < 0) {
1457 me_update_msg("failed to open cdr plugin");
1461 SetCdOpenCaseTime(time(NULL) + 2);
1464 strcpy(last_selected_fname, rom_fname_reload);
1468 static int main_menu_handler(int id, int keys)
1472 case MA_MAIN_RESUME_GAME:
1476 case MA_MAIN_SAVE_STATE:
1478 return menu_loop_savestate(0);
1480 case MA_MAIN_LOAD_STATE:
1482 return menu_loop_savestate(1);
1484 case MA_MAIN_RESET_GAME:
1485 if (ready_to_go && reset_game() == 0)
1488 case MA_MAIN_LOAD_ROM:
1489 if (romsel_run() == 0)
1492 case MA_MAIN_SWAP_CD:
1493 if (swap_cd_image() == 0)
1496 case MA_MAIN_RUN_BIOS:
1497 if (run_bios() == 0)
1500 case MA_MAIN_CREDITS:
1501 draw_menu_message(credits_text, draw_frame_credits);
1502 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1508 lprintf("%s: something unknown selected\n", __FUNCTION__);
1515 static menu_entry e_menu_main[] =
1519 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1520 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1521 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1522 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1523 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1524 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1525 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1526 mee_handler ("Options", menu_loop_options),
1527 mee_handler ("Controls", menu_loop_keyconfig),
1528 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1529 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1533 // ----------------------------
1535 static void menu_leave_emu(void);
1537 void menu_loop(void)
1543 if (bioses[1] == NULL && !warned_about_bios) {
1545 warned_about_bios = 1;
1548 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1549 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1550 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
1551 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1552 me_enable(e_menu_main, MA_MAIN_SWAP_CD, ready_to_go);
1553 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1555 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1558 me_loop(e_menu_main, &sel, draw_frame_main);
1559 } while (!ready_to_go);
1561 /* wait until menu, ok, back is released */
1562 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1565 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1570 static void scan_bios_plugins(void)
1572 char fname[MAXPATHLEN];
1574 int bios_i, gpu_i, spu_i;
1579 gpu_plugins[0] = "builtin_gpu";
1580 spu_plugins[0] = "builtin_spu";
1581 bios_i = gpu_i = spu_i = 1;
1583 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1584 dir = opendir(fname);
1586 perror("scan_bios_plugins bios opendir");
1601 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1604 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1605 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1606 printf("bad BIOS file: %s\n", ent->d_name);
1610 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1611 bioses[bios_i++] = strdup(ent->d_name);
1615 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1621 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1622 dir = opendir(fname);
1624 perror("scan_bios_plugins opendir");
1638 p = strstr(ent->d_name, ".so");
1642 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1643 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1645 fprintf(stderr, "%s\n", dlerror());
1649 // now what do we have here?
1650 tmp = dlsym(h, "GPUinit");
1653 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1654 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1658 tmp = dlsym(h, "SPUinit");
1661 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1662 spu_plugins[spu_i++] = strdup(ent->d_name);
1666 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1673 void menu_init(void)
1675 char buff[MAXPATHLEN];
1677 strcpy(last_selected_fname, "/media");
1679 scan_bios_plugins();
1683 menu_set_defconfig();
1684 menu_load_config(0);
1689 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1690 if (g_menubg_src_ptr == NULL)
1692 emu_make_path(buff, "skin/background.png", sizeof(buff));
1693 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1696 void menu_notify_mode_change(int w, int h, int bpp)
1702 if (scaling == SCALE_1_1) {
1703 g_layer_x = 800/2 - w/2; g_layer_y = 480/2 - h/2;
1704 g_layer_w = w; g_layer_h = h;
1708 static void menu_leave_emu(void)
1710 if (GPU_close != NULL) {
1711 int ret = GPU_close();
1713 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1716 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1717 if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
1718 int x = max(0, g_menuscreen_w - last_psx_w);
1719 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1720 int w = min(g_menuscreen_w, last_psx_w);
1721 int h = min(g_menuscreen_h, last_psx_h);
1722 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
1723 u16 *s = pl_fbdev_buf;
1725 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
1726 menu_darken_bg(d, s, w, 0);
1730 cpu_clock = get_cpu_clock();
1732 plat_video_menu_enter(ready_to_go);
1735 void menu_prepare_emu(void)
1737 R3000Acpu *prev_cpu = psxCpu;
1739 plat_video_menu_leave();
1743 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
1746 g_layer_x = 80; g_layer_y = 0;
1747 g_layer_w = 640; g_layer_h = 480;
1749 case SCALE_FULLSCREEN:
1750 g_layer_x = 0; g_layer_y = 0;
1751 g_layer_w = 800; g_layer_h = 480;
1757 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1758 if (psxCpu != prev_cpu)
1759 // note that this does not really reset, just clears drc caches
1762 // core doesn't care about Config.Cdda changes,
1763 // so handle them manually here
1768 apply_lcdrate(Config.PsxType);
1769 apply_filter(filter);
1772 if (GPU_open != NULL) {
1773 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1775 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1778 dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
1781 void me_update_msg(const char *msg)
1783 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1784 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1786 menu_error_time = plat_get_ticks_ms();
1787 lprintf("msg: %s\n", menu_error_msg);