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