move blit to core, allow filtering while blitting
[pcsx_rearmed.git] / frontend / 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 <string.h>
13 #include <errno.h>
14 #include <dlfcn.h>
15 #include <zlib.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <unistd.h>
19
20 #include "main.h"
21 #include "menu.h"
22 #include "config.h"
23 #include "plugin.h"
24 #include "plugin_lib.h"
25 #include "plat.h"
26 #include "pcnt.h"
27 #include "common/plat.h"
28 #include "common/input.h"
29 #include "linux/in_evdev.h"
30 #include "../libpcsxcore/misc.h"
31 #include "../libpcsxcore/cdrom.h"
32 #include "../libpcsxcore/cdriso.h"
33 #include "../libpcsxcore/cheat.h"
34 #include "../libpcsxcore/psemu_plugin_defs.h"
35 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
36 #include "../plugins/dfinput/main.h"
37 #include "../plugins/gpulib/cspace.h"
38 #include "revision.h"
39
40 #define REARMED_BIRTHDAY_TIME 1293306830        /* 25 Dec 2010 */
41
42 #define array_size(x) (sizeof(x) / sizeof(x[0]))
43
44 typedef enum
45 {
46         MA_NONE = 1,
47         MA_MAIN_RESUME_GAME,
48         MA_MAIN_SAVE_STATE,
49         MA_MAIN_LOAD_STATE,
50         MA_MAIN_RESET_GAME,
51         MA_MAIN_LOAD_ROM,
52         MA_MAIN_SWAP_CD,
53         MA_MAIN_SWAP_CD_MULTI,
54         MA_MAIN_RUN_BIOS,
55         MA_MAIN_RUN_EXE,
56         MA_MAIN_LOAD_CHEATS,
57         MA_MAIN_CHEATS,
58         MA_MAIN_CONTROLS,
59         MA_MAIN_CREDITS,
60         MA_MAIN_EXIT,
61         MA_CTRL_PLAYER1,
62         MA_CTRL_PLAYER2,
63         MA_CTRL_ANALOG,
64         MA_CTRL_EMU,
65         MA_CTRL_DEV_FIRST,
66         MA_CTRL_DEV_NEXT,
67         MA_CTRL_NUBS_BTNS,
68         MA_CTRL_DEADZONE,
69         MA_CTRL_VIBRATION,
70         MA_CTRL_DONE,
71         MA_OPT_SAVECFG,
72         MA_OPT_SAVECFG_GAME,
73         MA_OPT_CPU_CLOCKS,
74         MA_OPT_DISP_OPTS,
75         MA_OPT_SCALER,
76         MA_OPT_SCALER2,
77         MA_OPT_FILTERING,
78         MA_OPT_FILTERING2,
79         MA_OPT_SCALER_C,
80 } menu_id;
81
82 static int last_vout_w, last_vout_h, last_vout_bpp;
83 static int cpu_clock, cpu_clock_st, volume_boost, frameskip;
84 static char rom_fname_reload[MAXPATHLEN];
85 static char last_selected_fname[MAXPATHLEN];
86 static int warned_about_bios, region, in_type_sel1, in_type_sel2;
87 static int psx_clock;
88 static int memcard1_sel, memcard2_sel;
89 int g_opts, g_scaler;
90 int soft_scaling, analog_deadzone; // for Caanoo
91 int filter, soft_filter;
92
93 #ifdef __ARM_ARCH_7A__
94 #define DEFAULT_PSX_CLOCK 57
95 #define DEFAULT_PSX_CLOCK_S "57"
96 #else
97 #define DEFAULT_PSX_CLOCK 50
98 #define DEFAULT_PSX_CLOCK_S "50"
99 #endif
100
101 // sound plugin
102 extern int iUseReverb;
103 extern int iUseInterpolation;
104 extern int iXAPitch;
105 extern int iVolume;
106
107 static const char *bioses[24];
108 static const char *gpu_plugins[16];
109 static const char *spu_plugins[16];
110 static const char *memcards[32];
111 static int bios_sel, gpu_plugsel, spu_plugsel;
112
113
114 static int min(int x, int y) { return x < y ? x : y; }
115 static int max(int x, int y) { return x > y ? x : y; }
116
117 void emu_make_path(char *buff, const char *end, int size)
118 {
119         int pos, end_len;
120
121         end_len = strlen(end);
122         pos = plat_get_root_dir(buff, size);
123         strncpy(buff + pos, end, size - pos);
124         buff[size - 1] = 0;
125         if (pos + end_len > size - 1)
126                 printf("Warning: path truncated: %s\n", buff);
127 }
128
129 static int emu_check_save_file(int slot, int *time)
130 {
131         char fname[MAXPATHLEN];
132         struct stat status;
133         int ret;
134         
135         ret = emu_check_state(slot);
136         if (ret != 0 || time == NULL)
137                 return ret == 0 ? 1 : 0;
138
139         ret = get_state_filename(fname, sizeof(fname), slot);
140         if (ret != 0)
141                 return 1;
142
143         ret = stat(fname, &status);
144         if (ret != 0)
145                 return 1;
146
147         if (status.st_mtime < REARMED_BIRTHDAY_TIME)
148                 return 1; // probably bad rtc like on some Caanoos
149
150         *time = status.st_mtime;
151
152         return 1;
153 }
154
155 static int emu_save_load_game(int load, int unused)
156 {
157         int ret;
158
159         if (load) {
160                 ret = emu_load_state(state_slot);
161
162                 // reflect hle/bios mode from savestate
163                 if (Config.HLE)
164                         bios_sel = 0;
165                 else if (bios_sel == 0 && bioses[1] != NULL)
166                         // XXX: maybe find the right bios instead
167                         bios_sel = 1;
168         }
169         else
170                 ret = emu_save_state(state_slot);
171
172         return ret;
173 }
174
175 // propagate menu settings to the emu vars
176 static void menu_sync_config(void)
177 {
178         static int allow_abs_only_old;
179
180         Config.PsxAuto = 1;
181         if (region > 0) {
182                 Config.PsxAuto = 0;
183                 Config.PsxType = region - 1;
184         }
185         cycle_multiplier = 10000 / psx_clock;
186
187         switch (in_type_sel1) {
188         case 1:  in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
189         case 2:  in_type1 = PSE_PAD_TYPE_GUNCON;    break;
190         default: in_type1 = PSE_PAD_TYPE_STANDARD;
191         }
192         switch (in_type_sel2) {
193         case 1:  in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
194         case 2:  in_type2 = PSE_PAD_TYPE_GUNCON;    break;
195         default: in_type2 = PSE_PAD_TYPE_STANDARD;
196         }
197         if (in_evdev_allow_abs_only != allow_abs_only_old) {
198                 in_probe();
199                 allow_abs_only_old = in_evdev_allow_abs_only;
200         }
201
202         iVolume = 768 + 128 * volume_boost;
203         pl_rearmed_cbs.frameskip = frameskip - 1;
204         pl_timing_prepare(Config.PsxType);
205 }
206
207 static void menu_set_defconfig(void)
208 {
209         emu_set_default_config();
210
211         g_opts = 0;
212         g_scaler = SCALE_4_3;
213         volume_boost = 0;
214         frameskip = 0;
215         analog_deadzone = 50;
216         soft_scaling = 1;
217         soft_filter = 0;
218         psx_clock = DEFAULT_PSX_CLOCK;
219
220         region = 0;
221         in_type_sel1 = in_type_sel2 = 0;
222         in_evdev_allow_abs_only = 0;
223
224         menu_sync_config();
225 }
226
227 #define CE_CONFIG_STR(val) \
228         { #val, 0, Config.val }
229
230 #define CE_CONFIG_VAL(val) \
231         { #val, sizeof(Config.val), &Config.val }
232
233 #define CE_STR(val) \
234         { #val, 0, val }
235
236 #define CE_INTVAL(val) \
237         { #val, sizeof(val), &val }
238
239 #define CE_INTVAL_P(val) \
240         { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
241
242 // 'versioned' var, used when defaults change
243 #define CE_CONFIG_STR_V(val, ver) \
244         { #val #ver, 0, Config.val }
245
246 #define CE_INTVAL_V(val, ver) \
247         { #val #ver, sizeof(val), &val }
248
249 #define CE_INTVAL_PV(val, ver) \
250         { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
251
252 static const struct {
253         const char *name;
254         size_t len;
255         void *val;
256 } config_data[] = {
257         CE_CONFIG_STR(Bios),
258         CE_CONFIG_STR_V(Gpu, 3),
259         CE_CONFIG_STR(Spu),
260 //      CE_CONFIG_STR(Cdr),
261         CE_CONFIG_VAL(Xa),
262         CE_CONFIG_VAL(Sio),
263         CE_CONFIG_VAL(Mdec),
264         CE_CONFIG_VAL(Cdda),
265         CE_CONFIG_VAL(Debug),
266         CE_CONFIG_VAL(PsxOut),
267         CE_CONFIG_VAL(SpuIrq),
268         CE_CONFIG_VAL(RCntFix),
269         CE_CONFIG_VAL(VSyncWA),
270         CE_CONFIG_VAL(Cpu),
271         CE_CONFIG_VAL(CdrReschedule),
272         CE_INTVAL(region),
273         CE_INTVAL_V(g_scaler, 2),
274         CE_INTVAL(g_layer_x),
275         CE_INTVAL(g_layer_y),
276         CE_INTVAL(g_layer_w),
277         CE_INTVAL(g_layer_h),
278         CE_INTVAL(filter),
279         CE_INTVAL(soft_filter),
280         CE_INTVAL(state_slot),
281         CE_INTVAL(cpu_clock),
282         CE_INTVAL(g_opts),
283         CE_INTVAL(in_type_sel1),
284         CE_INTVAL(in_type_sel2),
285         CE_INTVAL(analog_deadzone),
286         CE_INTVAL_V(frameskip, 3),
287         CE_INTVAL_P(gpu_peops.iUseDither),
288         CE_INTVAL_P(gpu_peops.dwActFixes),
289         CE_INTVAL_P(gpu_unai.lineskip),
290         CE_INTVAL_P(gpu_unai.abe_hack),
291         CE_INTVAL_P(gpu_unai.no_light),
292         CE_INTVAL_P(gpu_unai.no_blend),
293         CE_INTVAL_P(gpu_neon.allow_interlace),
294         CE_INTVAL_P(gpu_neon.enhancement_enable),
295         CE_INTVAL_P(gpu_neon.enhancement_no_main),
296         CE_INTVAL_P(gpu_peopsgl.bDrawDither),
297         CE_INTVAL_P(gpu_peopsgl.iFilterType),
298         CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
299         CE_INTVAL_P(gpu_peopsgl.iUseMask),
300         CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
301         CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
302         CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
303         CE_INTVAL_P(gpu_peopsgl.iVRamSize),
304         CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
305         CE_INTVAL_P(gpu_peopsgl.dwActFixes),
306         CE_INTVAL_V(iUseReverb, 3),
307         CE_INTVAL_V(iXAPitch, 3),
308         CE_INTVAL_V(iUseInterpolation, 3),
309         CE_INTVAL(warned_about_bios),
310         CE_INTVAL(in_evdev_allow_abs_only),
311         CE_INTVAL(volume_boost),
312         CE_INTVAL(psx_clock),
313         CE_INTVAL(new_dynarec_hacks),
314         CE_INTVAL(in_enable_vibration),
315 };
316
317 static char *get_cd_label(void)
318 {
319         static char trimlabel[33];
320         int j;
321
322         strncpy(trimlabel, CdromLabel, 32);
323         trimlabel[32] = 0;
324         for (j = 31; j >= 0; j--)
325                 if (trimlabel[j] == ' ')
326                         trimlabel[j] = 0;
327
328         return trimlabel;
329 }
330
331 static void make_cfg_fname(char *buf, size_t size, int is_game)
332 {
333         if (is_game)
334                 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
335         else
336                 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
337 }
338
339 static void keys_write_all(FILE *f);
340 static char *mystrip(char *str);
341
342 static int menu_write_config(int is_game)
343 {
344         char cfgfile[MAXPATHLEN];
345         FILE *f;
346         int i;
347
348         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
349         f = fopen(cfgfile, "w");
350         if (f == NULL) {
351                 printf("menu_write_config: failed to open: %s\n", cfgfile);
352                 return -1;
353         }
354
355         for (i = 0; i < ARRAY_SIZE(config_data); i++) {
356                 fprintf(f, "%s = ", config_data[i].name);
357                 switch (config_data[i].len) {
358                 case 0:
359                         fprintf(f, "%s\n", (char *)config_data[i].val);
360                         break;
361                 case 1:
362                         fprintf(f, "%x\n", *(u8 *)config_data[i].val);
363                         break;
364                 case 2:
365                         fprintf(f, "%x\n", *(u16 *)config_data[i].val);
366                         break;
367                 case 4:
368                         fprintf(f, "%x\n", *(u32 *)config_data[i].val);
369                         break;
370                 default:
371                         printf("menu_write_config: unhandled len %d for %s\n",
372                                  (int)config_data[i].len, config_data[i].name);
373                         break;
374                 }
375         }
376
377         keys_write_all(f);
378         fclose(f);
379
380         return 0;
381 }
382
383 static int menu_do_last_cd_img(int is_get)
384 {
385         char path[256];
386         FILE *f;
387         int ret;
388
389         snprintf(path, sizeof(path), "." PCSX_DOT_DIR "lastcdimg.txt");
390         f = fopen(path, is_get ? "r" : "w");
391         if (f == NULL)
392                 return -1;
393
394         if (is_get) {
395                 ret = fread(last_selected_fname, 1, sizeof(last_selected_fname) - 1, f);
396                 last_selected_fname[ret] = 0;
397                 mystrip(last_selected_fname);
398         }
399         else
400                 fprintf(f, "%s\n", last_selected_fname);
401         fclose(f);
402
403         return 0;
404 }
405
406 static void parse_str_val(char *cval, const char *src)
407 {
408         char *tmp;
409         strncpy(cval, src, MAXPATHLEN);
410         cval[MAXPATHLEN - 1] = 0;
411         tmp = strchr(cval, '\n');
412         if (tmp == NULL)
413                 tmp = strchr(cval, '\r');
414         if (tmp != NULL)
415                 *tmp = 0;
416 }
417
418 static void keys_load_all(const char *cfg);
419
420 static int menu_load_config(int is_game)
421 {
422         char cfgfile[MAXPATHLEN];
423         int i, ret = -1;
424         long size;
425         char *cfg;
426         FILE *f;
427
428         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
429         f = fopen(cfgfile, "r");
430         if (f == NULL) {
431                 printf("menu_load_config: failed to open: %s\n", cfgfile);
432                 goto fail;
433         }
434
435         fseek(f, 0, SEEK_END);
436         size = ftell(f);
437         if (size <= 0) {
438                 printf("bad size %ld: %s\n", size, cfgfile);
439                 goto fail;
440         }
441
442         cfg = malloc(size + 1);
443         if (cfg == NULL)
444                 goto fail;
445
446         fseek(f, 0, SEEK_SET);
447         if (fread(cfg, 1, size, f) != size) {
448                 printf("failed to read: %s\n", cfgfile);
449                 goto fail_read;
450         }
451         cfg[size] = 0;
452
453         for (i = 0; i < ARRAY_SIZE(config_data); i++) {
454                 char *tmp, *tmp2;
455                 u32 val;
456
457                 tmp = strstr(cfg, config_data[i].name);
458                 if (tmp == NULL)
459                         continue;
460                 tmp += strlen(config_data[i].name);
461                 if (strncmp(tmp, " = ", 3) != 0)
462                         continue;
463                 tmp += 3;
464
465                 if (config_data[i].len == 0) {
466                         parse_str_val(config_data[i].val, tmp);
467                         continue;
468                 }
469
470                 tmp2 = NULL;
471                 val = strtoul(tmp, &tmp2, 16);
472                 if (tmp2 == NULL || tmp == tmp2)
473                         continue; // parse failed
474
475                 switch (config_data[i].len) {
476                 case 1:
477                         *(u8 *)config_data[i].val = val;
478                         break;
479                 case 2:
480                         *(u16 *)config_data[i].val = val;
481                         break;
482                 case 4:
483                         *(u32 *)config_data[i].val = val;
484                         break;
485                 default:
486                         printf("menu_load_config: unhandled len %d for %s\n",
487                                  (int)config_data[i].len, config_data[i].name);
488                         break;
489                 }
490         }
491
492         if (!is_game) {
493                 char *tmp = strstr(cfg, "lastcdimg = ");
494                 if (tmp != NULL) {
495                         tmp += 12;
496                         parse_str_val(last_selected_fname, tmp);
497                 }
498         }
499
500         keys_load_all(cfg);
501         ret = 0;
502 fail_read:
503         free(cfg);
504 fail:
505         if (f != NULL)
506                 fclose(f);
507
508         menu_sync_config();
509
510         // sync plugins
511         for (i = bios_sel = 0; bioses[i] != NULL; i++)
512                 if (strcmp(Config.Bios, bioses[i]) == 0)
513                         { bios_sel = i; break; }
514
515         for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
516                 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
517                         { gpu_plugsel = i; break; }
518
519         for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
520                 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
521                         { spu_plugsel = i; break; }
522
523         return ret;
524 }
525
526 // rrrr rggg gggb bbbb
527 static unsigned short fname2color(const char *fname)
528 {
529         static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z",
530                                             ".bz", ".znx", ".pbp", ".cbn" };
531         static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub",
532                                             ".table", ".index", ".sbi" };
533         const char *ext = strrchr(fname, '.');
534         int i;
535
536         if (ext == NULL)
537                 return 0xffff;
538         for (i = 0; i < array_size(cdimg_exts); i++)
539                 if (strcasecmp(ext, cdimg_exts[i]) == 0)
540                         return 0x7bff;
541         for (i = 0; i < array_size(other_exts); i++)
542                 if (strcasecmp(ext, other_exts[i]) == 0)
543                         return 0xa514;
544         return 0xffff;
545 }
546
547 static void draw_savestate_bg(int slot);
548
549 static const char *filter_exts[] = {
550         ".mp3", ".MP3", ".txt", ".htm", "html", ".jpg", ".pnd"
551 };
552
553 #define MENU_ALIGN_LEFT
554 #ifdef __ARM_ARCH_7A__ // assume hires device
555 #define MENU_X2 1
556 #else
557 #define MENU_X2 0
558 #endif
559
560 #define menu_init menu_init_common
561 #include "common/menu.c"
562 #undef menu_init
563
564 // a bit of black magic here
565 static void draw_savestate_bg(int slot)
566 {
567         static const int psx_widths[8]  = { 256, 368, 320, 384, 512, 512, 640, 640 };
568         int x, y, w, h;
569         char fname[MAXPATHLEN];
570         GPUFreeze_t *gpu;
571         u16 *s, *d;
572         gzFile f;
573         int ret;
574         u32 tmp;
575
576         ret = get_state_filename(fname, sizeof(fname), slot);
577         if (ret != 0)
578                 return;
579
580         f = gzopen(fname, "rb");
581         if (f == NULL)
582                 return;
583
584         if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
585                 fprintf(stderr, "gzseek failed\n");
586                 gzclose(f);
587                 return;
588         }
589
590         gpu = malloc(sizeof(*gpu));
591         if (gpu == NULL) {
592                 gzclose(f);
593                 return;
594         }
595
596         ret = gzread(f, gpu, sizeof(*gpu));
597         gzclose(f);
598         if (ret != sizeof(*gpu)) {
599                 fprintf(stderr, "gzread failed\n");
600                 goto out;
601         }
602
603         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
604
605         if (gpu->ulStatus & 0x800000)
606                 goto out; // disabled
607
608         x = gpu->ulControl[5] & 0x3ff;
609         y = (gpu->ulControl[5] >> 10) & 0x1ff;
610         s = (u16 *)gpu->psxVRam + y * 1024 + x;
611         w = psx_widths[(gpu->ulStatus >> 16) & 7];
612         tmp = gpu->ulControl[7];
613         h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
614         if (gpu->ulStatus & 0x80000) // doubleheight
615                 h *= 2;
616
617         x = max(0, g_menuscreen_w - w) & ~3;
618         y = max(0, g_menuscreen_h / 2 - h / 2);
619         w = min(g_menuscreen_w, w);
620         h = min(g_menuscreen_h, h);
621         d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
622
623         for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
624                 if (gpu->ulStatus & 0x200000)
625                         bgr888_to_rgb565(d, s, w * 3);
626                 else
627                         bgr555_to_rgb565(d, s, w * 2);
628
629                 // darken this so that menu text is visible
630                 if (g_menuscreen_w - w < 320)
631                         menu_darken_bg(d, d, w * 2, 0);
632         }
633
634 out:
635         free(gpu);
636 }
637
638 // -------------- key config --------------
639
640 me_bind_action me_ctrl_actions[] =
641 {
642         { "UP      ", 1 << DKEY_UP},
643         { "DOWN    ", 1 << DKEY_DOWN },
644         { "LEFT    ", 1 << DKEY_LEFT },
645         { "RIGHT   ", 1 << DKEY_RIGHT },
646         { "TRIANGLE", 1 << DKEY_TRIANGLE },
647         { "CIRCLE  ", 1 << DKEY_CIRCLE },
648         { "CROSS   ", 1 << DKEY_CROSS },
649         { "SQUARE  ", 1 << DKEY_SQUARE },
650         { "L1      ", 1 << DKEY_L1 },
651         { "R1      ", 1 << DKEY_R1 },
652         { "L2      ", 1 << DKEY_L2 },
653         { "R2      ", 1 << DKEY_R2 },
654         { "L3      ", 1 << DKEY_L3 },
655         { "R3      ", 1 << DKEY_R3 },
656         { "START   ", 1 << DKEY_START },
657         { "SELECT  ", 1 << DKEY_SELECT },
658         { NULL,       0 }
659 };
660
661 me_bind_action emuctrl_actions[] =
662 {
663         { "Save State       ", 1 << SACTION_SAVE_STATE },
664         { "Load State       ", 1 << SACTION_LOAD_STATE },
665         { "Prev Save Slot   ", 1 << SACTION_PREV_SSLOT },
666         { "Next Save Slot   ", 1 << SACTION_NEXT_SSLOT },
667         { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
668         { "Take Screenshot  ", 1 << SACTION_SCREENSHOT },
669         { "Switch Renderer  ", 1 << SACTION_SWITCH_DISPMODE },
670         { "Enter Menu       ", 1 << SACTION_ENTER_MENU },
671 #ifdef __ARM_ARCH_7A__ /* XXX */
672         { "Minimize         ", 1 << SACTION_MINIMIZE },
673 #endif
674         { "Gun Trigger      ", 1 << SACTION_GUN_TRIGGER },
675         { "Gun A button     ", 1 << SACTION_GUN_A },
676         { "Gun B button     ", 1 << SACTION_GUN_B },
677         { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
678 #ifndef __ARM_ARCH_7A__ /* XXX */
679         { "Volume Up        ", 1 << SACTION_VOLUME_UP },
680         { "Volume Down      ", 1 << SACTION_VOLUME_DOWN },
681 #endif
682         { NULL,                0 }
683 };
684
685 static char *mystrip(char *str)
686 {
687         int i, len;
688
689         len = strlen(str);
690         for (i = 0; i < len; i++)
691                 if (str[i] != ' ') break;
692         if (i > 0) memmove(str, str + i, len - i + 1);
693
694         len = strlen(str);
695         for (i = len - 1; i >= 0; i--)
696                 if (str[i] != ' ' && str[i] != '\r' && str[i] != '\n') break;
697         str[i+1] = 0;
698
699         return str;
700 }
701
702 static void get_line(char *d, size_t size, const char *s)
703 {
704         const char *pe;
705         size_t len;
706
707         for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
708                 ;
709         len = pe - s;
710         if (len > size - 1)
711                 len = size - 1;
712         strncpy(d, s, len);
713         d[len] = 0;
714
715         mystrip(d);
716 }
717
718 static void keys_write_all(FILE *f)
719 {
720         int d;
721
722         for (d = 0; d < IN_MAX_DEVS; d++)
723         {
724                 const int *binds = in_get_dev_binds(d);
725                 const char *name = in_get_dev_name(d, 0, 0);
726                 int k, count = 0;
727
728                 if (binds == NULL || name == NULL)
729                         continue;
730
731                 fprintf(f, "binddev = %s\n", name);
732                 in_get_config(d, IN_CFG_BIND_COUNT, &count);
733
734                 for (k = 0; k < count; k++)
735                 {
736                         int i, kbinds, mask;
737                         char act[32];
738
739                         act[0] = act[31] = 0;
740                         name = in_get_key_name(d, k);
741
742                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
743                         for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
744                                 mask = me_ctrl_actions[i].mask;
745                                 if (mask & kbinds) {
746                                         strncpy(act, me_ctrl_actions[i].name, 31);
747                                         fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
748                                         kbinds &= ~mask;
749                                 }
750                                 mask = me_ctrl_actions[i].mask << 16;
751                                 if (mask & kbinds) {
752                                         strncpy(act, me_ctrl_actions[i].name, 31);
753                                         fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
754                                         kbinds &= ~mask;
755                                 }
756                         }
757
758                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
759                         for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
760                                 mask = emuctrl_actions[i].mask;
761                                 if (mask & kbinds) {
762                                         strncpy(act, emuctrl_actions[i].name, 31);
763                                         fprintf(f, "bind %s = %s\n", name, mystrip(act));
764                                         kbinds &= ~mask;
765                                 }
766                         }
767                 }
768
769                 for (k = 0; k < array_size(in_adev); k++)
770                 {
771                         if (in_adev[k] == d)
772                                 fprintf(f, "bind_analog = %d\n", k);
773                 }
774         }
775 }
776
777 static int parse_bind_val(const char *val, int *type)
778 {
779         int i;
780
781         *type = IN_BINDTYPE_NONE;
782         if (val[0] == 0)
783                 return 0;
784         
785         if (strncasecmp(val, "player", 6) == 0)
786         {
787                 int player, shift = 0;
788                 player = atoi(val + 6) - 1;
789
790                 if ((unsigned int)player > 1)
791                         return -1;
792                 if (player == 1)
793                         shift = 16;
794
795                 *type = IN_BINDTYPE_PLAYER12;
796                 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
797                         if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
798                                 return me_ctrl_actions[i].mask << shift;
799                 }
800         }
801         for (i = 0; emuctrl_actions[i].name != NULL; i++) {
802                 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
803                         *type = IN_BINDTYPE_EMU;
804                         return emuctrl_actions[i].mask;
805                 }
806         }
807
808         return -1;
809 }
810
811 static void keys_load_all(const char *cfg)
812 {
813         char dev[256], key[128], *act;
814         const char *p;
815         int bind, bindtype;
816         int ret, dev_id;
817
818         p = cfg;
819         while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
820                 p += 10;
821
822                 get_line(dev, sizeof(dev), p);
823                 dev_id = in_config_parse_dev(dev);
824                 if (dev_id < 0) {
825                         printf("input: can't handle dev: %s\n", dev);
826                         continue;
827                 }
828
829                 in_unbind_all(dev_id, -1, -1);
830                 while ((p = strstr(p, "bind"))) {
831                         if (strncmp(p, "binddev = ", 10) == 0)
832                                 break;
833
834                         if (strncmp(p, "bind_analog", 11) == 0) {
835                                 ret = sscanf(p, "bind_analog = %d", &bind);
836                                 p += 11;
837                                 if (ret != 1) {
838                                         printf("input: parse error: %16s..\n", p);
839                                         continue;
840                                 }
841                                 if ((unsigned int)bind >= array_size(in_adev)) {
842                                         printf("input: analog id %d out of range\n", bind);
843                                         continue;
844                                 }
845                                 in_adev[bind] = dev_id;
846                                 continue;
847                         }
848
849                         p += 4;
850                         if (*p != ' ') {
851                                 printf("input: parse error: %16s..\n", p);
852                                 continue;
853                         }
854
855                         get_line(key, sizeof(key), p);
856                         act = strchr(key, '=');
857                         if (act == NULL) {
858                                 printf("parse failed: %16s..\n", p);
859                                 continue;
860                         }
861                         *act = 0;
862                         act++;
863                         mystrip(key);
864                         mystrip(act);
865
866                         bind = parse_bind_val(act, &bindtype);
867                         if (bind != -1 && bind != 0) {
868                                 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
869                                 in_config_bind_key(dev_id, key, bind, bindtype);
870                         }
871                         else
872                                 lprintf("config: unhandled action \"%s\"\n", act);
873                 }
874         }
875         in_clean_binds();
876 }
877
878 static int key_config_loop_wrap(int id, int keys)
879 {
880         switch (id) {
881                 case MA_CTRL_PLAYER1:
882                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
883                         break;
884                 case MA_CTRL_PLAYER2:
885                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
886                         break;
887                 case MA_CTRL_EMU:
888                         key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
889                         break;
890                 default:
891                         break;
892         }
893         return 0;
894 }
895
896 static const char *adevnames[IN_MAX_DEVS + 2];
897 static int stick_sel[2];
898
899 static menu_entry e_menu_keyconfig_analog[] =
900 {
901         mee_enum ("Left stick (L3)",  0, stick_sel[0], adevnames),
902         mee_range("  X axis",    0, in_adev_axis[0][0], 0, 7),
903         mee_range("  Y axis",    0, in_adev_axis[0][1], 0, 7),
904         mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
905         mee_range("  X axis",    0, in_adev_axis[1][0], 0, 7),
906         mee_range("  Y axis",    0, in_adev_axis[1][1], 0, 7),
907         mee_end,
908 };
909
910 static int key_config_analog(int id, int keys)
911 {
912         int i, d, count, sel = 0;
913         int sel2dev_map[IN_MAX_DEVS];
914
915         memset(adevnames, 0, sizeof(adevnames));
916         memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
917         memset(stick_sel, 0, sizeof(stick_sel));
918
919         adevnames[0] = "None";
920         i = 1;
921         for (d = 0; d < IN_MAX_DEVS; d++)
922         {
923                 const char *name = in_get_dev_name(d, 0, 1);
924                 if (name == NULL)
925                         continue;
926
927                 count = 0;
928                 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
929                 if (count == 0)
930                         continue;
931
932                 if (in_adev[0] == d) stick_sel[0] = i;
933                 if (in_adev[1] == d) stick_sel[1] = i;
934                 sel2dev_map[i] = d;
935                 adevnames[i++] = name;
936         }
937         adevnames[i] = NULL;
938
939         me_loop(e_menu_keyconfig_analog, &sel);
940
941         in_adev[0] = sel2dev_map[stick_sel[0]];
942         in_adev[1] = sel2dev_map[stick_sel[1]];
943
944         return 0;
945 }
946
947 static const char *mgn_dev_name(int id, int *offs)
948 {
949         const char *name = NULL;
950         static int it = 0;
951
952         if (id == MA_CTRL_DEV_FIRST)
953                 it = 0;
954
955         for (; it < IN_MAX_DEVS; it++) {
956                 name = in_get_dev_name(it, 1, 1);
957                 if (name != NULL)
958                         break;
959         }
960
961         it++;
962         return name;
963 }
964
965 static const char *mgn_saveloadcfg(int id, int *offs)
966 {
967         return "";
968 }
969
970 static int mh_savecfg(int id, int keys)
971 {
972         if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
973                 me_update_msg("config saved");
974         else
975                 me_update_msg("failed to write config");
976
977         return 1;
978 }
979
980 static int mh_input_rescan(int id, int keys)
981 {
982         //menu_sync_config();
983         in_probe();
984         me_update_msg("rescan complete.");
985
986         return 0;
987 }
988
989 static const char *men_in_type_sel[] = {
990         "Standard (SCPH-1080)",
991         "Analog (SCPH-1150)",
992         "GunCon",
993         NULL
994 };
995 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
996 static const char h_notsgun[]  = "Don't trigger (shoot) when touching screen in gun games.";
997 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
998
999 static menu_entry e_menu_keyconfig[] =
1000 {
1001         mee_handler_id("Player 1",              MA_CTRL_PLAYER1,    key_config_loop_wrap),
1002         mee_handler_id("Player 2",              MA_CTRL_PLAYER2,    key_config_loop_wrap),
1003         mee_handler_id("Analog controls",       MA_CTRL_ANALOG,     key_config_analog),
1004         mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU,        key_config_loop_wrap),
1005         mee_label     (""),
1006         mee_enum      ("Port 1 device",     0, in_type_sel1,    men_in_type_sel),
1007         mee_enum      ("Port 2 device",     0, in_type_sel2,    men_in_type_sel),
1008         mee_onoff_h   ("Nubs as buttons",   MA_CTRL_NUBS_BTNS,  in_evdev_allow_abs_only, 1, h_nub_btns),
1009         mee_onoff_h   ("Vibration",         MA_CTRL_VIBRATION,  in_enable_vibration, 1, h_vibration),
1010         mee_range     ("Analog deadzone",   MA_CTRL_DEADZONE,   analog_deadzone, 1, 99),
1011         mee_onoff_h   ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1012         mee_cust_nosave("Save global config",       MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1013         mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1014         mee_handler   ("Rescan devices:",  mh_input_rescan),
1015         mee_label     (""),
1016         mee_label_mk  (MA_CTRL_DEV_FIRST, mgn_dev_name),
1017         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1018         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1019         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1020         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1021         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1022         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1023         mee_end,
1024 };
1025
1026 static int menu_loop_keyconfig(int id, int keys)
1027 {
1028         static int sel = 0;
1029
1030 //      me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1031         me_loop(e_menu_keyconfig, &sel);
1032         return 0;
1033 }
1034
1035 // ------------ gfx options menu ------------
1036
1037 static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
1038 static const char *men_soft_filter[] = { "None",
1039 #ifdef __ARM_NEON__
1040         "scale2x", "eagle2x",
1041 #endif
1042         NULL };
1043 static const char *men_dummy[] = { NULL };
1044 static const char h_cscaler[]   = "Displays the scaler layer, you can resize it\n"
1045                                   "using d-pad or move it using R+d-pad";
1046 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1047
1048 static int menu_loop_cscaler(int id, int keys)
1049 {
1050         unsigned int inp;
1051
1052         g_scaler = SCALE_CUSTOM;
1053
1054         plat_gvideo_open(Config.PsxType);
1055
1056         for (;;)
1057         {
1058                 menu_draw_begin(0);
1059                 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1060                 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1061                 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1062                 menu_draw_end();
1063
1064                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1065                                 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1066                 if (inp & PBTN_UP)    g_layer_y--;
1067                 if (inp & PBTN_DOWN)  g_layer_y++;
1068                 if (inp & PBTN_LEFT)  g_layer_x--;
1069                 if (inp & PBTN_RIGHT) g_layer_x++;
1070                 if (!(inp & PBTN_R)) {
1071                         if (inp & PBTN_UP)    g_layer_h += 2;
1072                         if (inp & PBTN_DOWN)  g_layer_h -= 2;
1073                         if (inp & PBTN_LEFT)  g_layer_w += 2;
1074                         if (inp & PBTN_RIGHT) g_layer_w -= 2;
1075                 }
1076                 if (inp & (PBTN_MOK|PBTN_MBACK))
1077                         break;
1078
1079                 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1080                         if (g_layer_x < 0)   g_layer_x = 0;
1081                         if (g_layer_x > 640) g_layer_x = 640;
1082                         if (g_layer_y < 0)   g_layer_y = 0;
1083                         if (g_layer_y > 420) g_layer_y = 420;
1084                         if (g_layer_w < 160) g_layer_w = 160;
1085                         if (g_layer_h < 60)  g_layer_h = 60;
1086                         if (g_layer_x + g_layer_w > 800)
1087                                 g_layer_w = 800 - g_layer_x;
1088                         if (g_layer_y + g_layer_h > 480)
1089                                 g_layer_h = 480 - g_layer_y;
1090                         // resize the layer
1091                         plat_gvideo_open(Config.PsxType);
1092                 }
1093         }
1094
1095         plat_gvideo_close();
1096
1097         return 0;
1098 }
1099
1100 static menu_entry e_menu_gfx_options[] =
1101 {
1102         mee_enum      ("Scaler",                   MA_OPT_SCALER, g_scaler, men_scaler),
1103         mee_onoff     ("Software Scaling",         MA_OPT_SCALER2, soft_scaling, 1),
1104         mee_enum      ("Filter",                   MA_OPT_FILTERING, filter, men_dummy),
1105         mee_enum_h    ("Software Filter",          MA_OPT_FILTERING2, soft_filter, men_soft_filter, h_soft_filter),
1106 //      mee_onoff     ("Vsync",                    0, vsync, 1),
1107         mee_cust_h    ("Setup custom scaler",      MA_OPT_SCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1108         mee_end,
1109 };
1110
1111 static int menu_loop_gfx_options(int id, int keys)
1112 {
1113         static int sel = 0;
1114
1115         me_loop(e_menu_gfx_options, &sel);
1116
1117         return 0;
1118 }
1119
1120 // XXX
1121 void menu_set_filter_list(void *filters)
1122 {
1123         int i;
1124
1125         i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
1126         e_menu_gfx_options[i].data = filters;
1127         me_enable(e_menu_gfx_options, MA_OPT_FILTERING, filters != NULL);
1128 }
1129
1130 // ------------ bios/plugins ------------
1131
1132 #ifdef __ARM_NEON__
1133
1134 static const char h_gpu_neon[] =
1135         "Configure built-in NEON GPU plugin";
1136 static const char h_gpu_neon_enhanced[] =
1137         "Renders in double resolution at the cost of lower performance\n"
1138         "(not available for high resolution games)";
1139 static const char h_gpu_neon_enhanced_hack[] =
1140         "Speed hack for above option (glitches some games)";
1141 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1142
1143 static menu_entry e_menu_plugin_gpu_neon[] =
1144 {
1145         mee_enum      ("Enable interlace mode",      0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1146         mee_onoff_h   ("Enhanced resolution (slow)", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1147         mee_onoff_h   ("Enhanced res. speed hack",   0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1148         mee_end,
1149 };
1150
1151 static int menu_loop_plugin_gpu_neon(int id, int keys)
1152 {
1153         static int sel = 0;
1154         me_loop(e_menu_plugin_gpu_neon, &sel);
1155         return 0;
1156 }
1157
1158 #endif
1159
1160 static menu_entry e_menu_plugin_gpu_unai[] =
1161 {
1162         mee_onoff     ("Skip every 2nd line",        0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1163         mee_onoff     ("Abe's Odyssey hack",         0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1164         mee_onoff     ("Disable lighting",           0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1165         mee_onoff     ("Disable blending",           0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1166         mee_end,
1167 };
1168
1169 static int menu_loop_plugin_gpu_unai(int id, int keys)
1170 {
1171         int sel = 0;
1172         me_loop(e_menu_plugin_gpu_unai, &sel);
1173         return 0;
1174 }
1175
1176 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1177 //static const char h_gpu_0[]            = "Needed for Chrono Cross";
1178 static const char h_gpu_1[]            = "Capcom fighting games";
1179 static const char h_gpu_2[]            = "Black screens in Lunar";
1180 static const char h_gpu_3[]            = "Compatibility mode";
1181 static const char h_gpu_6[]            = "Pandemonium 2";
1182 //static const char h_gpu_7[]            = "Skip every second frame";
1183 static const char h_gpu_8[]            = "Needed by Dark Forces";
1184 static const char h_gpu_9[]            = "better g-colors, worse textures";
1185 static const char h_gpu_10[]           = "Toggle busy flags after drawing";
1186
1187 static menu_entry e_menu_plugin_gpu_peops[] =
1188 {
1189         mee_enum      ("Dithering",                  0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1190 //      mee_onoff_h   ("Odd/even bit hack",          0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1191         mee_onoff_h   ("Expand screen width",        0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1192         mee_onoff_h   ("Ignore brightness color",    0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1193         mee_onoff_h   ("Disable coordinate check",   0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1194         mee_onoff_h   ("Lazy screen update",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1195 //      mee_onoff_h   ("Old frame skipping",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1196         mee_onoff_h   ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1197         mee_onoff_h   ("Draw quads with triangles",  0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1198         mee_onoff_h   ("Fake 'gpu busy' states",     0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1199         mee_end,
1200 };
1201
1202 static int menu_loop_plugin_gpu_peops(int id, int keys)
1203 {
1204         static int sel = 0;
1205         me_loop(e_menu_plugin_gpu_peops, &sel);
1206         return 0;
1207 }
1208
1209 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1210         "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1211 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1212
1213 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1214 {
1215         mee_onoff     ("Dithering",                  0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1216         mee_enum      ("Texture Filtering",          0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1217         mee_enum      ("Framebuffer Textures",       0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1218         mee_onoff     ("Mask Detect",                0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1219         mee_onoff     ("Opaque Pass",                0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1220         mee_onoff     ("Advanced Blend",             0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1221         mee_onoff     ("Use Fast Mdec",              0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1222         mee_range     ("Texture RAM size (MB)",      0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1223         mee_onoff     ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1224         mee_label     ("Fixes/hacks:"),
1225         mee_onoff     ("FF7 cursor",                 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1226         mee_onoff     ("Direct FB updates",          0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1227         mee_onoff     ("Black brightness",           0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1228         mee_onoff     ("Swap front detection",       0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1229         mee_onoff     ("Disable coord check",        0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1230         mee_onoff     ("No blue glitches (LoD)",     0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1231         mee_onoff     ("Soft FB access",             0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1232         mee_onoff     ("FF9 rect",                   0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1233         mee_onoff     ("No subtr. blending",         0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1234         mee_onoff     ("Lazy upload (DW7)",          0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1235         mee_onoff     ("Additional uploads",         0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1236         mee_end,
1237 };
1238
1239 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1240 {
1241         static int sel = 0;
1242         me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1243         return 0;
1244 }
1245
1246 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1247 static const char h_spu_volboost[]  = "Large values cause distortion";
1248
1249 static menu_entry e_menu_plugin_spu[] =
1250 {
1251         mee_range_h   ("Volume boost",              0, volume_boost, -5, 30, h_spu_volboost),
1252         mee_onoff     ("Reverb",                    0, iUseReverb, 2),
1253         mee_enum      ("Interpolation",             0, iUseInterpolation, men_spu_interp),
1254         mee_onoff     ("Adjust XA pitch",           0, iXAPitch, 1),
1255         mee_end,
1256 };
1257
1258 static int menu_loop_plugin_spu(int id, int keys)
1259 {
1260         static int sel = 0;
1261         me_loop(e_menu_plugin_spu, &sel);
1262         return 0;
1263 }
1264
1265 static const char h_bios[]       = "HLE is simulated BIOS. BIOS selection is saved in\n"
1266                                    "savestates and can't be changed there. Must save\n"
1267                                    "config and reload the game for change to take effect";
1268 static const char h_plugin_gpu[] = 
1269 #ifdef __ARM_NEON__
1270                                    "builtin_gpu is the NEON GPU, very fast and accurate\n"
1271 #endif
1272                                    "gpu_peops is Pete's soft GPU, slow but accurate\n"
1273                                    "gpu_unai is GPU from PCSX4ALL, fast but glitchy\n"
1274                                    "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1275                                    "must save config and reload the game if changed";
1276 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1277                                    "must save config and reload the game if changed";
1278 static const char h_gpu_peops[]  = "Configure P.E.Op.S. SoftGL Driver V1.17";
1279 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1280 static const char h_gpu_unai[]   = "Configure Unai/PCSX4ALL Team GPU plugin";
1281 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1282
1283 static menu_entry e_menu_plugin_options[] =
1284 {
1285         mee_enum_h    ("BIOS",                          0, bios_sel, bioses, h_bios),
1286         mee_enum_h    ("GPU plugin",                    0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1287         mee_enum_h    ("SPU plugin",                    0, spu_plugsel, spu_plugins, h_plugin_spu),
1288 #ifdef __ARM_NEON__
1289         mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1290 #endif
1291         mee_handler_h ("Configure gpu_peops plugin",    menu_loop_plugin_gpu_peops, h_gpu_peops),
1292         mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1293         mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1294         mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1295         mee_end,
1296 };
1297
1298 static menu_entry e_menu_main2[];
1299
1300 static int menu_loop_plugin_options(int id, int keys)
1301 {
1302         static int sel = 0;
1303         me_loop(e_menu_plugin_options, &sel);
1304
1305         // sync BIOS/plugins
1306         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1307         snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1308         snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1309         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1310
1311         return 0;
1312 }
1313
1314 // ------------ adv options menu ------------
1315
1316 static const char h_cfg_psxclk[]  = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1317                                     "(lower value - less work for the emu, may be faster)";
1318 static const char h_cfg_nosmc[]   = "Will cause crashes when loading, break memcards";
1319 static const char h_cfg_gteunn[]  = "May cause graphical glitches";
1320 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1321
1322 static menu_entry e_menu_speed_hacks[] =
1323 {
1324         mee_range_h   ("PSX CPU clock, %%",        0, psx_clock, 1, 500, h_cfg_psxclk),
1325         mee_onoff_h   ("Disable SMC checks",       0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1326         mee_onoff_h   ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1327         mee_onoff_h   ("Disable GTE flags",        0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1328         mee_end,
1329 };
1330
1331 static int menu_loop_speed_hacks(int id, int keys)
1332 {
1333         static int sel = 0;
1334         me_loop(e_menu_speed_hacks, &sel);
1335         return 0;
1336 }
1337
1338 static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
1339 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
1340 static const char h_cfg_spu[]    = "Shows active SPU channels\n"
1341                                    "(green: normal, red: fmod, blue: noise)";
1342 static const char h_cfg_fl[]     = "Frame Limiter keeps the game from running too fast";
1343 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
1344 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
1345                                    "(proper .cue/.bin dump is needed otherwise)";
1346 static const char h_cfg_sio[]    = "You should not need this, breaks games";
1347 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1348 static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1349                                    "(timing hack, breaks other games)";
1350 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix\n"
1351                                    "(timing hack, breaks other games)";
1352 static const char h_cfg_cdrr[]   = "Compatibility tweak (CD timing hack, breaks FMVs)";
1353 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
1354                                    "Might be useful to overcome some dynarec bugs";
1355 static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
1356                                    "must reload game for any change to take effect";
1357
1358 static menu_entry e_menu_adv_options[] =
1359 {
1360         mee_onoff_h   ("Show CPU load",          0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1361         mee_onoff_h   ("Show SPU channels",      0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1362         mee_onoff_h   ("Disable Frame Limiter",  0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1363         mee_onoff_h   ("Disable XA Decoding",    0, Config.Xa, 1, h_cfg_xa),
1364         mee_onoff_h   ("Disable CD Audio",       0, Config.Cdda, 1, h_cfg_cdda),
1365         mee_onoff_h   ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1366         mee_onoff_h   ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1367         //mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
1368         mee_onoff_h   ("Rootcounter hack 2",     0, Config.VSyncWA, 1, h_cfg_rcnt2),
1369         mee_enum_h    ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
1370         mee_onoff_h   ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1371         mee_handler_h ("[Speed hacks]",             menu_loop_speed_hacks, h_cfg_shacks),
1372         mee_end,
1373 };
1374
1375 static int menu_loop_adv_options(int id, int keys)
1376 {
1377         static int sel = 0;
1378         me_loop(e_menu_adv_options, &sel);
1379         return 0;
1380 }
1381
1382 // ------------ options menu ------------
1383
1384 static int mh_restore_defaults(int id, int keys)
1385 {
1386         menu_set_defconfig();
1387         me_update_msg("defaults restored");
1388         return 1;
1389 }
1390
1391 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
1392 static const char *men_frameskip[]    = { "Auto", "Off", "1", "2", "3", NULL };
1393 /*
1394 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1395 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
1396                                         "loading state or both";
1397 */
1398 static const char h_restore_def[]     = "Switches back to default / recommended\n"
1399                                         "configuration";
1400 static const char h_frameskip[]       = "Warning: frameskip sometimes causes glitches\n";
1401
1402 static menu_entry e_menu_options[] =
1403 {
1404 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1405 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1406         mee_enum_h    ("Frameskip",                0, frameskip, men_frameskip, h_frameskip),
1407         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1408         mee_enum      ("Region",                   0, region, men_region),
1409         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1410         mee_handler_id("[Display]",                MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1411         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1412         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1413         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1414         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1415         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1416         mee_end,
1417 };
1418
1419 static int menu_loop_options(int id, int keys)
1420 {
1421         static int sel = 0;
1422         int i;
1423
1424         i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1425         e_menu_options[i].enabled = cpu_clock_st > 0 ? 1 : 0;
1426         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1427
1428         me_loop(e_menu_options, &sel);
1429
1430         return 0;
1431 }
1432
1433 // ------------ debug menu ------------
1434
1435 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1436 {
1437         int w = min(g_menuscreen_w, 1024);
1438         int h = min(g_menuscreen_h, 512);
1439         u16 *d = g_menuscreen_ptr;
1440         u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1441         char buff[64];
1442         int ty = 1;
1443
1444         gpuf->ulFreezeVersion = 1;
1445         if (GPU_freeze != NULL)
1446                 GPU_freeze(1, gpuf);
1447
1448         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1449                 bgr555_to_rgb565(d, s, w * 2);
1450
1451         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1452         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1453         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1454         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1455         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1456 }
1457
1458 static void debug_menu_loop(void)
1459 {
1460         int inp, df_x = 0, df_y = 0;
1461         GPUFreeze_t *gpuf;
1462
1463         gpuf = malloc(sizeof(*gpuf));
1464         if (gpuf == NULL)
1465                 return;
1466
1467         while (1)
1468         {
1469                 menu_draw_begin(0);
1470                 draw_frame_debug(gpuf, df_x, df_y);
1471                 menu_draw_end();
1472
1473                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1474                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1475                 if      (inp & PBTN_MBACK) break;
1476                 else if (inp & PBTN_UP)    { if (df_y > 0) df_y--; }
1477                 else if (inp & PBTN_DOWN)  { if (df_y < 512 - g_menuscreen_h) df_y++; }
1478                 else if (inp & PBTN_LEFT)  { if (df_x > 0) df_x -= 2; }
1479                 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1480         }
1481
1482         free(gpuf);
1483 }
1484
1485 // --------- memcard manager ---------
1486
1487 static void draw_mc_icon(int dx, int dy, const u16 *s)
1488 {
1489         u16 *d;
1490         int x, y, l, p;
1491         
1492         d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1493
1494         for (y = 0; y < 16; y++, s += 16) {
1495                 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1496                         for (x = 0; x < 16; x++) {
1497                                 p = s[x];
1498                                 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1499                                         | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1500                         }
1501                 }
1502         }
1503 }
1504
1505 static void draw_mc_bg(void)
1506 {
1507         McdBlock *blocks1, *blocks2;
1508         int maxicons = 15;
1509         int i, y, row2;
1510
1511         blocks1 = malloc(15 * sizeof(blocks1[0]));
1512         blocks2 = malloc(15 * sizeof(blocks1[0]));
1513         if (blocks1 == NULL || blocks2 == NULL)
1514                 goto out;
1515
1516         for (i = 0; i < 15; i++) {
1517                 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1518                 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1519         }
1520
1521         menu_draw_begin(1);
1522
1523         memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1524
1525         y = g_menuscreen_h / 2 - 15 * 32 / 2;
1526         if (y < 0) {
1527                 // doesn't fit..
1528                 y = 0;
1529                 maxicons = g_menuscreen_h / 32;
1530         }
1531
1532         row2 = g_menuscreen_w / 2;
1533         for (i = 0; i < maxicons; i++) {
1534                 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1535                 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1536
1537                 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1538                 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1539         }
1540
1541         menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1542
1543         menu_draw_end();
1544 out:
1545         free(blocks1);
1546         free(blocks2);
1547 }
1548
1549 static void handle_memcard_sel(void)
1550 {
1551         Config.Mcd1[0] = 0;
1552         if (memcard1_sel != 0)
1553                 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1554         Config.Mcd2[0] = 0;
1555         if (memcard2_sel != 0)
1556                 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1557         LoadMcds(Config.Mcd1, Config.Mcd2);
1558         draw_mc_bg();
1559 }
1560
1561 static menu_entry e_memcard_options[] =
1562 {
1563         mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1564         mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1565         mee_end,
1566 };
1567
1568 static int menu_loop_memcards(int id, int keys)
1569 {
1570         static int sel = 0;
1571         char *p;
1572         int i;
1573
1574         memcard1_sel = memcard2_sel = 0;
1575         p = strrchr(Config.Mcd1, '/');
1576         if (p != NULL)
1577                 for (i = 0; memcards[i] != NULL; i++)
1578                         if (strcmp(p + 1, memcards[i]) == 0)
1579                                 { memcard1_sel = i; break; }
1580         p = strrchr(Config.Mcd2, '/');
1581         if (p != NULL)
1582                 for (i = 0; memcards[i] != NULL; i++)
1583                         if (strcmp(p + 1, memcards[i]) == 0)
1584                                 { memcard2_sel = i; break; }
1585
1586         me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1587
1588         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1589
1590         return 0;
1591 }
1592
1593 // ------------ cheats menu ------------
1594
1595 static void draw_cheatlist(int sel)
1596 {
1597         int max_cnt, start, i, pos, active;
1598
1599         max_cnt = g_menuscreen_h / me_sfont_h;
1600         start = max_cnt / 2 - sel;
1601
1602         menu_draw_begin(1);
1603
1604         for (i = 0; i < NumCheats; i++) {
1605                 pos = start + i;
1606                 if (pos < 0) continue;
1607                 if (pos >= max_cnt) break;
1608                 active = Cheats[i].Enabled;
1609                 smalltext_out16(14,                pos * me_sfont_h,
1610                         active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1611                 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1612                         Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1613         }
1614         pos = start + i;
1615         if (pos < max_cnt)
1616                 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1617
1618         text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1619         menu_draw_end();
1620 }
1621
1622 static void menu_loop_cheats(void)
1623 {
1624         static int menu_sel = 0;
1625         int inp;
1626
1627         for (;;)
1628         {
1629                 draw_cheatlist(menu_sel);
1630                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1631                                 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1632                 if (inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1633                 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1634                 if (inp &(PBTN_LEFT|PBTN_L))  { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1635                 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1636                 if (inp & PBTN_MOK) { // action
1637                         if (menu_sel < NumCheats)
1638                                 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1639                         else    break;
1640                 }
1641                 if (inp & PBTN_MBACK)
1642                         break;
1643         }
1644 }
1645
1646 // --------- main menu help ----------
1647
1648 static void menu_bios_warn(void)
1649 {
1650         int inp;
1651         static const char msg[] =
1652                 "You don't seem to have copied any BIOS\n"
1653                 "files to\n"
1654 #ifdef __ARM_ARCH_7A__ // XXX
1655                 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1656 #else
1657                 "pcsx_rearmed/bios/\n\n"
1658 #endif
1659                 "While many games work fine with fake\n"
1660                 "(HLE) BIOS, others (like MGS and FF8)\n"
1661                 "require BIOS to work.\n"
1662                 "After copying the file, you'll also need\n"
1663                 "to select it in the emu's menu:\n"
1664                 "options->[BIOS/Plugins]\n\n"
1665                 "The file is usually named SCPH1001.BIN,\n"
1666                 "but other not compressed files can be\n"
1667                 "used too.\n\n"
1668                 "Press %s or %s to continue";
1669         char tmp_msg[sizeof(msg) + 64];
1670
1671         snprintf(tmp_msg, sizeof(tmp_msg), msg,
1672                 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1673         while (1)
1674         {
1675                 draw_menu_message(tmp_msg, NULL);
1676
1677                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
1678                 if (inp & (PBTN_MBACK|PBTN_MOK))
1679                         return;
1680         }
1681 }
1682
1683 // ------------ main menu ------------
1684
1685 static menu_entry e_menu_main[];
1686 void OnFile_Exit();
1687
1688 static void draw_frame_main(void)
1689 {
1690         struct tm *tmp;
1691         time_t ltime;
1692         int capacity;
1693         char ltime_s[16];
1694         char buff[64];
1695         char *out;
1696
1697         if (CdromId[0] != 0) {
1698                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1699                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1700                          Config.HLE ? "HLE" : "BIOS");
1701                 smalltext_out16(4, 1, buff, 0x105f);
1702         }
1703
1704         if (ready_to_go) {
1705                 capacity = plat_get_bat_capacity();
1706                 ltime = time(NULL);
1707                 tmp = localtime(&ltime);
1708                 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1709                 if (capacity >= 0) {
1710                         snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1711                         out = buff;
1712                 }
1713                 else
1714                         out = ltime_s;
1715                 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1716         }
1717 }
1718
1719 static void draw_frame_credits(void)
1720 {
1721         smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1722 }
1723
1724 static const char credits_text[] = 
1725         "PCSX-ReARMed\n\n"
1726         "(C) 1999-2003 PCSX Team\n"
1727         "(C) 2005-2009 PCSX-df Team\n"
1728         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1729         "ARM recompiler (C) 2009-2011 Ari64\n"
1730 #ifdef __ARM_NEON__
1731         "ARM NEON GPU (c) 2011-2012 Exophase\n"
1732 #endif
1733         "PEOpS GPU and SPU by Pete Bernert\n"
1734         "  and the P.E.Op.S. team\n"
1735         "PCSX4ALL plugin by PCSX4ALL team\n"
1736         "  Chui, Franxis, Unai\n\n"
1737         "integration, optimization and\n"
1738         "  frontend (C) 2010-2012 notaz\n";
1739
1740 static int reset_game(void)
1741 {
1742         // sanity check
1743         if (bios_sel == 0 && !Config.HLE)
1744                 return -1;
1745
1746         ClosePlugins();
1747         OpenPlugins();
1748         SysReset();
1749         if (CheckCdrom() != -1) {
1750                 LoadCdrom();
1751         }
1752         return 0;
1753 }
1754
1755 static int reload_plugins(const char *cdimg)
1756 {
1757         pl_vout_buf = NULL;
1758
1759         ClosePlugins();
1760
1761         set_cd_image(cdimg);
1762         LoadPlugins();
1763         pcnt_hook_plugins();
1764         NetOpened = 0;
1765         if (OpenPlugins() == -1) {
1766                 me_update_msg("failed to open plugins");
1767                 return -1;
1768         }
1769         plugin_call_rearmed_cbs();
1770
1771         cdrIsoMultidiskCount = 1;
1772         CdromId[0] = '\0';
1773         CdromLabel[0] = '\0';
1774
1775         return 0;
1776 }
1777
1778 static int run_bios(void)
1779 {
1780         if (bios_sel == 0)
1781                 return -1;
1782
1783         ready_to_go = 0;
1784         if (reload_plugins(NULL) != 0)
1785                 return -1;
1786         SysReset();
1787
1788         ready_to_go = 1;
1789         return 0;
1790 }
1791
1792 static int run_exe(void)
1793 {
1794         const char *fname;
1795
1796         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1797         if (fname == NULL)
1798                 return -1;
1799
1800         ready_to_go = 0;
1801         if (reload_plugins(NULL) != 0)
1802                 return -1;
1803
1804         SysReset();
1805         if (Load(fname) != 0) {
1806                 me_update_msg("exe load failed, bad file?");
1807                 printf("meh\n");
1808                 return -1;
1809         }
1810
1811         ready_to_go = 1;
1812         return 0;
1813 }
1814
1815 static int run_cd_image(const char *fname)
1816 {
1817         ready_to_go = 0;
1818         reload_plugins(fname);
1819
1820         // always autodetect, menu_sync_config will override as needed
1821         Config.PsxAuto = 1;
1822
1823         if (CheckCdrom() == -1) {
1824                 // Only check the CD if we are starting the console with a CD
1825                 ClosePlugins();
1826                 me_update_msg("unsupported/invalid CD image");
1827                 return -1;
1828         }
1829
1830         SysReset();
1831
1832         // Read main executable directly from CDRom and start it
1833         if (LoadCdrom() == -1) {
1834                 ClosePlugins();
1835                 me_update_msg("failed to load CD image");
1836                 return -1;
1837         }
1838
1839         emu_on_new_cd();
1840         ready_to_go = 1;
1841
1842         return 0;
1843 }
1844
1845 static int romsel_run(void)
1846 {
1847         int prev_gpu, prev_spu;
1848         const char *fname;
1849
1850         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1851         if (fname == NULL)
1852                 return -1;
1853
1854         printf("selected file: %s\n", fname);
1855
1856         new_dynarec_clear_full();
1857
1858         if (run_cd_image(fname) != 0)
1859                 return -1;
1860
1861         prev_gpu = gpu_plugsel;
1862         prev_spu = spu_plugsel;
1863         if (menu_load_config(1) != 0)
1864                 menu_load_config(0);
1865
1866         // check for plugin changes, have to repeat
1867         // loading if game config changed plugins to reload them
1868         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1869                 printf("plugin change detected, reloading plugins..\n");
1870                 if (run_cd_image(fname) != 0)
1871                         return -1;
1872         }
1873
1874         strcpy(last_selected_fname, rom_fname_reload);
1875         menu_do_last_cd_img(0);
1876         return 0;
1877 }
1878
1879 static int swap_cd_image(void)
1880 {
1881         char *fname;
1882
1883         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1884         if (fname == NULL)
1885                 return -1;
1886
1887         printf("selected file: %s\n", fname);
1888
1889         CdromId[0] = '\0';
1890         CdromLabel[0] = '\0';
1891
1892         set_cd_image(fname);
1893         if (ReloadCdromPlugin() < 0) {
1894                 me_update_msg("failed to load cdr plugin");
1895                 return -1;
1896         }
1897         if (CDR_open() < 0) {
1898                 me_update_msg("failed to open cdr plugin");
1899                 return -1;
1900         }
1901
1902         SetCdOpenCaseTime(time(NULL) + 2);
1903         LidInterrupt();
1904
1905         strcpy(last_selected_fname, rom_fname_reload);
1906         return 0;
1907 }
1908
1909 static int swap_cd_multidisk(void)
1910 {
1911         cdrIsoMultidiskSelect++;
1912         CdromId[0] = '\0';
1913         CdromLabel[0] = '\0';
1914
1915         CDR_close();
1916         if (CDR_open() < 0) {
1917                 me_update_msg("failed to open cdr plugin");
1918                 return -1;
1919         }
1920
1921         SetCdOpenCaseTime(time(NULL) + 2);
1922         LidInterrupt();
1923
1924         return 0;
1925 }
1926
1927 static void load_pcsx_cht(void)
1928 {
1929         char path[256];
1930         char *fname;
1931
1932         path[0] = 0;
1933         fname = menu_loop_romsel(path, sizeof(path));
1934         if (fname == NULL)
1935                 return;
1936
1937         printf("selected cheat file: %s\n", fname);
1938         LoadCheats(fname);
1939
1940         if (NumCheats == 0 && NumCodes == 0)
1941                 me_update_msg("failed to load cheats");
1942         else {
1943                 snprintf(path, sizeof(path), "%d cheat(s) loaded", NumCheats + NumCodes);
1944                 me_update_msg(path);
1945         }
1946         me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
1947 }
1948
1949 static int main_menu_handler(int id, int keys)
1950 {
1951         switch (id)
1952         {
1953         case MA_MAIN_RESUME_GAME:
1954                 if (ready_to_go)
1955                         return 1;
1956                 break;
1957         case MA_MAIN_SAVE_STATE:
1958                 if (ready_to_go)
1959                         return menu_loop_savestate(0);
1960                 break;
1961         case MA_MAIN_LOAD_STATE:
1962                 if (ready_to_go)
1963                         return menu_loop_savestate(1);
1964                 break;
1965         case MA_MAIN_RESET_GAME:
1966                 if (ready_to_go && reset_game() == 0)
1967                         return 1;
1968                 break;
1969         case MA_MAIN_LOAD_ROM:
1970                 if (romsel_run() == 0)
1971                         return 1;
1972                 break;
1973         case MA_MAIN_SWAP_CD:
1974                 if (swap_cd_image() == 0)
1975                         return 1;
1976                 break;
1977         case MA_MAIN_SWAP_CD_MULTI:
1978                 if (swap_cd_multidisk() == 0)
1979                         return 1;
1980                 break;
1981         case MA_MAIN_RUN_BIOS:
1982                 if (run_bios() == 0)
1983                         return 1;
1984                 break;
1985         case MA_MAIN_RUN_EXE:
1986                 if (run_exe() == 0)
1987                         return 1;
1988                 break;
1989         case MA_MAIN_CHEATS:
1990                 menu_loop_cheats();
1991                 break;
1992         case MA_MAIN_LOAD_CHEATS:
1993                 load_pcsx_cht();
1994                 break;
1995         case MA_MAIN_CREDITS:
1996                 draw_menu_message(credits_text, draw_frame_credits);
1997                 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
1998                 break;
1999         case MA_MAIN_EXIT:
2000                 OnFile_Exit();
2001                 break;
2002         default:
2003                 lprintf("%s: something unknown selected\n", __FUNCTION__);
2004                 break;
2005         }
2006
2007         return 0;
2008 }
2009
2010 static menu_entry e_menu_main2[] =
2011 {
2012         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
2013         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2014         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
2015         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
2016         mee_handler   ("Memcard manager",    menu_loop_memcards),
2017         mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS,   main_menu_handler),
2018         mee_end,
2019 };
2020
2021 static int main_menu2_handler(int id, int keys)
2022 {
2023         static int sel = 0;
2024
2025         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
2026         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2027         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2028         me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2029
2030         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2031 }
2032
2033 static const char h_extra[] = "Change CD, manage memcards..\n";
2034
2035 static menu_entry e_menu_main[] =
2036 {
2037         mee_label     (""),
2038         mee_label     (""),
2039         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
2040         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
2041         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
2042         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
2043         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
2044         mee_handler   ("Options",            menu_loop_options),
2045         mee_handler   ("Controls",           menu_loop_keyconfig),
2046         mee_handler_id("Cheats",             MA_MAIN_CHEATS,      main_menu_handler),
2047         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
2048         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
2049         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
2050         mee_end,
2051 };
2052
2053 // ----------------------------
2054
2055 static void menu_leave_emu(void);
2056
2057 void menu_loop(void)
2058 {
2059         static int sel = 0;
2060
2061         menu_leave_emu();
2062
2063         if (bioses[1] == NULL && !warned_about_bios) {
2064                 menu_bios_warn();
2065                 warned_about_bios = 1;
2066         }
2067
2068         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2069         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
2070         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
2071         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
2072         me_enable(e_menu_main, MA_MAIN_CHEATS,      ready_to_go && NumCheats);
2073
2074         in_set_config_int(0, IN_CFG_BLOCKING, 1);
2075
2076         do {
2077                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2078         } while (!ready_to_go);
2079
2080         /* wait until menu, ok, back is released */
2081         while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2082                 ;
2083
2084         in_set_config_int(0, IN_CFG_BLOCKING, 0);
2085
2086         menu_prepare_emu();
2087 }
2088
2089 static int qsort_strcmp(const void *p1, const void *p2)
2090 {
2091         char * const *s1 = (char * const *)p1;
2092         char * const *s2 = (char * const *)p2;
2093         return strcasecmp(*s1, *s2);
2094 }
2095
2096 static void scan_bios_plugins(void)
2097 {
2098         char fname[MAXPATHLEN];
2099         struct dirent *ent;
2100         int bios_i, gpu_i, spu_i, mc_i;
2101         char *p;
2102         DIR *dir;
2103
2104         bioses[0] = "HLE";
2105         gpu_plugins[0] = "builtin_gpu";
2106         spu_plugins[0] = "builtin_spu";
2107         memcards[0] = "(none)";
2108         bios_i = gpu_i = spu_i = mc_i = 1;
2109
2110         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2111         dir = opendir(fname);
2112         if (dir == NULL) {
2113                 perror("scan_bios_plugins bios opendir");
2114                 goto do_plugins;
2115         }
2116
2117         while (1) {
2118                 struct stat st;
2119
2120                 errno = 0;
2121                 ent = readdir(dir);
2122                 if (ent == NULL) {
2123                         if (errno != 0)
2124                                 perror("readdir");
2125                         break;
2126                 }
2127
2128                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2129                         continue;
2130
2131                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2132                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2133                         printf("bad BIOS file: %s\n", ent->d_name);
2134                         continue;
2135                 }
2136
2137                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2138                         bioses[bios_i++] = strdup(ent->d_name);
2139                         continue;
2140                 }
2141
2142                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2143         }
2144
2145         closedir(dir);
2146
2147 do_plugins:
2148         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2149         dir = opendir(fname);
2150         if (dir == NULL) {
2151                 perror("scan_bios_plugins plugins opendir");
2152                 goto do_memcards;
2153         }
2154
2155         while (1) {
2156                 void *h, *tmp;
2157
2158                 errno = 0;
2159                 ent = readdir(dir);
2160                 if (ent == NULL) {
2161                         if (errno != 0)
2162                                 perror("readdir");
2163                         break;
2164                 }
2165                 p = strstr(ent->d_name, ".so");
2166                 if (p == NULL)
2167                         continue;
2168
2169                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2170                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2171                 if (h == NULL) {
2172                         fprintf(stderr, "%s\n", dlerror());
2173                         continue;
2174                 }
2175
2176                 // now what do we have here?
2177                 tmp = dlsym(h, "GPUinit");
2178                 if (tmp) {
2179                         dlclose(h);
2180                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2181                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2182                         continue;
2183                 }
2184
2185                 tmp = dlsym(h, "SPUinit");
2186                 if (tmp) {
2187                         dlclose(h);
2188                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2189                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2190                         continue;
2191                 }
2192
2193                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2194                 dlclose(h);
2195         }
2196
2197         closedir(dir);
2198
2199 do_memcards:
2200         dir = opendir("." MEMCARD_DIR);
2201         if (dir == NULL) {
2202                 perror("scan_bios_plugins memcards opendir");
2203                 return;
2204         }
2205
2206         while (1) {
2207                 struct stat st;
2208
2209                 errno = 0;
2210                 ent = readdir(dir);
2211                 if (ent == NULL) {
2212                         if (errno != 0)
2213                                 perror("readdir");
2214                         break;
2215                 }
2216
2217                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2218                         continue;
2219
2220                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2221                 if (stat(fname, &st) != 0) {
2222                         printf("bad memcard file: %s\n", ent->d_name);
2223                         continue;
2224                 }
2225
2226                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2227                         memcards[mc_i++] = strdup(ent->d_name);
2228                         continue;
2229                 }
2230
2231                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2232         }
2233
2234         if (mc_i > 2)
2235                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2236
2237         closedir(dir);
2238 }
2239
2240 void menu_init(void)
2241 {
2242         char buff[MAXPATHLEN];
2243
2244         strcpy(last_selected_fname, "/media");
2245
2246         cpu_clock_st = cpu_clock = plat_cpu_clock_get();
2247
2248         scan_bios_plugins();
2249         menu_init_common();
2250
2251         menu_set_defconfig();
2252         menu_load_config(0);
2253         menu_do_last_cd_img(1);
2254         last_vout_w = 320;
2255         last_vout_h = 240;
2256         last_vout_bpp = 16;
2257
2258         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2259         g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2260         if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2261                 fprintf(stderr, "OOM\n");
2262                 exit(1);
2263         }
2264
2265         emu_make_path(buff, "skin/background.png", sizeof(buff));
2266         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2267
2268 #ifndef __ARM_ARCH_7A__ /* XXX */
2269         me_enable(e_menu_gfx_options, MA_OPT_SCALER, 0);
2270         me_enable(e_menu_gfx_options, MA_OPT_FILTERING, 0);
2271         me_enable(e_menu_gfx_options, MA_OPT_FILTERING2, 0);
2272         me_enable(e_menu_gfx_options, MA_OPT_SCALER_C, 0);
2273         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2274 #else
2275         me_enable(e_menu_gfx_options, MA_OPT_SCALER2, 0);
2276         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
2277         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2278 #endif
2279 }
2280
2281 void menu_notify_mode_change(int w, int h, int bpp)
2282 {
2283         last_vout_w = w;
2284         last_vout_h = h;
2285         last_vout_bpp = bpp;
2286 }
2287
2288 static void menu_leave_emu(void)
2289 {
2290         if (GPU_close != NULL) {
2291                 int ret = GPU_close();
2292                 if (ret)
2293                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2294         }
2295
2296         plat_video_menu_enter(ready_to_go);
2297
2298         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2299         if (pl_vout_buf != NULL && ready_to_go) {
2300                 int x = max(0, g_menuscreen_w - last_vout_w);
2301                 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2302                 int w = min(g_menuscreen_w, last_vout_w);
2303                 int h = min(g_menuscreen_h, last_vout_h);
2304                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2305                 char *s = pl_vout_buf;
2306
2307                 if (last_vout_bpp == 16) {
2308                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2309                                 menu_darken_bg(d, s, w, 0);
2310                 }
2311                 else {
2312                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2313                                 rgb888_to_rgb565(d, s, w * 3);
2314                                 menu_darken_bg(d, d, w, 0);
2315                         }
2316                 }
2317         }
2318
2319         if (ready_to_go)
2320                 cpu_clock = plat_cpu_clock_get();
2321 }
2322
2323 void menu_prepare_emu(void)
2324 {
2325         R3000Acpu *prev_cpu = psxCpu;
2326
2327         plat_video_menu_leave();
2328
2329         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2330         if (psxCpu != prev_cpu)
2331                 // note that this does not really reset, just clears drc caches
2332                 psxCpu->Reset();
2333
2334         // core doesn't care about Config.Cdda changes,
2335         // so handle them manually here
2336         if (Config.Cdda)
2337                 CDR_stop();
2338
2339         menu_sync_config();
2340         if (cpu_clock > 0)
2341                 plat_cpu_clock_apply(cpu_clock);
2342
2343         // push config to GPU plugin
2344         plugin_call_rearmed_cbs();
2345
2346         if (GPU_open != NULL) {
2347                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2348                 if (ret)
2349                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2350         }
2351
2352         dfinput_activate();
2353 }
2354
2355 void me_update_msg(const char *msg)
2356 {
2357         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2358         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2359
2360         menu_error_time = plat_get_ticks_ms();
2361         lprintf("msg: %s\n", menu_error_msg);
2362 }
2363
2364 void menu_finish(void)
2365 {
2366         plat_cpu_clock_apply(cpu_clock_st);
2367 }