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