initial pandora port
[fceu.git] / drivers / common / menu.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2010-2011
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
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.
9  */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <sys/stat.h>
16
17 #include "main.h"
18 #include "menu.h"
19 #include "input.h"
20 #include "settings.h"
21 #include "config.h"
22 #include "args.h"
23 #include "dface.h"
24 #include "../libpicofe/plat.h"
25 #include "../libpicofe/input.h"
26 #include "../../state.h"
27 #include "../../general.h"
28 #include "../../input.h"
29 #include "../../palette.h"
30 #include "revision.h"
31
32 #define array_size(x) (sizeof(x) / sizeof(x[0]))
33
34 #define state_slot CurrentState
35 extern uint8 Exit;
36
37 typedef enum
38 {
39         MA_NONE = 1,
40         MA_CTRL_PLAYER1,
41         MA_CTRL_PLAYER2,
42         MA_CTRL_EMU,
43         MA_CTRL_DEADZONE,
44         MA_OPT_SAVECFG,
45         MA_OPT_SAVECFG_GAME,
46         MA_CTRL_DEV_FIRST,
47         MA_CTRL_DEV_NEXT,
48         MA_OPT_NTSC_COLOR,
49         MA_OPT_NTSC_TINT,
50         MA_OPT_NTSC_HUE,
51         MA_OPT_SREND_N,
52         MA_OPT_EREND_N,
53         MA_OPT_SREND_P,
54         MA_OPT_EREND_P,
55         MA_OPT_CLIP,
56         MA_OPT_NO8LIM,
57         MA_OPT_GG,
58         MA_OPT_SHOWFPS,
59         MA_OPT_FSKIP,
60         MA_OPT_RENDERER,
61         MA_OPT_SOUNDON,
62         MA_OPT_SOUNDRATE,
63         MA_OPT_REGION,
64         MA_OPT_TURBO,
65         MA_OPT_SSTATE,
66         MA_OPT_SSLOT,
67         MA_OPT_GAMMA,
68         MA_MAIN_RESUME_GAME,
69         MA_MAIN_SAVE_STATE,
70         MA_MAIN_LOAD_STATE,
71         MA_MAIN_RESET_GAME,
72         MA_MAIN_LOAD_ROM,
73         MA_MAIN_CHEATS,
74         MA_MAIN_CREDITS,
75         MA_MAIN_EXIT,
76 } menu_id;
77
78 void emu_make_path(char *buff, const char *end, int size)
79 {
80         int pos, end_len;
81
82         end_len = strlen(end);
83         pos = plat_get_root_dir(buff, size);
84         strncpy(buff + pos, end, size - pos);
85         buff[size - 1] = 0;
86         if (pos + end_len > size - 1)
87                 printf("Warning: path truncated: %s\n", buff);
88 }
89
90 static int emu_check_save_file(int slot, int *time)
91 {
92         struct stat status;
93         char *fname;
94         FILE *st;
95         int retval = 0;
96         int ret;
97         
98         fname = FCEU_MakeFName(FCEUMKF_STATE, slot, 0);
99         st = fopen(fname,"rb");
100         if (st == NULL)
101                 goto out;
102         fclose(st);
103
104         retval = 1;
105         if (time == NULL)
106                 goto out;
107
108         ret = stat(fname, &status);
109         if (ret != 0)
110                 goto out;
111
112         if (status.st_mtime < 1347000000)
113                 goto out; // probably bad rtc like on some Caanoos
114
115         *time = status.st_mtime;
116
117 out:
118         free(fname);
119         return retval;
120 }
121
122 static int emu_save_load_game(int load, int unused)
123 {
124         if (load)
125                 FCEUI_LoadState();
126         else
127                 FCEUI_SaveState();
128
129         return 0;
130 }
131
132 // rrrr rggg gggb bbbb
133 static unsigned short fname2color(const char *fname)
134 {
135         static const char *rom_exts[]   = { ".zip", ".nes", ".fds", ".unf",
136                                             ".nez", ".unif" };
137         static const char *other_exts[] = { ".nsf", ".ips", ".fcm" };
138         const char *ext = strrchr(fname, '.');
139         int i;
140
141         if (ext == NULL)
142                 return 0xffff;
143         for (i = 0; i < array_size(rom_exts); i++)
144                 if (strcasecmp(ext, rom_exts[i]) == 0)
145                         return 0xbdff;
146         for (i = 0; i < array_size(other_exts); i++)
147                 if (strcasecmp(ext, other_exts[i]) == 0)
148                         return 0xaff5;
149         return 0xffff;
150 }
151
152 static const char *filter_exts[] = {
153         ".txt", ".srm", ".pnd"
154 };
155
156 #define MENU_ALIGN_LEFT
157 #ifdef __ARM_ARCH_7A__ // assume hires device
158 #define MENU_X2 1
159 #else
160 #define MENU_X2 0
161 #endif
162
163 #include "../libpicofe/menu.c"
164
165 static void draw_savestate_bg(int slot)
166 {
167 }
168
169 static void debug_menu_loop(void)
170 {
171 }
172
173 // ------------ patch/gg menu ------------
174
175 extern void *cheats;
176 static int cheat_count, cheat_start, cheat_pos;
177
178 static int countcallb(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data)
179 {
180         cheat_count++;
181         return 1;
182 }
183
184 static int clistcallb(char *name, uint32 a, uint8 v, int compare, int s, int type, void *data)
185 {
186         int pos;
187
188         pos = cheat_start + cheat_pos;
189         cheat_pos++;
190         if (pos < 0) return 1;
191         if (pos >= g_menuscreen_h / me_sfont_h) return 0;
192
193         smalltext_out16(14, pos * me_sfont_h, s ? "ON " : "OFF", 0xffff);
194         smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h, type ? "S" : "R", 0xffff);
195         smalltext_out16(14 + me_sfont_w*6, pos * me_sfont_h, name, 0xffff);
196
197         return 1;
198 }
199
200 static void draw_patchlist(int sel)
201 {
202         int pos, max_cnt;
203
204         menu_draw_begin(1, 1);
205
206         max_cnt = g_menuscreen_h / me_sfont_h;
207         cheat_start = max_cnt / 2 - sel;
208         cheat_pos = 0;
209         FCEUI_ListCheats(clistcallb, 0);
210
211         pos = cheat_start + cheat_pos;
212         if (pos < max_cnt)
213                 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
214
215         text_out16(5, max_cnt / 2 * me_sfont_h, ">");
216         menu_draw_end();
217 }
218
219 void patches_menu_loop(void)
220 {
221         static int menu_sel = 0;
222         int inp;
223
224         cheat_count = 0;
225         FCEUI_ListCheats(countcallb, 0);
226
227         for (;;)
228         {
229                 draw_patchlist(menu_sel);
230                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
231                                 |PBTN_MOK|PBTN_MBACK, NULL, 33);
232                 if (inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = cheat_count; }
233                 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > cheat_count) menu_sel = 0; }
234                 if (inp &(PBTN_LEFT|PBTN_L))  { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
235                 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > cheat_count) menu_sel = cheat_count; }
236                 if (inp & PBTN_MOK) { // action
237                         if (menu_sel < cheat_count)
238                                 FCEUI_ToggleCheat(menu_sel);
239                         else    break;
240                 }
241                 if (inp & PBTN_MBACK)
242                         break;
243         }
244 }
245
246 // -------------- key config --------------
247
248 // b_turbo,a_turbo  RLDU SEBA
249 me_bind_action me_ctrl_actions[] =
250 {
251         { "UP     ", NKEY_UP      },
252         { "DOWN   ", NKEY_DOWN    },
253         { "LEFT   ", NKEY_LEFT    },
254         { "RIGHT  ", NKEY_RIGHT   },
255         { "A      ", NKEY_A       },
256         { "B      ", NKEY_B       },
257         { "A TURBO", NKEY_A_TURBO },
258         { "B TURBO", NKEY_B_TURBO },
259         { "START  ", NKEY_START   },
260         { "SELECT ", NKEY_SELECT  },
261         { NULL, 0 }
262 };
263
264 me_bind_action emuctrl_actions[] =
265 {
266         { "Save State       ", EACT_SAVE_STATE },
267         { "Load State       ", EACT_LOAD_STATE },
268         { "Next State Slot  ", EACT_NEXT_SLOT },
269         { "Prev State Slot  ", EACT_PREV_SLOT },
270         { "FDS Insert/Eject ", EACT_FDS_INSERT },
271         { "FDS Select Disk  ", EACT_FDS_SELECT },
272         { "VSUni Insert Coin", EACT_INSERT_COIN },
273         { "Enter Menu       ", EACT_ENTER_MENU },
274         { NULL,                0 }
275 };
276
277 static int key_config_loop_wrap(int id, int keys)
278 {
279         switch (id) {
280                 case MA_CTRL_PLAYER1:
281                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
282                         break;
283                 case MA_CTRL_PLAYER2:
284                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
285                         break;
286                 case MA_CTRL_EMU:
287                         key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
288                         break;
289                 default:
290                         break;
291         }
292         return 0;
293 }
294
295 static const char *mgn_dev_name(int id, int *offs)
296 {
297         const char *name = NULL;
298         static int it = 0;
299
300         if (id == MA_CTRL_DEV_FIRST)
301                 it = 0;
302
303         for (; it < IN_MAX_DEVS; it++) {
304                 name = in_get_dev_name(it, 1, 1);
305                 if (name != NULL)
306                         break;
307         }
308
309         it++;
310         return name;
311 }
312
313 static const char *mgn_saveloadcfg(int id, int *offs)
314 {
315         return "";
316 }
317
318 static void config_commit(void);
319
320 static int mh_savecfg(int id, int keys)
321 {
322         const char *fname = NULL;
323         if (id == MA_OPT_SAVECFG_GAME)
324                 fname = lastLoadedGameName;
325
326         config_commit();
327         if (SaveConfig(fname) == 0)
328                 menu_update_msg("config saved");
329         else
330                 menu_update_msg("failed to write config");
331
332         return 1;
333 }
334
335 static int mh_input_rescan(int id, int keys)
336 {
337         //menu_sync_config();
338         in_probe();
339         menu_update_msg("rescan complete.");
340
341         return 0;
342 }
343
344 static menu_entry e_menu_keyconfig[] =
345 {
346         mee_handler_id("Player 1",              MA_CTRL_PLAYER1,    key_config_loop_wrap),
347         mee_handler_id("Player 2",              MA_CTRL_PLAYER2,    key_config_loop_wrap),
348         mee_handler_id("Emulator controls",     MA_CTRL_EMU,        key_config_loop_wrap),
349         mee_label     (""),
350 //      mee_range     ("Analog deadzone",   MA_CTRL_DEADZONE,   analog_deadzone, 1, 99),
351         mee_cust_nosave("Save global config",       MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
352         mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
353         mee_handler   ("Rescan devices:",  mh_input_rescan),
354         mee_label     (""),
355         mee_label_mk  (MA_CTRL_DEV_FIRST, mgn_dev_name),
356         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
357         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
358         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
359         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
360         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
361         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
362         mee_end,
363 };
364
365 static int menu_loop_keyconfig(int id, int keys)
366 {
367         static int sel = 0;
368
369         me_loop(e_menu_keyconfig, &sel);
370         return 0;
371 }
372
373 // --------- FCEU options ----------
374
375 extern int ntsccol,ntschue,ntsctint;
376 extern int srendlinev[2];
377 extern int erendlinev[2];
378 extern int eoptions;
379 extern char *cpalette;
380 extern void LoadCPalette(void);
381
382 static menu_entry e_menu_fceu_options[] =
383 {
384         //gp2x_text_out15(tl_x,  y,      "Custom palette: %s", cpal);
385         mee_onoff     ("NTSC Color Emulation",      MA_OPT_NTSC_COLOR, ntsccol, 1),
386         mee_range     ("  Tint (default: 56)",      MA_OPT_NTSC_TINT, ntsctint, 0, 128),
387         mee_range     ("  Hue  (default: 72)",      MA_OPT_NTSC_HUE, ntschue, 0, 128),
388         mee_range     ("First visible line (NTSC)", MA_OPT_SREND_N, srendlinev[0], 0, 239),
389         mee_range     ("Last visible line (NTSC)",  MA_OPT_EREND_N, erendlinev[0], 0, 239),
390         mee_range     ("First visible line (PAL)",  MA_OPT_SREND_P, srendlinev[1], 0, 239),
391         mee_range     ("Last visible line (PAL)",   MA_OPT_EREND_P, erendlinev[1], 0, 239),
392         mee_onoff     ("Clip 8 left/right columns", MA_OPT_CLIP, eoptions, EO_CLIPSIDES),
393         mee_onoff     ("Disable 8 sprite limit",    MA_OPT_NO8LIM, eoptions, EO_NO8LIM),
394         mee_onoff     ("Enable authentic GameGenie",MA_OPT_GG, eoptions, EO_GG),
395         mee_end,
396 };
397
398 static int menu_loop_fceu_options(int id, int keys)
399 {
400         static int sel = 0;
401         int i;
402
403         FCEUI_GetNTSCTH(&ntsctint, &ntschue);
404
405         me_loop(e_menu_fceu_options, &sel);
406
407         for(i = 0; i < 2; i++)
408         {
409                 if (srendlinev[i] < 0 || srendlinev[i] > 239)
410                         srendlinev[i] = 0;
411                 if (erendlinev[i] < srendlinev[i] || erendlinev[i] > 239)
412                         erendlinev[i] = 239;
413         }
414         FCEUI_SetNTSCTH(ntsccol, ntsctint, ntschue);
415         FCEUI_SetRenderedLines(srendlinev[0],erendlinev[0],srendlinev[1],erendlinev[1]);
416         FCEUI_DisableSpriteLimitation(eoptions&EO_NO8LIM);
417         FCEUI_SetGameGenie(eoptions&EO_GG);
418         //if (cpalette) LoadCPalette();
419         //else 
420         FCEUI_SetPaletteArray(0); // set to default
421         FCEU_ResetPalette();
422
423         return 0;
424 }
425
426 // -------------- options --------------
427
428 static const char *men_frameskip[] = { "Auto", "0", "1", "2", "3", "4", NULL };
429 static const char *men_rates[]   = { "8000", "11025", "16000", "22050", "44100", NULL };
430 static const int   men_rates_i[] = {  8000 ,  11025 ,  16000 ,  22050 ,  44100 };
431 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
432 static const char *men_sstate[] = { "OFF", "writes", "loads", "both", NULL };
433 static const char h_renderer[] = "ROM reload required for this\n"
434                                  "setting to take effect";
435
436 static int sndrate_i;
437 static int sndon;
438 static int turbo_i;
439 static int frameskip_i;
440
441 static void config_commit(void)
442 {
443         Settings.sound_rate = men_rates_i[sndrate_i];
444         soundvol = sndon ? 50 : 0;
445         Settings.turbo_rate_add = (turbo_i * 2 << 24) / 60 + 1;
446         Settings.frameskip = frameskip_i - 1;
447
448         if (Settings.region_force)
449                 FCEUI_SetVidSystem(Settings.region_force - 1);
450 }
451
452 static menu_entry e_menu_options[] =
453 {
454         mee_onoff      ("Show FPS",                MA_OPT_SHOWFPS, Settings.showfps, 1),
455         mee_enum       ("Frameskip",               MA_OPT_FSKIP, frameskip_i, men_frameskip),
456         mee_onoff_h    ("Accurate renderer (slow)",MA_OPT_RENDERER, Settings.accurate_mode, 1, h_renderer),
457         mee_onoff      ("Enable sound",            MA_OPT_SOUNDON, sndon, 1),
458         mee_enum       ("Sound Rate",              MA_OPT_SOUNDRATE, sndrate_i, men_rates),
459         mee_enum       ("Region",                  MA_OPT_REGION, Settings.region_force, men_region),
460         mee_range      ("Turbo rate (Hz)",         MA_OPT_TURBO, turbo_i, 1, 30),
461         mee_enum       ("Confirm savestate",       MA_OPT_SSTATE, Settings.sstate_confirm, men_sstate),
462         mee_range      ("Save slot",               MA_OPT_SSLOT, CurrentState, 0, 9),
463         mee_range      ("Gamma correction",        MA_OPT_GAMMA, Settings.gamma, 0, 300),
464         mee_handler    ("[FCE Ultra options]",     menu_loop_fceu_options),
465         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
466         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
467         mee_end,
468 };
469
470 static int menu_loop_options(int id, int keys)
471 {
472         static int sel = 0;
473         int oldrate;
474         int i;
475
476         oldrate = Settings.sound_rate;
477         for (i = 0; i < array_size(men_rates_i); i++) {
478                 if (Settings.sound_rate == men_rates_i[i]) {
479                         sndrate_i = i;
480                         break;
481                 }
482         }
483         sndon = soundvol != 0;
484         turbo_i = (Settings.turbo_rate_add * 60 / 2) >> 24;
485         frameskip_i = Settings.frameskip + 1;
486
487         me_loop(e_menu_options, &sel);
488
489         config_commit();
490         if (oldrate != Settings.sound_rate)
491                 InitSound();
492
493         return 0;
494 }
495
496 // -------------- root menu --------------
497
498 static const char credits_text[] = 
499         "GPFCE " REV "\n"
500         "(c) notaz, 2007,2012\n\n"
501         "Based on FCE Ultra versions\n"
502         "0.81 and 0.98.1x\n\n"
503         "         - Credits -\n"
504         "Bero: FCE\n"
505         "Xodnizel: FCE Ultra\n"
506         "FCA author: 6502 core\n";
507
508 static void draw_frame_credits(void)
509 {
510         smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
511 }
512
513 static int romsel_run(void)
514 {
515         const char *fname;
516
517         fname = menu_loop_romsel(lastLoadedGameName, sizeof(lastLoadedGameName));
518         if (fname == NULL)
519                 return -1;
520
521         printf("selected file: %s\n", fname);
522         //keys_load_all(cfg);
523
524         strcpy(lastLoadedGameName, rom_fname_reload);
525         return 0;
526 }
527
528 static int menu_loop_ret;
529
530 static int main_menu_handler(int id, int keys)
531 {
532         switch (id)
533         {
534         case MA_MAIN_RESUME_GAME:
535                 if (fceugi)
536                         return 1;
537                 break;
538         case MA_MAIN_SAVE_STATE:
539                 if (fceugi) {
540                         return menu_loop_savestate(0);
541                 }
542                 break;
543         case MA_MAIN_LOAD_STATE:
544                 if (fceugi) {
545                         return menu_loop_savestate(1);
546                 }
547                 break;
548         case MA_MAIN_RESET_GAME:
549                 if (fceugi) {
550                         FCEU_DoSimpleCommand(FCEUNPCMD_RESET);
551                         return 1;
552                 }
553                 break;
554         case MA_MAIN_LOAD_ROM:
555                 if (romsel_run() == 0) {
556                         menu_loop_ret = 2;
557                         return 1;
558                 }
559                 break;
560         case MA_MAIN_CREDITS:
561                 draw_menu_message(credits_text, draw_frame_credits);
562                 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
563                 break;
564         case MA_MAIN_EXIT:
565                 menu_loop_ret = 1;
566                 return 1;
567         default:
568                 lprintf("%s: something unknown selected\n", __FUNCTION__);
569                 break;
570         }
571
572         return 0;
573 }
574
575 static menu_entry e_menu_main[] =
576 {
577         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
578         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
579         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
580         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
581         mee_handler_id("Load ROM",           MA_MAIN_LOAD_ROM,    main_menu_handler),
582         mee_handler   ("Options",            menu_loop_options),
583         mee_handler   ("Controls",           menu_loop_keyconfig),
584         mee_handler_id("Cheats",             MA_MAIN_CHEATS,      main_menu_handler),
585         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
586         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
587         mee_end,
588 };
589
590 // ----------------------------
591
592 int menu_loop(void)
593 {
594         static int sel = 0;
595
596         menu_loop_ret = 0;
597
598         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, fceugi != NULL);
599         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  fceugi != NULL);
600         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  fceugi != NULL);
601         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  fceugi != NULL);
602         me_enable(e_menu_main, MA_MAIN_CHEATS,      fceugi && cheats);
603
604         plat_video_menu_enter(fceugi != NULL);
605         in_set_config_int(0, IN_CFG_BLOCKING, 1);
606
607         do {
608                 me_loop_d(e_menu_main, &sel, NULL, NULL);
609         } while (!fceugi && menu_loop_ret == 0);
610
611         /* wait until menu, ok, back is released */
612         while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
613                 ;
614
615         in_set_config_int(0, IN_CFG_BLOCKING, 0);
616         plat_video_menu_leave();
617
618         Exit = 0;
619         return menu_loop_ret;
620 }
621
622 void menu_init(void)
623 {
624         char buff[256];
625
626         g_border_style = 1;
627         menu_init_base();
628
629         //menu_load_config(0);
630
631         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
632         g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
633         if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
634                 fprintf(stderr, "OOM\n");
635                 exit(1);
636         }
637
638         emu_make_path(buff, "skin/background.png", sizeof(buff));
639         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
640 }
641
642 void menu_update_msg(const char *msg)
643 {
644         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
645         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
646
647         menu_error_time = plat_get_ticks_ms();
648         lprintf("msg: %s\n", menu_error_msg);
649 }
650