add fixed frameskip option
[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(iUseReverb),
257         CE_INTVAL(iXAPitch),
258         CE_INTVAL_V(iUseInterpolation, 2),
259         CE_INTVAL_V(iSPUIRQWait, 2),
260         CE_INTVAL(iUseTimer),
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         if (CheckCdrom() == -1) {
1608                 // Only check the CD if we are starting the console with a CD
1609                 ClosePlugins();
1610                 me_update_msg("unsupported/invalid CD image");
1611                 return -1;
1612         }
1613
1614         SysReset();
1615
1616         // Read main executable directly from CDRom and start it
1617         if (LoadCdrom() == -1) {
1618                 ClosePlugins();
1619                 me_update_msg("failed to load CD image");
1620                 return -1;
1621         }
1622
1623         ready_to_go = 1;
1624         return 0;
1625 }
1626
1627 static int romsel_run(void)
1628 {
1629         int prev_gpu, prev_spu;
1630         const char *fname;
1631
1632         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1633         if (fname == NULL)
1634                 return -1;
1635
1636         printf("selected file: %s\n", fname);
1637
1638         new_dynarec_clear_full();
1639
1640         if (run_cd_image(fname) != 0)
1641                 return -1;
1642
1643         prev_gpu = gpu_plugsel;
1644         prev_spu = spu_plugsel;
1645         if (menu_load_config(1) != 0)
1646                 menu_load_config(0);
1647
1648         // check for plugin changes, have to repeat
1649         // loading if game config changed plugins to reload them
1650         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1651                 printf("plugin change detected, reloading plugins..\n");
1652                 if (run_cd_image(fname) != 0)
1653                         return -1;
1654         }
1655
1656         strcpy(last_selected_fname, rom_fname_reload);
1657         return 0;
1658 }
1659
1660 static int swap_cd_image(void)
1661 {
1662         char *fname;
1663
1664         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1665         if (fname == NULL)
1666                 return -1;
1667
1668         printf("selected file: %s\n", fname);
1669
1670         CdromId[0] = '\0';
1671         CdromLabel[0] = '\0';
1672
1673         set_cd_image(fname);
1674         if (ReloadCdromPlugin() < 0) {
1675                 me_update_msg("failed to load cdr plugin");
1676                 return -1;
1677         }
1678         if (CDR_open() < 0) {
1679                 me_update_msg("failed to open cdr plugin");
1680                 return -1;
1681         }
1682
1683         SetCdOpenCaseTime(time(NULL) + 2);
1684         LidInterrupt();
1685
1686         strcpy(last_selected_fname, rom_fname_reload);
1687         return 0;
1688 }
1689
1690 static int main_menu_handler(int id, int keys)
1691 {
1692         switch (id)
1693         {
1694         case MA_MAIN_RESUME_GAME:
1695                 if (ready_to_go)
1696                         return 1;
1697                 break;
1698         case MA_MAIN_SAVE_STATE:
1699                 if (ready_to_go)
1700                         return menu_loop_savestate(0);
1701                 break;
1702         case MA_MAIN_LOAD_STATE:
1703                 if (ready_to_go)
1704                         return menu_loop_savestate(1);
1705                 break;
1706         case MA_MAIN_RESET_GAME:
1707                 if (ready_to_go && reset_game() == 0)
1708                         return 1;
1709                 break;
1710         case MA_MAIN_LOAD_ROM:
1711                 if (romsel_run() == 0)
1712                         return 1;
1713                 break;
1714         case MA_MAIN_SWAP_CD:
1715                 if (swap_cd_image() == 0)
1716                         return 1;
1717                 break;
1718         case MA_MAIN_RUN_BIOS:
1719                 if (run_bios() == 0)
1720                         return 1;
1721                 break;
1722         case MA_MAIN_RUN_EXE:
1723                 if (run_exe() == 0)
1724                         return 1;
1725                 break;
1726         case MA_MAIN_CREDITS:
1727                 draw_menu_message(credits_text, draw_frame_credits);
1728                 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1729                 break;
1730         case MA_MAIN_EXIT:
1731                 OnFile_Exit();
1732                 break;
1733         default:
1734                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1735                 break;
1736         }
1737
1738         return 0;
1739 }
1740
1741 static menu_entry e_menu_main2[] =
1742 {
1743         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,     main_menu_handler),
1744         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,    main_menu_handler),
1745         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,     main_menu_handler),
1746         mee_handler   ("Memcard manager",    menu_loop_memcards),
1747         mee_end,
1748 };
1749
1750 static int main_menu2_handler(int id, int keys)
1751 {
1752         static int sel = 0;
1753
1754         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
1755         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1756
1757         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1758 }
1759
1760 static const char h_extra[] = "Change CD, manage memcards..\n";
1761
1762 static menu_entry e_menu_main[] =
1763 {
1764         mee_label     (""),
1765         mee_label     (""),
1766         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
1767         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
1768         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
1769         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
1770         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
1771         mee_handler   ("Options",            menu_loop_options),
1772         mee_handler   ("Controls",           menu_loop_keyconfig),
1773         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
1774         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
1775         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
1776         mee_end,
1777 };
1778
1779 // ----------------------------
1780
1781 static void menu_leave_emu(void);
1782
1783 void menu_loop(void)
1784 {
1785         static int sel = 0;
1786
1787         menu_leave_emu();
1788
1789         if (bioses[1] == NULL && !warned_about_bios) {
1790                 menu_bios_warn();
1791                 warned_about_bios = 1;
1792         }
1793
1794         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1795         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
1796         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
1797         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
1798
1799         in_set_config_int(0, IN_CFG_BLOCKING, 1);
1800
1801         do {
1802                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
1803         } while (!ready_to_go);
1804
1805         /* wait until menu, ok, back is released */
1806         while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1807                 ;
1808
1809         in_set_config_int(0, IN_CFG_BLOCKING, 0);
1810
1811         menu_prepare_emu();
1812 }
1813
1814 static int qsort_strcmp(const void *p1, const void *p2)
1815 {
1816         char * const *s1 = (char * const *)p1;
1817         char * const *s2 = (char * const *)p2;
1818         return strcasecmp(*s1, *s2);
1819 }
1820
1821 static void scan_bios_plugins(void)
1822 {
1823         char fname[MAXPATHLEN];
1824         struct dirent *ent;
1825         int bios_i, gpu_i, spu_i, mc_i;
1826         char *p;
1827         DIR *dir;
1828
1829         bioses[0] = "HLE";
1830         gpu_plugins[0] = "builtin_gpu";
1831         spu_plugins[0] = "builtin_spu";
1832         memcards[0] = "(none)";
1833         bios_i = gpu_i = spu_i = mc_i = 1;
1834
1835         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1836         dir = opendir(fname);
1837         if (dir == NULL) {
1838                 perror("scan_bios_plugins bios opendir");
1839                 goto do_plugins;
1840         }
1841
1842         while (1) {
1843                 struct stat st;
1844
1845                 errno = 0;
1846                 ent = readdir(dir);
1847                 if (ent == NULL) {
1848                         if (errno != 0)
1849                                 perror("readdir");
1850                         break;
1851                 }
1852
1853                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1854                         continue;
1855
1856                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1857                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1858                         printf("bad BIOS file: %s\n", ent->d_name);
1859                         continue;
1860                 }
1861
1862                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1863                         bioses[bios_i++] = strdup(ent->d_name);
1864                         continue;
1865                 }
1866
1867                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1868         }
1869
1870         closedir(dir);
1871
1872 do_plugins:
1873         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1874         dir = opendir(fname);
1875         if (dir == NULL) {
1876                 perror("scan_bios_plugins plugins opendir");
1877                 goto do_memcards;
1878         }
1879
1880         while (1) {
1881                 void *h, *tmp;
1882
1883                 errno = 0;
1884                 ent = readdir(dir);
1885                 if (ent == NULL) {
1886                         if (errno != 0)
1887                                 perror("readdir");
1888                         break;
1889                 }
1890                 p = strstr(ent->d_name, ".so");
1891                 if (p == NULL)
1892                         continue;
1893
1894                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1895                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1896                 if (h == NULL) {
1897                         fprintf(stderr, "%s\n", dlerror());
1898                         continue;
1899                 }
1900
1901                 // now what do we have here?
1902                 tmp = dlsym(h, "GPUinit");
1903                 if (tmp) {
1904                         dlclose(h);
1905                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1906                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1907                         continue;
1908                 }
1909
1910                 tmp = dlsym(h, "SPUinit");
1911                 if (tmp) {
1912                         dlclose(h);
1913                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1914                                 spu_plugins[spu_i++] = strdup(ent->d_name);
1915                         continue;
1916                 }
1917
1918                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1919                 dlclose(h);
1920         }
1921
1922         closedir(dir);
1923
1924 do_memcards:
1925         dir = opendir("." MEMCARD_DIR);
1926         if (dir == NULL) {
1927                 perror("scan_bios_plugins memcards opendir");
1928                 return;
1929         }
1930
1931         while (1) {
1932                 struct stat st;
1933
1934                 errno = 0;
1935                 ent = readdir(dir);
1936                 if (ent == NULL) {
1937                         if (errno != 0)
1938                                 perror("readdir");
1939                         break;
1940                 }
1941
1942                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1943                         continue;
1944
1945                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
1946                 if (stat(fname, &st) != 0) {
1947                         printf("bad memcard file: %s\n", ent->d_name);
1948                         continue;
1949                 }
1950
1951                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
1952                         memcards[mc_i++] = strdup(ent->d_name);
1953                         continue;
1954                 }
1955
1956                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
1957         }
1958
1959         if (mc_i > 2)
1960                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
1961
1962         closedir(dir);
1963 }
1964
1965 void menu_init(void)
1966 {
1967         char buff[MAXPATHLEN];
1968
1969         strcpy(last_selected_fname, "/media");
1970
1971         scan_bios_plugins();
1972         pnd_menu_init();
1973         menu_init_common();
1974
1975         menu_set_defconfig();
1976         menu_load_config(0);
1977         last_psx_w = 320;
1978         last_psx_h = 240;
1979         last_psx_bpp = 16;
1980
1981         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1982         if (g_menubg_src_ptr == NULL)
1983                 exit(1);
1984         emu_make_path(buff, "skin/background.png", sizeof(buff));
1985         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1986 }
1987
1988 void menu_notify_mode_change(int w, int h, int bpp)
1989 {
1990         float mult;
1991         int imult;
1992
1993         last_psx_w = w;
1994         last_psx_h = h;
1995         last_psx_bpp = bpp;
1996
1997         switch (scaling) {
1998         case SCALE_1_1:
1999                 g_layer_w = w; g_layer_h = h;
2000                 break;
2001
2002         case SCALE_4_3:
2003                 mult = 240.0f / (float)h * 4.0f / 3.0f;
2004                 if (h > 256)
2005                         mult *= 2.0f;
2006                 g_layer_w = mult * (float)g_menuscreen_h;
2007                 g_layer_h = g_menuscreen_h;
2008                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2009                 break;
2010
2011         case SCALE_4_3v2:
2012                 // 4:3 that prefers integer scaling
2013                 imult = g_menuscreen_h / h;
2014                 g_layer_w = w * imult;
2015                 g_layer_h = h * imult;
2016                 mult = (float)g_layer_w / (float)g_layer_h;
2017                 if (mult < 1.25f || mult > 1.666f)
2018                         g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2019                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2020                 break;
2021
2022         case SCALE_FULLSCREEN:
2023                 g_layer_w = g_menuscreen_w;
2024                 g_layer_h = g_menuscreen_h;
2025                 break;
2026
2027         default:
2028                 break;
2029         }
2030
2031         g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2032         g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2033         if (g_layer_x < 0) g_layer_x = 0;
2034         if (g_layer_y < 0) g_layer_y = 0;
2035         if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2036         if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2037 }
2038
2039 static void menu_leave_emu(void)
2040 {
2041         if (GPU_close != NULL) {
2042                 int ret = GPU_close();
2043                 if (ret)
2044                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2045         }
2046
2047         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2048         if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
2049                 int x = max(0, g_menuscreen_w - last_psx_w);
2050                 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2051                 int w = min(g_menuscreen_w, last_psx_w);
2052                 int h = min(g_menuscreen_h, last_psx_h);
2053                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2054                 u16 *s = pl_vout_buf;
2055
2056                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
2057                         menu_darken_bg(d, s, w, 0);
2058         }
2059
2060         if (ready_to_go)
2061                 cpu_clock = get_cpu_clock();
2062
2063         plat_video_menu_enter(ready_to_go);
2064 }
2065
2066 void menu_prepare_emu(void)
2067 {
2068         R3000Acpu *prev_cpu = psxCpu;
2069
2070         plat_video_menu_leave();
2071
2072         menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2073
2074         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2075         if (psxCpu != prev_cpu)
2076                 // note that this does not really reset, just clears drc caches
2077                 psxCpu->Reset();
2078
2079         // core doesn't care about Config.Cdda changes,
2080         // so handle them manually here
2081         if (Config.Cdda)
2082                 CDR_stop();
2083
2084         menu_sync_config();
2085         apply_lcdrate(Config.PsxType);
2086         apply_filter(filter);
2087         apply_cpu_clock();
2088
2089         // push config to GPU plugin
2090         plugin_call_rearmed_cbs();
2091
2092         if (GPU_open != NULL) {
2093                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2094                 if (ret)
2095                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2096         }
2097
2098         dfinput_activate();
2099 }
2100
2101 void me_update_msg(const char *msg)
2102 {
2103         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2104         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2105
2106         menu_error_time = plat_get_ticks_ms();
2107         lprintf("msg: %s\n", menu_error_msg);
2108 }
2109