989c6ec95c02cb6c4f757da75ea5acad37092458
[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         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 // XXX: should really menu code cotrol the layer size?
2262 void menu_notify_mode_change(int w, int h, int bpp)
2263 {
2264         float mult;
2265         int imult;
2266
2267         last_psx_w = w;
2268         last_psx_h = h;
2269         last_psx_bpp = bpp;
2270
2271         switch (scaling) {
2272         case SCALE_1_1:
2273                 g_layer_w = w; g_layer_h = h;
2274                 break;
2275
2276         case SCALE_4_3v2:
2277                 if (h > g_menuscreen_h || (240 < h && h <= 360))
2278                         goto fractional_4_3;
2279
2280                 // 4:3 that prefers integer scaling
2281                 imult = g_menuscreen_h / h;
2282                 g_layer_w = w * imult;
2283                 g_layer_h = h * imult;
2284                 mult = (float)g_layer_w / (float)g_layer_h;
2285                 if (mult < 1.25f || mult > 1.666f)
2286                         g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2287                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2288                 break;
2289
2290         fractional_4_3:
2291         case SCALE_4_3:
2292                 mult = 240.0f / (float)h * 4.0f / 3.0f;
2293                 if (h > 256)
2294                         mult *= 2.0f;
2295                 g_layer_w = mult * (float)g_menuscreen_h;
2296                 g_layer_h = g_menuscreen_h;
2297                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2298                 break;
2299
2300         case SCALE_FULLSCREEN:
2301                 g_layer_w = g_menuscreen_w;
2302                 g_layer_h = g_menuscreen_h;
2303                 break;
2304
2305         default:
2306                 break;
2307         }
2308
2309         g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2310         g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2311         if (g_layer_x < 0) g_layer_x = 0;
2312         if (g_layer_y < 0) g_layer_y = 0;
2313         if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2314         if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2315 }
2316
2317 static void menu_leave_emu(void)
2318 {
2319         if (GPU_close != NULL) {
2320                 int ret = GPU_close();
2321                 if (ret)
2322                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2323         }
2324
2325         plat_video_menu_enter(ready_to_go);
2326
2327         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2328         if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
2329                 int x = max(0, g_menuscreen_w - last_psx_w);
2330                 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2331                 int w = min(g_menuscreen_w, last_psx_w);
2332                 int h = min(g_menuscreen_h, last_psx_h);
2333                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2334                 u16 *s = pl_vout_buf;
2335
2336                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
2337                         menu_darken_bg(d, s, w, 0);
2338         }
2339
2340         if (ready_to_go)
2341                 cpu_clock = plat_cpu_clock_get();
2342 }
2343
2344 void menu_prepare_emu(void)
2345 {
2346         R3000Acpu *prev_cpu = psxCpu;
2347
2348         plat_video_menu_leave();
2349
2350         menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2351
2352         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2353         if (psxCpu != prev_cpu)
2354                 // note that this does not really reset, just clears drc caches
2355                 psxCpu->Reset();
2356
2357         // core doesn't care about Config.Cdda changes,
2358         // so handle them manually here
2359         if (Config.Cdda)
2360                 CDR_stop();
2361
2362         menu_sync_config();
2363         apply_lcdrate(Config.PsxType);
2364         apply_filter(filter);
2365         plat_cpu_clock_apply(cpu_clock);
2366
2367         // push config to GPU plugin
2368         plugin_call_rearmed_cbs();
2369
2370         if (GPU_open != NULL) {
2371                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2372                 if (ret)
2373                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2374         }
2375
2376         dfinput_activate();
2377 }
2378
2379 void me_update_msg(const char *msg)
2380 {
2381         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2382         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2383
2384         menu_error_time = plat_get_ticks_ms();
2385         lprintf("msg: %s\n", menu_error_msg);
2386 }
2387