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