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