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