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