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