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