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