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