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