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