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