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