gpu_neon: integrate Exophase's rasterizer
[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         "GPU and SPU code by Pete Bernert\n"
1651         "  and the P.E.Op.S. team\n"
1652         "ARM recompiler (C) 2009-2011 Ari64\n"
1653         "PCSX4ALL plugins by PCSX4ALL team\n"
1654         "  Chui, Franxis, Unai\n\n"
1655         "integration, optimization and\n"
1656         "  frontend (C) 2010-2011 notaz\n";
1657
1658 static int reset_game(void)
1659 {
1660         // sanity check
1661         if (bios_sel == 0 && !Config.HLE)
1662                 return -1;
1663
1664         ClosePlugins();
1665         OpenPlugins();
1666         SysReset();
1667         if (CheckCdrom() != -1) {
1668                 LoadCdrom();
1669         }
1670         return 0;
1671 }
1672
1673 static int reload_plugins(const char *cdimg)
1674 {
1675         pl_vout_buf = NULL;
1676
1677         ClosePlugins();
1678
1679         set_cd_image(cdimg);
1680         LoadPlugins();
1681         pcnt_hook_plugins();
1682         NetOpened = 0;
1683         if (OpenPlugins() == -1) {
1684                 me_update_msg("failed to open plugins");
1685                 return -1;
1686         }
1687         plugin_call_rearmed_cbs();
1688
1689         cdrIsoMultidiskCount = 1;
1690         CdromId[0] = '\0';
1691         CdromLabel[0] = '\0';
1692
1693         return 0;
1694 }
1695
1696 static int run_bios(void)
1697 {
1698         if (bios_sel == 0)
1699                 return -1;
1700
1701         ready_to_go = 0;
1702         if (reload_plugins(NULL) != 0)
1703                 return -1;
1704         SysReset();
1705
1706         ready_to_go = 1;
1707         return 0;
1708 }
1709
1710 static int run_exe(void)
1711 {
1712         const char *fname;
1713
1714         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1715         if (fname == NULL)
1716                 return -1;
1717
1718         ready_to_go = 0;
1719         if (reload_plugins(NULL) != 0)
1720                 return -1;
1721
1722         SysReset();
1723         if (Load(fname) != 0) {
1724                 me_update_msg("exe load failed, bad file?");
1725                 printf("meh\n");
1726                 return -1;
1727         }
1728
1729         ready_to_go = 1;
1730         return 0;
1731 }
1732
1733 static int run_cd_image(const char *fname)
1734 {
1735         ready_to_go = 0;
1736         reload_plugins(fname);
1737
1738         // always autodetect, menu_sync_config will override as needed
1739         Config.PsxAuto = 1;
1740
1741         if (CheckCdrom() == -1) {
1742                 // Only check the CD if we are starting the console with a CD
1743                 ClosePlugins();
1744                 me_update_msg("unsupported/invalid CD image");
1745                 return -1;
1746         }
1747
1748         SysReset();
1749
1750         // Read main executable directly from CDRom and start it
1751         if (LoadCdrom() == -1) {
1752                 ClosePlugins();
1753                 me_update_msg("failed to load CD image");
1754                 return -1;
1755         }
1756
1757         ready_to_go = 1;
1758         snprintf(hud_msg, sizeof(hud_msg), "Booting up...");
1759         hud_new_msg = 2;
1760         return 0;
1761 }
1762
1763 static int romsel_run(void)
1764 {
1765         int prev_gpu, prev_spu;
1766         const char *fname;
1767
1768         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1769         if (fname == NULL)
1770                 return -1;
1771
1772         printf("selected file: %s\n", fname);
1773
1774         new_dynarec_clear_full();
1775
1776         if (run_cd_image(fname) != 0)
1777                 return -1;
1778
1779         prev_gpu = gpu_plugsel;
1780         prev_spu = spu_plugsel;
1781         if (menu_load_config(1) != 0)
1782                 menu_load_config(0);
1783
1784         // check for plugin changes, have to repeat
1785         // loading if game config changed plugins to reload them
1786         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1787                 printf("plugin change detected, reloading plugins..\n");
1788                 if (run_cd_image(fname) != 0)
1789                         return -1;
1790         }
1791
1792         if (Config.HLE)
1793                 printf("note: running without BIOS, expect compatibility problems\n");
1794
1795         strcpy(last_selected_fname, rom_fname_reload);
1796         return 0;
1797 }
1798
1799 static int swap_cd_image(void)
1800 {
1801         char *fname;
1802
1803         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1804         if (fname == NULL)
1805                 return -1;
1806
1807         printf("selected file: %s\n", fname);
1808
1809         CdromId[0] = '\0';
1810         CdromLabel[0] = '\0';
1811
1812         set_cd_image(fname);
1813         if (ReloadCdromPlugin() < 0) {
1814                 me_update_msg("failed to load cdr plugin");
1815                 return -1;
1816         }
1817         if (CDR_open() < 0) {
1818                 me_update_msg("failed to open cdr plugin");
1819                 return -1;
1820         }
1821
1822         SetCdOpenCaseTime(time(NULL) + 2);
1823         LidInterrupt();
1824
1825         strcpy(last_selected_fname, rom_fname_reload);
1826         return 0;
1827 }
1828
1829 static int swap_cd_multidisk(void)
1830 {
1831         cdrIsoMultidiskSelect++;
1832         CdromId[0] = '\0';
1833         CdromLabel[0] = '\0';
1834
1835         CDR_close();
1836         if (CDR_open() < 0) {
1837                 me_update_msg("failed to open cdr plugin");
1838                 return -1;
1839         }
1840
1841         SetCdOpenCaseTime(time(NULL) + 2);
1842         LidInterrupt();
1843
1844         return 0;
1845 }
1846
1847 static int main_menu_handler(int id, int keys)
1848 {
1849         switch (id)
1850         {
1851         case MA_MAIN_RESUME_GAME:
1852                 if (ready_to_go)
1853                         return 1;
1854                 break;
1855         case MA_MAIN_SAVE_STATE:
1856                 if (ready_to_go)
1857                         return menu_loop_savestate(0);
1858                 break;
1859         case MA_MAIN_LOAD_STATE:
1860                 if (ready_to_go)
1861                         return menu_loop_savestate(1);
1862                 break;
1863         case MA_MAIN_RESET_GAME:
1864                 if (ready_to_go && reset_game() == 0)
1865                         return 1;
1866                 break;
1867         case MA_MAIN_LOAD_ROM:
1868                 if (romsel_run() == 0)
1869                         return 1;
1870                 break;
1871         case MA_MAIN_SWAP_CD:
1872                 if (swap_cd_image() == 0)
1873                         return 1;
1874                 break;
1875         case MA_MAIN_SWAP_CD_MULTI:
1876                 if (swap_cd_multidisk() == 0)
1877                         return 1;
1878                 break;
1879         case MA_MAIN_RUN_BIOS:
1880                 if (run_bios() == 0)
1881                         return 1;
1882                 break;
1883         case MA_MAIN_RUN_EXE:
1884                 if (run_exe() == 0)
1885                         return 1;
1886                 break;
1887         case MA_MAIN_CREDITS:
1888                 draw_menu_message(credits_text, draw_frame_credits);
1889                 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1890                 break;
1891         case MA_MAIN_EXIT:
1892                 OnFile_Exit();
1893                 break;
1894         default:
1895                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1896                 break;
1897         }
1898
1899         return 0;
1900 }
1901
1902 static menu_entry e_menu_main2[] =
1903 {
1904         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
1905         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
1906         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
1907         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
1908         mee_handler   ("Memcard manager",    menu_loop_memcards),
1909         mee_end,
1910 };
1911
1912 static int main_menu2_handler(int id, int keys)
1913 {
1914         static int sel = 0;
1915
1916         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
1917         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
1918         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1919
1920         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1921 }
1922
1923 static const char h_extra[] = "Change CD, manage memcards..\n";
1924
1925 static menu_entry e_menu_main[] =
1926 {
1927         mee_label     (""),
1928         mee_label     (""),
1929         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
1930         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
1931         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
1932         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
1933         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
1934         mee_handler   ("Options",            menu_loop_options),
1935         mee_handler   ("Controls",           menu_loop_keyconfig),
1936         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
1937         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
1938         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
1939         mee_end,
1940 };
1941
1942 // ----------------------------
1943
1944 static void menu_leave_emu(void);
1945
1946 void menu_loop(void)
1947 {
1948         static int sel = 0;
1949
1950         menu_leave_emu();
1951
1952         if (bioses[1] == NULL && !warned_about_bios) {
1953                 menu_bios_warn();
1954                 warned_about_bios = 1;
1955         }
1956
1957         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1958         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
1959         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
1960         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
1961
1962         in_set_config_int(0, IN_CFG_BLOCKING, 1);
1963
1964         do {
1965                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
1966         } while (!ready_to_go);
1967
1968         /* wait until menu, ok, back is released */
1969         while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1970                 ;
1971
1972         in_set_config_int(0, IN_CFG_BLOCKING, 0);
1973
1974         menu_prepare_emu();
1975 }
1976
1977 static int qsort_strcmp(const void *p1, const void *p2)
1978 {
1979         char * const *s1 = (char * const *)p1;
1980         char * const *s2 = (char * const *)p2;
1981         return strcasecmp(*s1, *s2);
1982 }
1983
1984 static void scan_bios_plugins(void)
1985 {
1986         char fname[MAXPATHLEN];
1987         struct dirent *ent;
1988         int bios_i, gpu_i, spu_i, mc_i;
1989         char *p;
1990         DIR *dir;
1991
1992         bioses[0] = "HLE";
1993         gpu_plugins[0] = "builtin_gpu";
1994         spu_plugins[0] = "builtin_spu";
1995         memcards[0] = "(none)";
1996         bios_i = gpu_i = spu_i = mc_i = 1;
1997
1998         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1999         dir = opendir(fname);
2000         if (dir == NULL) {
2001                 perror("scan_bios_plugins bios opendir");
2002                 goto do_plugins;
2003         }
2004
2005         while (1) {
2006                 struct stat st;
2007
2008                 errno = 0;
2009                 ent = readdir(dir);
2010                 if (ent == NULL) {
2011                         if (errno != 0)
2012                                 perror("readdir");
2013                         break;
2014                 }
2015
2016                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2017                         continue;
2018
2019                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2020                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2021                         printf("bad BIOS file: %s\n", ent->d_name);
2022                         continue;
2023                 }
2024
2025                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2026                         bioses[bios_i++] = strdup(ent->d_name);
2027                         continue;
2028                 }
2029
2030                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2031         }
2032
2033         closedir(dir);
2034
2035 do_plugins:
2036         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2037         dir = opendir(fname);
2038         if (dir == NULL) {
2039                 perror("scan_bios_plugins plugins opendir");
2040                 goto do_memcards;
2041         }
2042
2043         while (1) {
2044                 void *h, *tmp;
2045
2046                 errno = 0;
2047                 ent = readdir(dir);
2048                 if (ent == NULL) {
2049                         if (errno != 0)
2050                                 perror("readdir");
2051                         break;
2052                 }
2053                 p = strstr(ent->d_name, ".so");
2054                 if (p == NULL)
2055                         continue;
2056
2057                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2058                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2059                 if (h == NULL) {
2060                         fprintf(stderr, "%s\n", dlerror());
2061                         continue;
2062                 }
2063
2064                 // now what do we have here?
2065                 tmp = dlsym(h, "GPUinit");
2066                 if (tmp) {
2067                         dlclose(h);
2068                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2069                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2070                         continue;
2071                 }
2072
2073                 tmp = dlsym(h, "SPUinit");
2074                 if (tmp) {
2075                         dlclose(h);
2076                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2077                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2078                         continue;
2079                 }
2080
2081                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2082                 dlclose(h);
2083         }
2084
2085         closedir(dir);
2086
2087 do_memcards:
2088         dir = opendir("." MEMCARD_DIR);
2089         if (dir == NULL) {
2090                 perror("scan_bios_plugins memcards opendir");
2091                 return;
2092         }
2093
2094         while (1) {
2095                 struct stat st;
2096
2097                 errno = 0;
2098                 ent = readdir(dir);
2099                 if (ent == NULL) {
2100                         if (errno != 0)
2101                                 perror("readdir");
2102                         break;
2103                 }
2104
2105                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2106                         continue;
2107
2108                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2109                 if (stat(fname, &st) != 0) {
2110                         printf("bad memcard file: %s\n", ent->d_name);
2111                         continue;
2112                 }
2113
2114                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2115                         memcards[mc_i++] = strdup(ent->d_name);
2116                         continue;
2117                 }
2118
2119                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2120         }
2121
2122         if (mc_i > 2)
2123                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2124
2125         closedir(dir);
2126 }
2127
2128 void menu_init(void)
2129 {
2130         char buff[MAXPATHLEN];
2131
2132         strcpy(last_selected_fname, "/media");
2133
2134         scan_bios_plugins();
2135         pnd_menu_init();
2136         menu_init_common();
2137
2138         menu_set_defconfig();
2139         menu_load_config(0);
2140         last_psx_w = 320;
2141         last_psx_h = 240;
2142         last_psx_bpp = 16;
2143
2144         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2145         if (g_menubg_src_ptr == NULL)
2146                 exit(1);
2147         emu_make_path(buff, "skin/background.png", sizeof(buff));
2148         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2149
2150 #ifndef __ARM_ARCH_7A__ /* XXX */
2151         me_enable(e_menu_options, MA_OPT_DISP_OPTS, 0);
2152         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2153 #else
2154         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
2155         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2156 #endif
2157 }
2158
2159 void menu_notify_mode_change(int w, int h, int bpp)
2160 {
2161         float mult;
2162         int imult;
2163
2164         last_psx_w = w;
2165         last_psx_h = h;
2166         last_psx_bpp = bpp;
2167
2168         switch (scaling) {
2169         case SCALE_1_1:
2170                 g_layer_w = w; g_layer_h = h;
2171                 break;
2172
2173         case SCALE_4_3v2:
2174                 if (h > g_menuscreen_h || (240 < h && h <= 360))
2175                         goto fractional_4_3;
2176
2177                 // 4:3 that prefers integer scaling
2178                 imult = g_menuscreen_h / h;
2179                 g_layer_w = w * imult;
2180                 g_layer_h = h * imult;
2181                 mult = (float)g_layer_w / (float)g_layer_h;
2182                 if (mult < 1.25f || mult > 1.666f)
2183                         g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2184                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2185                 break;
2186
2187         fractional_4_3:
2188         case SCALE_4_3:
2189                 mult = 240.0f / (float)h * 4.0f / 3.0f;
2190                 if (h > 256)
2191                         mult *= 2.0f;
2192                 g_layer_w = mult * (float)g_menuscreen_h;
2193                 g_layer_h = g_menuscreen_h;
2194                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2195                 break;
2196
2197         case SCALE_FULLSCREEN:
2198                 g_layer_w = g_menuscreen_w;
2199                 g_layer_h = g_menuscreen_h;
2200                 break;
2201
2202         default:
2203                 break;
2204         }
2205
2206         g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2207         g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2208         if (g_layer_x < 0) g_layer_x = 0;
2209         if (g_layer_y < 0) g_layer_y = 0;
2210         if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2211         if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2212 }
2213
2214 static void menu_leave_emu(void)
2215 {
2216         if (GPU_close != NULL) {
2217                 int ret = GPU_close();
2218                 if (ret)
2219                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2220         }
2221
2222         plat_video_menu_enter(ready_to_go);
2223
2224         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2225         if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
2226                 int x = max(0, g_menuscreen_w - last_psx_w);
2227                 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2228                 int w = min(g_menuscreen_w, last_psx_w);
2229                 int h = min(g_menuscreen_h, last_psx_h);
2230                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2231                 u16 *s = pl_vout_buf;
2232
2233                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
2234                         menu_darken_bg(d, s, w, 0);
2235         }
2236
2237         if (ready_to_go)
2238                 cpu_clock = plat_cpu_clock_get();
2239 }
2240
2241 void menu_prepare_emu(void)
2242 {
2243         R3000Acpu *prev_cpu = psxCpu;
2244
2245         plat_video_menu_leave();
2246
2247         menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2248
2249         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2250         if (psxCpu != prev_cpu)
2251                 // note that this does not really reset, just clears drc caches
2252                 psxCpu->Reset();
2253
2254         // core doesn't care about Config.Cdda changes,
2255         // so handle them manually here
2256         if (Config.Cdda)
2257                 CDR_stop();
2258
2259         menu_sync_config();
2260         apply_lcdrate(Config.PsxType);
2261         apply_filter(filter);
2262         plat_cpu_clock_apply(cpu_clock);
2263
2264         // push config to GPU plugin
2265         plugin_call_rearmed_cbs();
2266
2267         if (GPU_open != NULL) {
2268                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2269                 if (ret)
2270                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2271         }
2272
2273         dfinput_activate();
2274 }
2275
2276 void me_update_msg(const char *msg)
2277 {
2278         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2279         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2280
2281         menu_error_time = plat_get_ticks_ms();
2282         lprintf("msg: %s\n", menu_error_msg);
2283 }
2284