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