frontend: save gpu-gles settings too
[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 "omap.h"
26 #include "plat.h"
27 #include "pcnt.h"
28 #include "common/plat.h"
29 #include "common/input.h"
30 #include "linux/in_evdev.h"
31 #include "../libpcsxcore/misc.h"
32 #include "../libpcsxcore/cdrom.h"
33 #include "../libpcsxcore/cdriso.h"
34 #include "../libpcsxcore/psemu_plugin_defs.h"
35 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
36 #include "../plugins/dfinput/main.h"
37 #include "../plugins/gpulib/cspace.h"
38 #include "revision.h"
39
40 #define REARMED_BIRTHDAY_TIME 1293306830        /* 25 Dec 2010 */
41
42 #define array_size(x) (sizeof(x) / sizeof(x[0]))
43
44 typedef enum
45 {
46         MA_NONE = 1,
47         MA_MAIN_RESUME_GAME,
48         MA_MAIN_SAVE_STATE,
49         MA_MAIN_LOAD_STATE,
50         MA_MAIN_RESET_GAME,
51         MA_MAIN_LOAD_ROM,
52         MA_MAIN_SWAP_CD,
53         MA_MAIN_SWAP_CD_MULTI,
54         MA_MAIN_RUN_BIOS,
55         MA_MAIN_RUN_EXE,
56         MA_MAIN_CONTROLS,
57         MA_MAIN_CREDITS,
58         MA_MAIN_EXIT,
59         MA_CTRL_PLAYER1,
60         MA_CTRL_PLAYER2,
61         MA_CTRL_ANALOG,
62         MA_CTRL_EMU,
63         MA_CTRL_DEV_FIRST,
64         MA_CTRL_DEV_NEXT,
65         MA_CTRL_NUBS_BTNS,
66         MA_CTRL_DEADZONE,
67         MA_CTRL_VIBRATION,
68         MA_CTRL_DONE,
69         MA_OPT_SAVECFG,
70         MA_OPT_SAVECFG_GAME,
71         MA_OPT_CPU_CLOCKS,
72         MA_OPT_DISP_OPTS,
73         MA_OPT_SCALER,
74         MA_OPT_SCALER2,
75         MA_OPT_FILTERING,
76         MA_OPT_SCALER_C,
77 } menu_id;
78
79 enum {
80         SCALE_1_1,
81         SCALE_4_3,
82         SCALE_4_3v2,
83         SCALE_FULLSCREEN,
84         SCALE_CUSTOM,
85 };
86
87 static int last_psx_w, last_psx_h, last_psx_bpp;
88 static int scaling, filter, cpu_clock, cpu_clock_st, volume_boost, frameskip;
89 static char rom_fname_reload[MAXPATHLEN];
90 static char last_selected_fname[MAXPATHLEN];
91 static int warned_about_bios, region, in_type_sel1, in_type_sel2;
92 static int psx_clock;
93 static int memcard1_sel, memcard2_sel;
94 int g_opts;
95 int soft_scaling, analog_deadzone; // for Caanoo
96
97 #ifdef __ARM_ARCH_7A__
98 #define DEFAULT_PSX_CLOCK 57
99 #define DEFAULT_PSX_CLOCK_S "57"
100 #else
101 #define DEFAULT_PSX_CLOCK 50
102 #define DEFAULT_PSX_CLOCK_S "50"
103 #endif
104
105 // sound plugin
106 extern int iUseReverb;
107 extern int iUseInterpolation;
108 extern int iXAPitch;
109 extern int iSPUIRQWait;
110 extern int iUseTimer;
111 extern int iVolume;
112
113 static const char *bioses[24];
114 static const char *gpu_plugins[16];
115 static const char *spu_plugins[16];
116 static const char *memcards[32];
117 static int bios_sel, gpu_plugsel, spu_plugsel;
118
119
120 static int min(int x, int y) { return x < y ? x : y; }
121 static int max(int x, int y) { return x > y ? x : y; }
122
123 void emu_make_path(char *buff, const char *end, int size)
124 {
125         int pos, end_len;
126
127         end_len = strlen(end);
128         pos = plat_get_root_dir(buff, size);
129         strncpy(buff + pos, end, size - pos);
130         buff[size - 1] = 0;
131         if (pos + end_len > size - 1)
132                 printf("Warning: path truncated: %s\n", buff);
133 }
134
135 static int emu_check_save_file(int slot, int *time)
136 {
137         char fname[MAXPATHLEN];
138         struct stat status;
139         int ret;
140         
141         ret = emu_check_state(slot);
142         if (ret != 0 || time == NULL)
143                 return ret == 0 ? 1 : 0;
144
145         ret = get_state_filename(fname, sizeof(fname), slot);
146         if (ret != 0)
147                 return 1;
148
149         ret = stat(fname, &status);
150         if (ret != 0)
151                 return 1;
152
153         if (status.st_mtime < REARMED_BIRTHDAY_TIME)
154                 return 1; // probably bad rtc like on some Caanoos
155
156         *time = status.st_mtime;
157
158         return 1;
159 }
160
161 static int emu_save_load_game(int load, int unused)
162 {
163         int ret;
164
165         if (load) {
166                 ret = emu_load_state(state_slot);
167
168                 // reflect hle/bios mode from savestate
169                 if (Config.HLE)
170                         bios_sel = 0;
171                 else if (bios_sel == 0 && bioses[1] != NULL)
172                         // XXX: maybe find the right bios instead
173                         bios_sel = 1;
174         }
175         else
176                 ret = emu_save_state(state_slot);
177
178         return ret;
179 }
180
181 // propagate menu settings to the emu vars
182 static void menu_sync_config(void)
183 {
184         static int allow_abs_only_old;
185
186         Config.PsxAuto = 1;
187         if (region > 0) {
188                 Config.PsxAuto = 0;
189                 Config.PsxType = region - 1;
190         }
191         cycle_multiplier = 10000 / psx_clock;
192
193         switch (in_type_sel1) {
194         case 1:  in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
195         case 2:  in_type1 = PSE_PAD_TYPE_GUNCON;    break;
196         default: in_type1 = PSE_PAD_TYPE_STANDARD;
197         }
198         switch (in_type_sel2) {
199         case 1:  in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
200         case 2:  in_type2 = PSE_PAD_TYPE_GUNCON;    break;
201         default: in_type2 = PSE_PAD_TYPE_STANDARD;
202         }
203         if (in_evdev_allow_abs_only != allow_abs_only_old) {
204                 in_probe();
205                 allow_abs_only_old = in_evdev_allow_abs_only;
206         }
207
208         iVolume = 768 + 128 * volume_boost;
209         pl_rearmed_cbs.frameskip = frameskip - 1;
210         pl_timing_prepare(Config.PsxType);
211 }
212
213 static void menu_set_defconfig(void)
214 {
215         emu_set_default_config();
216
217         g_opts = 0;
218         scaling = SCALE_4_3;
219         volume_boost = 0;
220         frameskip = 0;
221         analog_deadzone = 50;
222         soft_scaling = 1;
223         psx_clock = DEFAULT_PSX_CLOCK;
224
225         region = 0;
226         in_type_sel1 = in_type_sel2 = 0;
227         in_evdev_allow_abs_only = 0;
228
229         menu_sync_config();
230 }
231
232 #define CE_CONFIG_STR(val) \
233         { #val, 0, Config.val }
234
235 #define CE_CONFIG_VAL(val) \
236         { #val, sizeof(Config.val), &Config.val }
237
238 #define CE_STR(val) \
239         { #val, 0, val }
240
241 #define CE_INTVAL(val) \
242         { #val, sizeof(val), &val }
243
244 #define CE_INTVAL_P(val) \
245         { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
246
247 // 'versioned' var, used when defaults change
248 #define CE_CONFIG_STR_V(val, ver) \
249         { #val #ver, 0, Config.val }
250
251 #define CE_INTVAL_V(val, ver) \
252         { #val #ver, sizeof(val), &val }
253
254 #define CE_INTVAL_PV(val, ver) \
255         { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
256
257 static const struct {
258         const char *name;
259         size_t len;
260         void *val;
261 } config_data[] = {
262         CE_CONFIG_STR(Bios),
263         CE_CONFIG_STR_V(Gpu, 2),
264         CE_CONFIG_STR(Spu),
265 //      CE_CONFIG_STR(Cdr),
266         CE_CONFIG_VAL(Xa),
267         CE_CONFIG_VAL(Sio),
268         CE_CONFIG_VAL(Mdec),
269         CE_CONFIG_VAL(Cdda),
270         CE_CONFIG_VAL(Debug),
271         CE_CONFIG_VAL(PsxOut),
272         CE_CONFIG_VAL(SpuIrq),
273         CE_CONFIG_VAL(RCntFix),
274         CE_CONFIG_VAL(VSyncWA),
275         CE_CONFIG_VAL(Cpu),
276         CE_CONFIG_VAL(CdrReschedule),
277         CE_INTVAL(region),
278         CE_INTVAL_V(scaling, 2),
279         CE_INTVAL(g_layer_x),
280         CE_INTVAL(g_layer_y),
281         CE_INTVAL(g_layer_w),
282         CE_INTVAL(g_layer_h),
283         CE_INTVAL(filter),
284         CE_INTVAL(state_slot),
285         CE_INTVAL(cpu_clock),
286         CE_INTVAL(g_opts),
287         CE_INTVAL(in_type_sel1),
288         CE_INTVAL(in_type_sel2),
289         CE_INTVAL(analog_deadzone),
290         CE_INTVAL_V(frameskip, 3),
291         CE_INTVAL_P(gpu_peops.iUseDither),
292         CE_INTVAL_P(gpu_peops.dwActFixes),
293         CE_INTVAL_P(gpu_unai.lineskip),
294         CE_INTVAL_P(gpu_unai.abe_hack),
295         CE_INTVAL_P(gpu_unai.no_light),
296         CE_INTVAL_P(gpu_unai.no_blend),
297         CE_INTVAL_P(gpu_neon.allow_interlace),
298         CE_INTVAL_P(gpu_peopsgl.bDrawDither),
299         CE_INTVAL_P(gpu_peopsgl.iFilterType),
300         CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
301         CE_INTVAL_P(gpu_peopsgl.iUseMask),
302         CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
303         CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
304         CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
305         CE_INTVAL_P(gpu_peopsgl.iVRamSize),
306         CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
307         CE_INTVAL_P(gpu_peopsgl.dwActFixes),
308         CE_INTVAL_V(iUseReverb, 3),
309         CE_INTVAL_V(iXAPitch, 3),
310         CE_INTVAL_V(iUseInterpolation, 3),
311         CE_INTVAL_V(iSPUIRQWait, 3),
312         CE_INTVAL_V(iUseTimer, 3),
313         CE_INTVAL(warned_about_bios),
314         CE_INTVAL(in_evdev_allow_abs_only),
315         CE_INTVAL(volume_boost),
316         CE_INTVAL(psx_clock),
317         CE_INTVAL(new_dynarec_hacks),
318         CE_INTVAL(in_enable_vibration),
319 };
320
321 static char *get_cd_label(void)
322 {
323         static char trimlabel[33];
324         int j;
325
326         strncpy(trimlabel, CdromLabel, 32);
327         trimlabel[32] = 0;
328         for (j = 31; j >= 0; j--)
329                 if (trimlabel[j] == ' ')
330                         trimlabel[j] = 0;
331
332         return trimlabel;
333 }
334
335 static void make_cfg_fname(char *buf, size_t size, int is_game)
336 {
337         if (is_game)
338                 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
339         else
340                 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
341 }
342
343 static void keys_write_all(FILE *f);
344
345 static int menu_write_config(int is_game)
346 {
347         char cfgfile[MAXPATHLEN];
348         FILE *f;
349         int i;
350
351         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
352         f = fopen(cfgfile, "w");
353         if (f == NULL) {
354                 printf("menu_write_config: failed to open: %s\n", cfgfile);
355                 return -1;
356         }
357
358         for (i = 0; i < ARRAY_SIZE(config_data); i++) {
359                 fprintf(f, "%s = ", config_data[i].name);
360                 switch (config_data[i].len) {
361                 case 0:
362                         fprintf(f, "%s\n", (char *)config_data[i].val);
363                         break;
364                 case 1:
365                         fprintf(f, "%x\n", *(u8 *)config_data[i].val);
366                         break;
367                 case 2:
368                         fprintf(f, "%x\n", *(u16 *)config_data[i].val);
369                         break;
370                 case 4:
371                         fprintf(f, "%x\n", *(u32 *)config_data[i].val);
372                         break;
373                 default:
374                         printf("menu_write_config: unhandled len %d for %s\n",
375                                  config_data[i].len, config_data[i].name);
376                         break;
377                 }
378         }
379
380         if (!is_game)
381                 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
382
383         keys_write_all(f);
384         fclose(f);
385
386         return 0;
387 }
388
389 static void parse_str_val(char *cval, const char *src)
390 {
391         char *tmp;
392         strncpy(cval, src, MAXPATHLEN);
393         cval[MAXPATHLEN - 1] = 0;
394         tmp = strchr(cval, '\n');
395         if (tmp == NULL)
396                 tmp = strchr(cval, '\r');
397         if (tmp != NULL)
398                 *tmp = 0;
399 }
400
401 static void keys_load_all(const char *cfg);
402
403 static int menu_load_config(int is_game)
404 {
405         char cfgfile[MAXPATHLEN];
406         int i, ret = -1;
407         long size;
408         char *cfg;
409         FILE *f;
410
411         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
412         f = fopen(cfgfile, "r");
413         if (f == NULL) {
414                 printf("menu_load_config: failed to open: %s\n", cfgfile);
415                 goto fail;
416         }
417
418         fseek(f, 0, SEEK_END);
419         size = ftell(f);
420         if (size <= 0) {
421                 printf("bad size %ld: %s\n", size, cfgfile);
422                 goto fail;
423         }
424
425         cfg = malloc(size + 1);
426         if (cfg == NULL)
427                 goto fail;
428
429         fseek(f, 0, SEEK_SET);
430         if (fread(cfg, 1, size, f) != size) {
431                 printf("failed to read: %s\n", cfgfile);
432                 goto fail_read;
433         }
434         cfg[size] = 0;
435
436         for (i = 0; i < ARRAY_SIZE(config_data); i++) {
437                 char *tmp, *tmp2;
438                 u32 val;
439
440                 tmp = strstr(cfg, config_data[i].name);
441                 if (tmp == NULL)
442                         continue;
443                 tmp += strlen(config_data[i].name);
444                 if (strncmp(tmp, " = ", 3) != 0)
445                         continue;
446                 tmp += 3;
447
448                 if (config_data[i].len == 0) {
449                         parse_str_val(config_data[i].val, tmp);
450                         continue;
451                 }
452
453                 tmp2 = NULL;
454                 val = strtoul(tmp, &tmp2, 16);
455                 if (tmp2 == NULL || tmp == tmp2)
456                         continue; // parse failed
457
458                 switch (config_data[i].len) {
459                 case 1:
460                         *(u8 *)config_data[i].val = val;
461                         break;
462                 case 2:
463                         *(u16 *)config_data[i].val = val;
464                         break;
465                 case 4:
466                         *(u32 *)config_data[i].val = val;
467                         break;
468                 default:
469                         printf("menu_load_config: unhandled len %d for %s\n",
470                                  config_data[i].len, config_data[i].name);
471                         break;
472                 }
473         }
474
475         if (!is_game) {
476                 char *tmp = strstr(cfg, "lastcdimg = ");
477                 if (tmp != NULL) {
478                         tmp += 12;
479                         parse_str_val(last_selected_fname, tmp);
480                 }
481         }
482
483         keys_load_all(cfg);
484         ret = 0;
485 fail_read:
486         free(cfg);
487 fail:
488         if (f != NULL)
489                 fclose(f);
490
491         menu_sync_config();
492
493         // sync plugins
494         for (i = bios_sel = 0; bioses[i] != NULL; i++)
495                 if (strcmp(Config.Bios, bioses[i]) == 0)
496                         { bios_sel = i; break; }
497
498         for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
499                 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
500                         { gpu_plugsel = i; break; }
501
502         for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
503                 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
504                         { spu_plugsel = i; break; }
505
506         return ret;
507 }
508
509 // rrrr rggg gggb bbbb
510 static unsigned short fname2color(const char *fname)
511 {
512         static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z",
513                                             ".bz", ".znx", ".pbp", ".cbn" };
514         static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub",
515                                             ".table", ".index", ".sbi" };
516         const char *ext = strrchr(fname, '.');
517         int i;
518
519         if (ext == NULL)
520                 return 0xffff;
521         for (i = 0; i < array_size(cdimg_exts); i++)
522                 if (strcasecmp(ext, cdimg_exts[i]) == 0)
523                         return 0x7bff;
524         for (i = 0; i < array_size(other_exts); i++)
525                 if (strcasecmp(ext, other_exts[i]) == 0)
526                         return 0xa514;
527         return 0xffff;
528 }
529
530 static void draw_savestate_bg(int slot);
531
532 static const char *filter_exts[] = {
533         ".mp3", ".MP3", ".txt", ".htm", "html", ".jpg", ".pnd"
534 };
535
536 #define MENU_ALIGN_LEFT
537 #ifdef __ARM_ARCH_7A__ // assume hires device
538 #define MENU_X2 1
539 #else
540 #define MENU_X2 0
541 #endif
542
543 #define menu_init menu_init_common
544 #include "common/menu.c"
545 #undef menu_init
546
547 // a bit of black magic here
548 static void draw_savestate_bg(int slot)
549 {
550         static const int psx_widths[8]  = { 256, 368, 320, 384, 512, 512, 640, 640 };
551         int x, y, w, h;
552         char fname[MAXPATHLEN];
553         GPUFreeze_t *gpu;
554         u16 *s, *d;
555         gzFile f;
556         int ret;
557         u32 tmp;
558
559         ret = get_state_filename(fname, sizeof(fname), slot);
560         if (ret != 0)
561                 return;
562
563         f = gzopen(fname, "rb");
564         if (f == NULL)
565                 return;
566
567         if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
568                 fprintf(stderr, "gzseek failed\n");
569                 gzclose(f);
570                 return;
571         }
572
573         gpu = malloc(sizeof(*gpu));
574         if (gpu == NULL) {
575                 gzclose(f);
576                 return;
577         }
578
579         ret = gzread(f, gpu, sizeof(*gpu));
580         gzclose(f);
581         if (ret != sizeof(*gpu)) {
582                 fprintf(stderr, "gzread failed\n");
583                 goto out;
584         }
585
586         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
587
588         if (gpu->ulStatus & 0x800000)
589                 goto out; // disabled
590
591         x = gpu->ulControl[5] & 0x3ff;
592         y = (gpu->ulControl[5] >> 10) & 0x1ff;
593         s = (u16 *)gpu->psxVRam + y * 1024 + (x & ~1);
594         w = psx_widths[(gpu->ulStatus >> 16) & 7];
595         tmp = gpu->ulControl[7];
596         h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
597         if (gpu->ulStatus & 0x80000) // doubleheight
598                 h *= 2;
599
600         x = max(0, g_menuscreen_w - w) & ~3;
601         y = max(0, g_menuscreen_h / 2 - h / 2);
602         w = min(g_menuscreen_w, w);
603         h = min(g_menuscreen_h, h);
604         d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
605
606         for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
607                 if (gpu->ulStatus & 0x200000)
608                         bgr888_to_rgb565(d, s, w * 3);
609                 else
610                         bgr555_to_rgb565(d, s, w * 2);
611 #ifndef __ARM_ARCH_7A__
612                 // better darken this on small screens
613                 menu_darken_bg(d, d, w * 2, 0);
614 #endif
615         }
616
617 out:
618         free(gpu);
619 }
620
621 // ---------- XXX: pandora specific -----------
622
623 static const char pnd_script_base[] = "sudo -n /usr/pandora/scripts";
624 static char **pnd_filter_list;
625
626 static void apply_filter(int which)
627 {
628         static int old = -1;
629         char buf[128];
630         int i;
631
632         if (pnd_filter_list == NULL || which == old)
633                 return;
634
635         for (i = 0; i < which; i++)
636                 if (pnd_filter_list[i] == NULL)
637                         return;
638
639         if (pnd_filter_list[i] == NULL)
640                 return;
641
642         snprintf(buf, sizeof(buf), "%s/op_videofir.sh %s", pnd_script_base, pnd_filter_list[i]);
643         system(buf);
644         old = which;
645 }
646
647 static void apply_lcdrate(int pal)
648 {
649         static int old = -1;
650         char buf[128];
651
652         if (pal == old)
653                 return;
654
655         snprintf(buf, sizeof(buf), "%s/op_lcdrate.sh %d",
656                         pnd_script_base, pal ? 50 : 60);
657         system(buf);
658         old = pal;
659 }
660
661 static menu_entry e_menu_gfx_options[];
662
663 static void pnd_menu_init(void)
664 {
665         struct dirent *ent;
666         int i, count = 0;
667         char **mfilters;
668         char buff[64];
669         DIR *dir;
670
671         cpu_clock_st = cpu_clock = plat_cpu_clock_get();
672
673         dir = opendir("/etc/pandora/conf/dss_fir");
674         if (dir == NULL) {
675                 perror("filter opendir");
676                 return;
677         }
678
679         while (1) {
680                 errno = 0;
681                 ent = readdir(dir);
682                 if (ent == NULL) {
683                         if (errno != 0)
684                                 perror("readdir");
685                         break;
686                 }
687
688                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
689                         continue;
690
691                 count++;
692         }
693
694         if (count == 0)
695                 return;
696
697         mfilters = calloc(count + 1, sizeof(mfilters[0]));
698         if (mfilters == NULL)
699                 return;
700
701         rewinddir(dir);
702         for (i = 0; (ent = readdir(dir)); ) {
703                 size_t len;
704
705                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
706                         continue;
707
708                 len = strlen(ent->d_name);
709
710                 // skip pre-HF5 extra files
711                 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
712                         continue;
713                 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
714                         continue;
715
716                 // have to cut "_up_h" for pre-HF5
717                 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
718                         len -= 5;
719
720                 if (len > sizeof(buff) - 1)
721                         continue;
722
723                 strncpy(buff, ent->d_name, len);
724                 buff[len] = 0;
725                 mfilters[i] = strdup(buff);
726                 if (mfilters[i] != NULL)
727                         i++;
728         }
729         closedir(dir);
730
731         i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
732         e_menu_gfx_options[i].data = (void *)mfilters;
733         pnd_filter_list = mfilters;
734 }
735
736 void menu_finish(void)
737 {
738         plat_cpu_clock_apply(cpu_clock_st);
739 }
740
741 // -------------- key config --------------
742
743 me_bind_action me_ctrl_actions[] =
744 {
745         { "UP      ", 1 << DKEY_UP},
746         { "DOWN    ", 1 << DKEY_DOWN },
747         { "LEFT    ", 1 << DKEY_LEFT },
748         { "RIGHT   ", 1 << DKEY_RIGHT },
749         { "TRIANGLE", 1 << DKEY_TRIANGLE },
750         { "CIRCLE  ", 1 << DKEY_CIRCLE },
751         { "CROSS   ", 1 << DKEY_CROSS },
752         { "SQUARE  ", 1 << DKEY_SQUARE },
753         { "L1      ", 1 << DKEY_L1 },
754         { "R1      ", 1 << DKEY_R1 },
755         { "L2      ", 1 << DKEY_L2 },
756         { "R2      ", 1 << DKEY_R2 },
757         { "L3      ", 1 << DKEY_L3 },
758         { "R3      ", 1 << DKEY_R3 },
759         { "START   ", 1 << DKEY_START },
760         { "SELECT  ", 1 << DKEY_SELECT },
761         { NULL,       0 }
762 };
763
764 me_bind_action emuctrl_actions[] =
765 {
766         { "Save State       ", 1 << SACTION_SAVE_STATE },
767         { "Load State       ", 1 << SACTION_LOAD_STATE },
768         { "Prev Save Slot   ", 1 << SACTION_PREV_SSLOT },
769         { "Next Save Slot   ", 1 << SACTION_NEXT_SSLOT },
770         { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
771         { "Take Screenshot  ", 1 << SACTION_SCREENSHOT },
772         { "Enter Menu       ", 1 << SACTION_ENTER_MENU },
773 #ifdef __ARM_ARCH_7A__ /* XXX */
774         { "Minimize         ", 1 << SACTION_MINIMIZE },
775 #endif
776         { "Gun Trigger      ", 1 << SACTION_GUN_TRIGGER },
777         { "Gun A button     ", 1 << SACTION_GUN_A },
778         { "Gun B button     ", 1 << SACTION_GUN_B },
779         { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
780 #ifndef __ARM_ARCH_7A__ /* XXX */
781         { "Volume Up        ", 1 << SACTION_VOLUME_UP },
782         { "Volume Down      ", 1 << SACTION_VOLUME_DOWN },
783 #endif
784         { NULL,                0 }
785 };
786
787 static char *mystrip(char *str)
788 {
789         int i, len;
790
791         len = strlen(str);
792         for (i = 0; i < len; i++)
793                 if (str[i] != ' ') break;
794         if (i > 0) memmove(str, str + i, len - i + 1);
795
796         len = strlen(str);
797         for (i = len - 1; i >= 0; i--)
798                 if (str[i] != ' ') break;
799         str[i+1] = 0;
800
801         return str;
802 }
803
804 static void get_line(char *d, size_t size, const char *s)
805 {
806         const char *pe;
807         size_t len;
808
809         for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
810                 ;
811         len = pe - s;
812         if (len > size - 1)
813                 len = size - 1;
814         strncpy(d, s, len);
815         d[len] = 0;
816
817         mystrip(d);
818 }
819
820 static void keys_write_all(FILE *f)
821 {
822         int d;
823
824         for (d = 0; d < IN_MAX_DEVS; d++)
825         {
826                 const int *binds = in_get_dev_binds(d);
827                 const char *name = in_get_dev_name(d, 0, 0);
828                 int k, count = 0;
829
830                 if (binds == NULL || name == NULL)
831                         continue;
832
833                 fprintf(f, "binddev = %s\n", name);
834                 in_get_config(d, IN_CFG_BIND_COUNT, &count);
835
836                 for (k = 0; k < count; k++)
837                 {
838                         int i, kbinds, mask;
839                         char act[32];
840
841                         act[0] = act[31] = 0;
842                         name = in_get_key_name(d, k);
843
844                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
845                         for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
846                                 mask = me_ctrl_actions[i].mask;
847                                 if (mask & kbinds) {
848                                         strncpy(act, me_ctrl_actions[i].name, 31);
849                                         fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
850                                         kbinds &= ~mask;
851                                 }
852                                 mask = me_ctrl_actions[i].mask << 16;
853                                 if (mask & kbinds) {
854                                         strncpy(act, me_ctrl_actions[i].name, 31);
855                                         fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
856                                         kbinds &= ~mask;
857                                 }
858                         }
859
860                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
861                         for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
862                                 mask = emuctrl_actions[i].mask;
863                                 if (mask & kbinds) {
864                                         strncpy(act, emuctrl_actions[i].name, 31);
865                                         fprintf(f, "bind %s = %s\n", name, mystrip(act));
866                                         kbinds &= ~mask;
867                                 }
868                         }
869                 }
870
871                 for (k = 0; k < array_size(in_adev); k++)
872                 {
873                         if (in_adev[k] == d)
874                                 fprintf(f, "bind_analog = %d\n", k);
875                 }
876         }
877 }
878
879 static int parse_bind_val(const char *val, int *type)
880 {
881         int i;
882
883         *type = IN_BINDTYPE_NONE;
884         if (val[0] == 0)
885                 return 0;
886         
887         if (strncasecmp(val, "player", 6) == 0)
888         {
889                 int player, shift = 0;
890                 player = atoi(val + 6) - 1;
891
892                 if ((unsigned int)player > 1)
893                         return -1;
894                 if (player == 1)
895                         shift = 16;
896
897                 *type = IN_BINDTYPE_PLAYER12;
898                 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
899                         if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
900                                 return me_ctrl_actions[i].mask << shift;
901                 }
902         }
903         for (i = 0; emuctrl_actions[i].name != NULL; i++) {
904                 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
905                         *type = IN_BINDTYPE_EMU;
906                         return emuctrl_actions[i].mask;
907                 }
908         }
909
910         return -1;
911 }
912
913 static void keys_load_all(const char *cfg)
914 {
915         char dev[256], key[128], *act;
916         const char *p;
917         int bind, bindtype;
918         int ret, dev_id;
919
920         p = cfg;
921         while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
922                 p += 10;
923
924                 get_line(dev, sizeof(dev), p);
925                 dev_id = in_config_parse_dev(dev);
926                 if (dev_id < 0) {
927                         printf("input: can't handle dev: %s\n", dev);
928                         continue;
929                 }
930
931                 in_unbind_all(dev_id, -1, -1);
932                 while ((p = strstr(p, "bind"))) {
933                         if (strncmp(p, "binddev = ", 10) == 0)
934                                 break;
935
936                         if (strncmp(p, "bind_analog", 11) == 0) {
937                                 ret = sscanf(p, "bind_analog = %d", &bind);
938                                 p += 11;
939                                 if (ret != 1) {
940                                         printf("input: parse error: %16s..\n", p);
941                                         continue;
942                                 }
943                                 if ((unsigned int)bind >= array_size(in_adev)) {
944                                         printf("input: analog id %d out of range\n", bind);
945                                         continue;
946                                 }
947                                 in_adev[bind] = dev_id;
948                                 continue;
949                         }
950
951                         p += 4;
952                         if (*p != ' ') {
953                                 printf("input: parse error: %16s..\n", p);
954                                 continue;
955                         }
956
957                         get_line(key, sizeof(key), p);
958                         act = strchr(key, '=');
959                         if (act == NULL) {
960                                 printf("parse failed: %16s..\n", p);
961                                 continue;
962                         }
963                         *act = 0;
964                         act++;
965                         mystrip(key);
966                         mystrip(act);
967
968                         bind = parse_bind_val(act, &bindtype);
969                         if (bind != -1 && bind != 0) {
970                                 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
971                                 in_config_bind_key(dev_id, key, bind, bindtype);
972                         }
973                         else
974                                 lprintf("config: unhandled action \"%s\"\n", act);
975                 }
976         }
977         in_clean_binds();
978 }
979
980 static int key_config_loop_wrap(int id, int keys)
981 {
982         switch (id) {
983                 case MA_CTRL_PLAYER1:
984                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
985                         break;
986                 case MA_CTRL_PLAYER2:
987                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
988                         break;
989                 case MA_CTRL_EMU:
990                         key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
991                         break;
992                 default:
993                         break;
994         }
995         return 0;
996 }
997
998 static const char *adevnames[IN_MAX_DEVS + 2];
999 static int stick_sel[2];
1000
1001 static menu_entry e_menu_keyconfig_analog[] =
1002 {
1003         mee_enum ("Left stick (L3)",  0, stick_sel[0], adevnames),
1004         mee_range("  X axis",    0, in_adev_axis[0][0], 0, 7),
1005         mee_range("  Y axis",    0, in_adev_axis[0][1], 0, 7),
1006         mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
1007         mee_range("  X axis",    0, in_adev_axis[1][0], 0, 7),
1008         mee_range("  Y axis",    0, in_adev_axis[1][1], 0, 7),
1009         mee_end,
1010 };
1011
1012 static int key_config_analog(int id, int keys)
1013 {
1014         int i, d, count, sel = 0;
1015         int sel2dev_map[IN_MAX_DEVS];
1016
1017         memset(adevnames, 0, sizeof(adevnames));
1018         memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1019         memset(stick_sel, 0, sizeof(stick_sel));
1020
1021         adevnames[0] = "None";
1022         i = 1;
1023         for (d = 0; d < IN_MAX_DEVS; d++)
1024         {
1025                 const char *name = in_get_dev_name(d, 0, 1);
1026                 if (name == NULL)
1027                         continue;
1028
1029                 count = 0;
1030                 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1031                 if (count == 0)
1032                         continue;
1033
1034                 if (in_adev[0] == d) stick_sel[0] = i;
1035                 if (in_adev[1] == d) stick_sel[1] = i;
1036                 sel2dev_map[i] = d;
1037                 adevnames[i++] = name;
1038         }
1039         adevnames[i] = NULL;
1040
1041         me_loop(e_menu_keyconfig_analog, &sel);
1042
1043         in_adev[0] = sel2dev_map[stick_sel[0]];
1044         in_adev[1] = sel2dev_map[stick_sel[1]];
1045
1046         return 0;
1047 }
1048
1049 static const char *mgn_dev_name(int id, int *offs)
1050 {
1051         const char *name = NULL;
1052         static int it = 0;
1053
1054         if (id == MA_CTRL_DEV_FIRST)
1055                 it = 0;
1056
1057         for (; it < IN_MAX_DEVS; it++) {
1058                 name = in_get_dev_name(it, 1, 1);
1059                 if (name != NULL)
1060                         break;
1061         }
1062
1063         it++;
1064         return name;
1065 }
1066
1067 static const char *mgn_saveloadcfg(int id, int *offs)
1068 {
1069         return "";
1070 }
1071
1072 static int mh_savecfg(int id, int keys)
1073 {
1074         if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1075                 me_update_msg("config saved");
1076         else
1077                 me_update_msg("failed to write config");
1078
1079         return 1;
1080 }
1081
1082 static int mh_input_rescan(int id, int keys)
1083 {
1084         //menu_sync_config();
1085         in_probe();
1086         me_update_msg("rescan complete.");
1087
1088         return 0;
1089 }
1090
1091 static const char *men_in_type_sel[] = {
1092         "Standard (SCPH-1080)",
1093         "Analog (SCPH-1150)",
1094         "GunCon",
1095         NULL
1096 };
1097 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1098 static const char h_notsgun[]  = "Don't trigger (shoot) when touching screen in gun games.";
1099 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1100
1101 static menu_entry e_menu_keyconfig[] =
1102 {
1103         mee_handler_id("Player 1",              MA_CTRL_PLAYER1,    key_config_loop_wrap),
1104         mee_handler_id("Player 2",              MA_CTRL_PLAYER2,    key_config_loop_wrap),
1105         mee_handler_id("Analog controls",       MA_CTRL_ANALOG,     key_config_analog),
1106         mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU,        key_config_loop_wrap),
1107         mee_label     (""),
1108         mee_enum      ("Port 1 device",     0, in_type_sel1,    men_in_type_sel),
1109         mee_enum      ("Port 2 device",     0, in_type_sel2,    men_in_type_sel),
1110         mee_onoff_h   ("Nubs as buttons",   MA_CTRL_NUBS_BTNS,  in_evdev_allow_abs_only, 1, h_nub_btns),
1111         mee_onoff_h   ("Vibration",         MA_CTRL_VIBRATION,  in_enable_vibration, 1, h_vibration),
1112         mee_range     ("Analog deadzone",   MA_CTRL_DEADZONE,   analog_deadzone, 1, 99),
1113         mee_onoff_h   ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1114         mee_cust_nosave("Save global config",       MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1115         mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1116         mee_handler   ("Rescan devices:",  mh_input_rescan),
1117         mee_label     (""),
1118         mee_label_mk  (MA_CTRL_DEV_FIRST, 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_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1125         mee_end,
1126 };
1127
1128 static int menu_loop_keyconfig(int id, int keys)
1129 {
1130         static int sel = 0;
1131
1132 //      me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1133         me_loop(e_menu_keyconfig, &sel);
1134         return 0;
1135 }
1136
1137 // ------------ gfx options menu ------------
1138
1139 static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
1140 static const char h_cscaler[]   = "Displays the scaler layer, you can resize it\n"
1141                                   "using d-pad or move it using R+d-pad";
1142 static const char *men_dummy[] = { NULL };
1143
1144 static int menu_loop_cscaler(int id, int keys)
1145 {
1146         unsigned int inp;
1147
1148         scaling = SCALE_CUSTOM;
1149
1150         omap_enable_layer(1);
1151
1152         for (;;)
1153         {
1154                 menu_draw_begin(0);
1155                 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1156                 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1157                 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1158                 menu_draw_end();
1159
1160                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
1161                 if (inp & PBTN_UP)    g_layer_y--;
1162                 if (inp & PBTN_DOWN)  g_layer_y++;
1163                 if (inp & PBTN_LEFT)  g_layer_x--;
1164                 if (inp & PBTN_RIGHT) g_layer_x++;
1165                 if (!(inp & PBTN_R)) {
1166                         if (inp & PBTN_UP)    g_layer_h += 2;
1167                         if (inp & PBTN_DOWN)  g_layer_h -= 2;
1168                         if (inp & PBTN_LEFT)  g_layer_w += 2;
1169                         if (inp & PBTN_RIGHT) g_layer_w -= 2;
1170                 }
1171                 if (inp & (PBTN_MOK|PBTN_MBACK))
1172                         break;
1173
1174                 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1175                         if (g_layer_x < 0)   g_layer_x = 0;
1176                         if (g_layer_x > 640) g_layer_x = 640;
1177                         if (g_layer_y < 0)   g_layer_y = 0;
1178                         if (g_layer_y > 420) g_layer_y = 420;
1179                         if (g_layer_w < 160) g_layer_w = 160;
1180                         if (g_layer_h < 60)  g_layer_h = 60;
1181                         if (g_layer_x + g_layer_w > 800)
1182                                 g_layer_w = 800 - g_layer_x;
1183                         if (g_layer_y + g_layer_h > 480)
1184                                 g_layer_h = 480 - g_layer_y;
1185                         omap_enable_layer(1);
1186                 }
1187         }
1188
1189         omap_enable_layer(0);
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         char ltime_s[16];
1718         char buff[64];
1719
1720         if (CdromId[0] != 0) {
1721                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1722                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1723                          Config.HLE ? "HLE" : "BIOS");
1724                 smalltext_out16(4, 1, buff, 0x105f);
1725         }
1726
1727         if (ready_to_go) {
1728                 ltime = time(NULL);
1729                 tmp = localtime(&ltime);
1730                 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1731                 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, plat_get_bat_capacity());
1732                 smalltext_out16(4, 1 + me_sfont_h, buff, 0x105f);
1733         }
1734 }
1735
1736 static void draw_frame_credits(void)
1737 {
1738         smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1739 }
1740
1741 static const char credits_text[] = 
1742         "PCSX-ReARMed\n\n"
1743         "(C) 1999-2003 PCSX Team\n"
1744         "(C) 2005-2009 PCSX-df Team\n"
1745         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1746         "ARM recompiler (C) 2009-2011 Ari64\n"
1747 #ifdef __ARM_NEON__
1748         "ARM NEON GPU (c) 2011-2012 Exophase\n"
1749 #endif
1750         "PEOpS GPU and SPU by Pete Bernert\n"
1751         "  and the P.E.Op.S. team\n"
1752         "PCSX4ALL plugin by PCSX4ALL team\n"
1753         "  Chui, Franxis, Unai\n\n"
1754         "integration, optimization and\n"
1755         "  frontend (C) 2010-2012 notaz\n";
1756
1757 static int reset_game(void)
1758 {
1759         // sanity check
1760         if (bios_sel == 0 && !Config.HLE)
1761                 return -1;
1762
1763         ClosePlugins();
1764         OpenPlugins();
1765         SysReset();
1766         if (CheckCdrom() != -1) {
1767                 LoadCdrom();
1768         }
1769         return 0;
1770 }
1771
1772 static int reload_plugins(const char *cdimg)
1773 {
1774         pl_vout_buf = NULL;
1775
1776         ClosePlugins();
1777
1778         set_cd_image(cdimg);
1779         LoadPlugins();
1780         pcnt_hook_plugins();
1781         NetOpened = 0;
1782         if (OpenPlugins() == -1) {
1783                 me_update_msg("failed to open plugins");
1784                 return -1;
1785         }
1786         plugin_call_rearmed_cbs();
1787
1788         cdrIsoMultidiskCount = 1;
1789         CdromId[0] = '\0';
1790         CdromLabel[0] = '\0';
1791
1792         return 0;
1793 }
1794
1795 static int run_bios(void)
1796 {
1797         if (bios_sel == 0)
1798                 return -1;
1799
1800         ready_to_go = 0;
1801         if (reload_plugins(NULL) != 0)
1802                 return -1;
1803         SysReset();
1804
1805         ready_to_go = 1;
1806         return 0;
1807 }
1808
1809 static int run_exe(void)
1810 {
1811         const char *fname;
1812
1813         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1814         if (fname == NULL)
1815                 return -1;
1816
1817         ready_to_go = 0;
1818         if (reload_plugins(NULL) != 0)
1819                 return -1;
1820
1821         SysReset();
1822         if (Load(fname) != 0) {
1823                 me_update_msg("exe load failed, bad file?");
1824                 printf("meh\n");
1825                 return -1;
1826         }
1827
1828         ready_to_go = 1;
1829         return 0;
1830 }
1831
1832 static int run_cd_image(const char *fname)
1833 {
1834         ready_to_go = 0;
1835         reload_plugins(fname);
1836
1837         // always autodetect, menu_sync_config will override as needed
1838         Config.PsxAuto = 1;
1839
1840         if (CheckCdrom() == -1) {
1841                 // Only check the CD if we are starting the console with a CD
1842                 ClosePlugins();
1843                 me_update_msg("unsupported/invalid CD image");
1844                 return -1;
1845         }
1846
1847         SysReset();
1848
1849         // Read main executable directly from CDRom and start it
1850         if (LoadCdrom() == -1) {
1851                 ClosePlugins();
1852                 me_update_msg("failed to load CD image");
1853                 return -1;
1854         }
1855
1856         ready_to_go = 1;
1857         snprintf(hud_msg, sizeof(hud_msg), "Booting up...");
1858         hud_new_msg = 2;
1859         return 0;
1860 }
1861
1862 static int romsel_run(void)
1863 {
1864         int prev_gpu, prev_spu;
1865         const char *fname;
1866
1867         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1868         if (fname == NULL)
1869                 return -1;
1870
1871         printf("selected file: %s\n", fname);
1872
1873         new_dynarec_clear_full();
1874
1875         if (run_cd_image(fname) != 0)
1876                 return -1;
1877
1878         prev_gpu = gpu_plugsel;
1879         prev_spu = spu_plugsel;
1880         if (menu_load_config(1) != 0)
1881                 menu_load_config(0);
1882
1883         // check for plugin changes, have to repeat
1884         // loading if game config changed plugins to reload them
1885         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1886                 printf("plugin change detected, reloading plugins..\n");
1887                 if (run_cd_image(fname) != 0)
1888                         return -1;
1889         }
1890
1891         if (Config.HLE)
1892                 printf("note: running without BIOS, expect compatibility problems\n");
1893
1894         strcpy(last_selected_fname, rom_fname_reload);
1895         return 0;
1896 }
1897
1898 static int swap_cd_image(void)
1899 {
1900         char *fname;
1901
1902         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1903         if (fname == NULL)
1904                 return -1;
1905
1906         printf("selected file: %s\n", fname);
1907
1908         CdromId[0] = '\0';
1909         CdromLabel[0] = '\0';
1910
1911         set_cd_image(fname);
1912         if (ReloadCdromPlugin() < 0) {
1913                 me_update_msg("failed to load cdr plugin");
1914                 return -1;
1915         }
1916         if (CDR_open() < 0) {
1917                 me_update_msg("failed to open cdr plugin");
1918                 return -1;
1919         }
1920
1921         SetCdOpenCaseTime(time(NULL) + 2);
1922         LidInterrupt();
1923
1924         strcpy(last_selected_fname, rom_fname_reload);
1925         return 0;
1926 }
1927
1928 static int swap_cd_multidisk(void)
1929 {
1930         cdrIsoMultidiskSelect++;
1931         CdromId[0] = '\0';
1932         CdromLabel[0] = '\0';
1933
1934         CDR_close();
1935         if (CDR_open() < 0) {
1936                 me_update_msg("failed to open cdr plugin");
1937                 return -1;
1938         }
1939
1940         SetCdOpenCaseTime(time(NULL) + 2);
1941         LidInterrupt();
1942
1943         return 0;
1944 }
1945
1946 static int main_menu_handler(int id, int keys)
1947 {
1948         switch (id)
1949         {
1950         case MA_MAIN_RESUME_GAME:
1951                 if (ready_to_go)
1952                         return 1;
1953                 break;
1954         case MA_MAIN_SAVE_STATE:
1955                 if (ready_to_go)
1956                         return menu_loop_savestate(0);
1957                 break;
1958         case MA_MAIN_LOAD_STATE:
1959                 if (ready_to_go)
1960                         return menu_loop_savestate(1);
1961                 break;
1962         case MA_MAIN_RESET_GAME:
1963                 if (ready_to_go && reset_game() == 0)
1964                         return 1;
1965                 break;
1966         case MA_MAIN_LOAD_ROM:
1967                 if (romsel_run() == 0)
1968                         return 1;
1969                 break;
1970         case MA_MAIN_SWAP_CD:
1971                 if (swap_cd_image() == 0)
1972                         return 1;
1973                 break;
1974         case MA_MAIN_SWAP_CD_MULTI:
1975                 if (swap_cd_multidisk() == 0)
1976                         return 1;
1977                 break;
1978         case MA_MAIN_RUN_BIOS:
1979                 if (run_bios() == 0)
1980                         return 1;
1981                 break;
1982         case MA_MAIN_RUN_EXE:
1983                 if (run_exe() == 0)
1984                         return 1;
1985                 break;
1986         case MA_MAIN_CREDITS:
1987                 draw_menu_message(credits_text, draw_frame_credits);
1988                 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1989                 break;
1990         case MA_MAIN_EXIT:
1991                 OnFile_Exit();
1992                 break;
1993         default:
1994                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1995                 break;
1996         }
1997
1998         return 0;
1999 }
2000
2001 static menu_entry e_menu_main2[] =
2002 {
2003         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
2004         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2005         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
2006         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
2007         mee_handler   ("Memcard manager",    menu_loop_memcards),
2008         mee_end,
2009 };
2010
2011 static int main_menu2_handler(int id, int keys)
2012 {
2013         static int sel = 0;
2014
2015         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
2016         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2017         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2018
2019         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2020 }
2021
2022 static const char h_extra[] = "Change CD, manage memcards..\n";
2023
2024 static menu_entry e_menu_main[] =
2025 {
2026         mee_label     (""),
2027         mee_label     (""),
2028         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
2029         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
2030         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
2031         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
2032         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
2033         mee_handler   ("Options",            menu_loop_options),
2034         mee_handler   ("Controls",           menu_loop_keyconfig),
2035         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
2036         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
2037         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
2038         mee_end,
2039 };
2040
2041 // ----------------------------
2042
2043 static void menu_leave_emu(void);
2044
2045 void menu_loop(void)
2046 {
2047         static int sel = 0;
2048
2049         menu_leave_emu();
2050
2051         if (bioses[1] == NULL && !warned_about_bios) {
2052                 menu_bios_warn();
2053                 warned_about_bios = 1;
2054         }
2055
2056         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2057         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
2058         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
2059         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
2060
2061         in_set_config_int(0, IN_CFG_BLOCKING, 1);
2062
2063         do {
2064                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2065         } while (!ready_to_go);
2066
2067         /* wait until menu, ok, back is released */
2068         while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2069                 ;
2070
2071         in_set_config_int(0, IN_CFG_BLOCKING, 0);
2072
2073         menu_prepare_emu();
2074 }
2075
2076 static int qsort_strcmp(const void *p1, const void *p2)
2077 {
2078         char * const *s1 = (char * const *)p1;
2079         char * const *s2 = (char * const *)p2;
2080         return strcasecmp(*s1, *s2);
2081 }
2082
2083 static void scan_bios_plugins(void)
2084 {
2085         char fname[MAXPATHLEN];
2086         struct dirent *ent;
2087         int bios_i, gpu_i, spu_i, mc_i;
2088         char *p;
2089         DIR *dir;
2090
2091         bioses[0] = "HLE";
2092         gpu_plugins[0] = "builtin_gpu";
2093         spu_plugins[0] = "builtin_spu";
2094         memcards[0] = "(none)";
2095         bios_i = gpu_i = spu_i = mc_i = 1;
2096
2097         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2098         dir = opendir(fname);
2099         if (dir == NULL) {
2100                 perror("scan_bios_plugins bios opendir");
2101                 goto do_plugins;
2102         }
2103
2104         while (1) {
2105                 struct stat st;
2106
2107                 errno = 0;
2108                 ent = readdir(dir);
2109                 if (ent == NULL) {
2110                         if (errno != 0)
2111                                 perror("readdir");
2112                         break;
2113                 }
2114
2115                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2116                         continue;
2117
2118                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2119                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2120                         printf("bad BIOS file: %s\n", ent->d_name);
2121                         continue;
2122                 }
2123
2124                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2125                         bioses[bios_i++] = strdup(ent->d_name);
2126                         continue;
2127                 }
2128
2129                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2130         }
2131
2132         closedir(dir);
2133
2134 do_plugins:
2135         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2136         dir = opendir(fname);
2137         if (dir == NULL) {
2138                 perror("scan_bios_plugins plugins opendir");
2139                 goto do_memcards;
2140         }
2141
2142         while (1) {
2143                 void *h, *tmp;
2144
2145                 errno = 0;
2146                 ent = readdir(dir);
2147                 if (ent == NULL) {
2148                         if (errno != 0)
2149                                 perror("readdir");
2150                         break;
2151                 }
2152                 p = strstr(ent->d_name, ".so");
2153                 if (p == NULL)
2154                         continue;
2155
2156                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2157                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2158                 if (h == NULL) {
2159                         fprintf(stderr, "%s\n", dlerror());
2160                         continue;
2161                 }
2162
2163                 // now what do we have here?
2164                 tmp = dlsym(h, "GPUinit");
2165                 if (tmp) {
2166                         dlclose(h);
2167                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2168                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2169                         continue;
2170                 }
2171
2172                 tmp = dlsym(h, "SPUinit");
2173                 if (tmp) {
2174                         dlclose(h);
2175                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2176                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2177                         continue;
2178                 }
2179
2180                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2181                 dlclose(h);
2182         }
2183
2184         closedir(dir);
2185
2186 do_memcards:
2187         dir = opendir("." MEMCARD_DIR);
2188         if (dir == NULL) {
2189                 perror("scan_bios_plugins memcards opendir");
2190                 return;
2191         }
2192
2193         while (1) {
2194                 struct stat st;
2195
2196                 errno = 0;
2197                 ent = readdir(dir);
2198                 if (ent == NULL) {
2199                         if (errno != 0)
2200                                 perror("readdir");
2201                         break;
2202                 }
2203
2204                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2205                         continue;
2206
2207                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2208                 if (stat(fname, &st) != 0) {
2209                         printf("bad memcard file: %s\n", ent->d_name);
2210                         continue;
2211                 }
2212
2213                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2214                         memcards[mc_i++] = strdup(ent->d_name);
2215                         continue;
2216                 }
2217
2218                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2219         }
2220
2221         if (mc_i > 2)
2222                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2223
2224         closedir(dir);
2225 }
2226
2227 void menu_init(void)
2228 {
2229         char buff[MAXPATHLEN];
2230
2231         strcpy(last_selected_fname, "/media");
2232
2233         scan_bios_plugins();
2234         pnd_menu_init();
2235         menu_init_common();
2236
2237         menu_set_defconfig();
2238         menu_load_config(0);
2239         last_psx_w = 320;
2240         last_psx_h = 240;
2241         last_psx_bpp = 16;
2242
2243         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2244         if (g_menubg_src_ptr == NULL)
2245                 exit(1);
2246         emu_make_path(buff, "skin/background.png", sizeof(buff));
2247         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2248
2249 #ifndef __ARM_ARCH_7A__ /* XXX */
2250         me_enable(e_menu_gfx_options, MA_OPT_SCALER, 0);
2251         me_enable(e_menu_gfx_options, MA_OPT_FILTERING, 0);
2252         me_enable(e_menu_gfx_options, MA_OPT_SCALER_C, 0);
2253         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2254 #else
2255         me_enable(e_menu_gfx_options, MA_OPT_SCALER2, 0);
2256         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
2257         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2258 #endif
2259 }
2260
2261 void menu_notify_mode_change(int w, int h, int bpp)
2262 {
2263         float mult;
2264         int imult;
2265
2266         last_psx_w = w;
2267         last_psx_h = h;
2268         last_psx_bpp = bpp;
2269
2270         switch (scaling) {
2271         case SCALE_1_1:
2272                 g_layer_w = w; g_layer_h = h;
2273                 break;
2274
2275         case SCALE_4_3v2:
2276                 if (h > g_menuscreen_h || (240 < h && h <= 360))
2277                         goto fractional_4_3;
2278
2279                 // 4:3 that prefers integer scaling
2280                 imult = g_menuscreen_h / h;
2281                 g_layer_w = w * imult;
2282                 g_layer_h = h * imult;
2283                 mult = (float)g_layer_w / (float)g_layer_h;
2284                 if (mult < 1.25f || mult > 1.666f)
2285                         g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2286                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2287                 break;
2288
2289         fractional_4_3:
2290         case SCALE_4_3:
2291                 mult = 240.0f / (float)h * 4.0f / 3.0f;
2292                 if (h > 256)
2293                         mult *= 2.0f;
2294                 g_layer_w = mult * (float)g_menuscreen_h;
2295                 g_layer_h = g_menuscreen_h;
2296                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2297                 break;
2298
2299         case SCALE_FULLSCREEN:
2300                 g_layer_w = g_menuscreen_w;
2301                 g_layer_h = g_menuscreen_h;
2302                 break;
2303
2304         default:
2305                 break;
2306         }
2307
2308         g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2309         g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2310         if (g_layer_x < 0) g_layer_x = 0;
2311         if (g_layer_y < 0) g_layer_y = 0;
2312         if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2313         if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2314 }
2315
2316 static void menu_leave_emu(void)
2317 {
2318         if (GPU_close != NULL) {
2319                 int ret = GPU_close();
2320                 if (ret)
2321                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2322         }
2323
2324         plat_video_menu_enter(ready_to_go);
2325
2326         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2327         if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
2328                 int x = max(0, g_menuscreen_w - last_psx_w);
2329                 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2330                 int w = min(g_menuscreen_w, last_psx_w);
2331                 int h = min(g_menuscreen_h, last_psx_h);
2332                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2333                 u16 *s = pl_vout_buf;
2334
2335                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
2336                         menu_darken_bg(d, s, w, 0);
2337         }
2338
2339         if (ready_to_go)
2340                 cpu_clock = plat_cpu_clock_get();
2341 }
2342
2343 void menu_prepare_emu(void)
2344 {
2345         R3000Acpu *prev_cpu = psxCpu;
2346
2347         plat_video_menu_leave();
2348
2349         menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2350
2351         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2352         if (psxCpu != prev_cpu)
2353                 // note that this does not really reset, just clears drc caches
2354                 psxCpu->Reset();
2355
2356         // core doesn't care about Config.Cdda changes,
2357         // so handle them manually here
2358         if (Config.Cdda)
2359                 CDR_stop();
2360
2361         menu_sync_config();
2362         apply_lcdrate(Config.PsxType);
2363         apply_filter(filter);
2364         plat_cpu_clock_apply(cpu_clock);
2365
2366         // push config to GPU plugin
2367         plugin_call_rearmed_cbs();
2368
2369         if (GPU_open != NULL) {
2370                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2371                 if (ret)
2372                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2373         }
2374
2375         dfinput_activate();
2376 }
2377
2378 void me_update_msg(const char *msg)
2379 {
2380         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2381         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2382
2383         menu_error_time = plat_get_ticks_ms();
2384         lprintf("msg: %s\n", menu_error_msg);
2385 }
2386