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