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