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