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