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