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