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