spu: some misc refactoring/cleanup
[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_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1087 static const char h_spu_irq_wait[]  = "Wait for CPU (recommended set to ON)";
1088 static const char h_spu_thread[]    = "Run sound emulation in main thread (recommended)";
1089
1090 static menu_entry e_menu_plugin_spu[] =
1091 {
1092         mee_onoff     ("Reverb",                    0, iUseReverb, 2),
1093         mee_enum      ("Interpolation",             0, iUseInterpolation, men_spu_interp),
1094         mee_onoff     ("Adjust XA pitch",           0, iXAPitch, 1),
1095         mee_onoff_h   ("SPU IRQ Wait",              0, iSPUIRQWait, 1, h_spu_irq_wait),
1096         mee_onoff_h   ("Sound in main thread",      0, iUseTimer, 2, h_spu_thread),
1097         mee_end,
1098 };
1099
1100 static int menu_loop_plugin_spu(int id, int keys)
1101 {
1102         static int sel = 0;
1103         me_loop(e_menu_plugin_spu, &sel);
1104         return 0;
1105 }
1106
1107 static const char h_bios[]       = "HLE is simulated BIOS. BIOS selection is saved in savestates\n"
1108                                    "and can't be changed there. Must save config and reload\n"
1109                                    "the game for change to take effect";
1110 static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1111                                    "for plugin change to take effect";
1112 static const char h_gpu[]        = "Configure P.E.Op.S. SoftGL Driver V1.17";
1113 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1114
1115 static menu_entry e_menu_plugin_options[] =
1116 {
1117         mee_enum_h    ("BIOS",                          0, bios_sel, bioses, h_bios),
1118         mee_enum_h    ("GPU plugin",                    0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1119         mee_enum_h    ("SPU plugin",                    0, spu_plugsel, spu_plugins, h_plugin_xpu),
1120         mee_handler_h ("Configure gpu_peops plugin",    menu_loop_plugin_gpu, h_gpu),
1121         mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1122         mee_end,
1123 };
1124
1125 static menu_entry e_menu_main2[];
1126
1127 static int menu_loop_plugin_options(int id, int keys)
1128 {
1129         static int sel = 0;
1130         me_loop(e_menu_plugin_options, &sel);
1131
1132         // sync BIOS/plugins
1133         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1134         snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1135         snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1136         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1137
1138         return 0;
1139 }
1140
1141 // ------------ adv options menu ------------
1142
1143 static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
1144 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
1145 static const char h_cfg_spu[]    = "Shows active SPU channels\n"
1146                                    "(green: normal, red: fmod, blue: noise)";
1147 static const char h_cfg_fl[]     = "Frame Limiter keeps the game from running too fast";
1148 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
1149 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
1150                                    "(proper .cue/.bin dump is needed otherwise)";
1151 static const char h_cfg_sio[]    = "You should not need this, breaks games";
1152 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1153 static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1154                                    "(timing hack, breaks other games)";
1155 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix\n"
1156                                    "(timing hack, breaks other games)";
1157 static const char h_cfg_cdrr[]   = "Compatibility tweak (fixes Team Buddies, maybe more)\n"
1158                                    "(CD timing hack, breaks FMVs)";
1159 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
1160                                    "Might be useful to overcome some dynarec bugs";
1161
1162 static menu_entry e_menu_adv_options[] =
1163 {
1164         mee_onoff_h   ("Show CPU load",          0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1165         mee_onoff_h   ("Show SPU channels",      0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1166         mee_onoff_h   ("Disable Frame Limiter",  0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1167         mee_onoff_h   ("Disable XA Decoding",    0, Config.Xa, 1, h_cfg_xa),
1168         mee_onoff_h   ("Disable CD Audio",       0, Config.Cdda, 1, h_cfg_cdda),
1169         mee_onoff_h   ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1170         mee_onoff_h   ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1171         mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
1172         mee_onoff_h   ("Rootcounter hack 2",     0, Config.VSyncWA, 1, h_cfg_rcnt2),
1173         mee_enum_h    ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
1174         mee_onoff_h   ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1175         mee_end,
1176 };
1177
1178 static int menu_loop_adv_options(int id, int keys)
1179 {
1180         static int sel = 0;
1181         me_loop(e_menu_adv_options, &sel);
1182         return 0;
1183 }
1184
1185 // ------------ options menu ------------
1186
1187 static int mh_restore_defaults(int id, int keys)
1188 {
1189         menu_set_defconfig();
1190         me_update_msg("defaults restored");
1191         return 1;
1192 }
1193
1194 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
1195 /*
1196 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1197 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
1198                                         "loading state or both";
1199 */
1200 static const char h_restore_def[]     = "Switches back to default / recommended\n"
1201                                         "configuration";
1202 static const char h_frameskip[]       = "Warning: frameskip sometimes causes glitches\n";
1203
1204 static menu_entry e_menu_options[] =
1205 {
1206 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1207 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1208         mee_onoff_h   ("Frameskip",                0, pl_rearmed_cbs.frameskip, 1, h_frameskip),
1209         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1210         mee_enum      ("Region",                   0, region, men_region),
1211         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1212         mee_handler   ("[Display]",                menu_loop_gfx_options),
1213         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1214         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1215         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1216         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1217         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1218         mee_end,
1219 };
1220
1221 static int menu_loop_options(int id, int keys)
1222 {
1223         static int sel = 0;
1224         int i;
1225
1226         i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1227         e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1228         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1229
1230         me_loop(e_menu_options, &sel);
1231
1232         return 0;
1233 }
1234
1235 // ------------ debug menu ------------
1236
1237 static void draw_frame_debug(GPUFreeze_t *gpuf)
1238 {
1239         int w = min(g_menuscreen_w, 1024);
1240         int h = min(g_menuscreen_h, 512);
1241         u16 *d = g_menuscreen_ptr;
1242         u16 *s = (u16 *)gpuf->psxVRam;
1243         char buff[64];
1244         int ty = 1;
1245
1246         gpuf->ulFreezeVersion = 1;
1247         if (GPU_freeze != NULL)
1248                 GPU_freeze(1, gpuf);
1249
1250         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1251                 bgr555_to_rgb565(d, s, w * 2);
1252
1253         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1254         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1255         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1256         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1257         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1258 }
1259
1260 static void debug_menu_loop(void)
1261 {
1262         GPUFreeze_t *gpuf;
1263         int inp;
1264
1265         gpuf = malloc(sizeof(*gpuf));
1266         if (gpuf == NULL)
1267                 return;
1268
1269         while (1)
1270         {
1271                 menu_draw_begin(0);
1272                 draw_frame_debug(gpuf);
1273                 menu_draw_end();
1274
1275                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1276                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1277                 if (inp & PBTN_MBACK)
1278                         break;
1279         }
1280
1281         free(gpuf);
1282 }
1283
1284 // --------- memcard manager ---------
1285
1286 static void draw_mc_icon(int dx, int dy, const u16 *s)
1287 {
1288         u16 *d;
1289         int x, y, l, p;
1290         
1291         d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1292
1293         for (y = 0; y < 16; y++, s += 16) {
1294                 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1295                         for (x = 0; x < 16; x++) {
1296                                 p = s[x];
1297                                 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1298                                         | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1299                         }
1300                 }
1301         }
1302 }
1303
1304 static void draw_mc_bg(void)
1305 {
1306         McdBlock *blocks1, *blocks2;
1307         int maxicons = 15;
1308         int i, y, row2;
1309
1310         blocks1 = malloc(15 * sizeof(blocks1[0]));
1311         blocks2 = malloc(15 * sizeof(blocks1[0]));
1312         if (blocks1 == NULL || blocks2 == NULL)
1313                 goto out;
1314
1315         for (i = 0; i < 15; i++) {
1316                 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1317                 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1318         }
1319
1320         menu_draw_begin(1);
1321
1322         memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1323
1324         y = g_menuscreen_h / 2 - 15 * 32 / 2;
1325         if (y < 0) {
1326                 // doesn't fit..
1327                 y = 0;
1328                 maxicons = g_menuscreen_h / 32;
1329         }
1330
1331         row2 = g_menuscreen_w / 2;
1332         for (i = 0; i < maxicons; i++) {
1333                 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1334                 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1335
1336                 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1337                 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1338         }
1339
1340         menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1341
1342         menu_draw_end();
1343 out:
1344         free(blocks1);
1345         free(blocks2);
1346 }
1347
1348 static void handle_memcard_sel(void)
1349 {
1350         Config.Mcd1[0] = 0;
1351         if (memcard1_sel != 0)
1352                 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1353         Config.Mcd2[0] = 0;
1354         if (memcard2_sel != 0)
1355                 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1356         LoadMcds(Config.Mcd1, Config.Mcd2);
1357         draw_mc_bg();
1358 }
1359
1360 static menu_entry e_memcard_options[] =
1361 {
1362         mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1363         mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1364         mee_end,
1365 };
1366
1367 static int menu_loop_memcards(int id, int keys)
1368 {
1369         static int sel = 0;
1370         char *p;
1371         int i;
1372
1373         memcard1_sel = memcard2_sel = 0;
1374         p = strrchr(Config.Mcd1, '/');
1375         if (p != NULL)
1376                 for (i = 0; memcards[i] != NULL; i++)
1377                         if (strcmp(p + 1, memcards[i]) == 0)
1378                                 { memcard1_sel = i; break; }
1379         p = strrchr(Config.Mcd2, '/');
1380         if (p != NULL)
1381                 for (i = 0; memcards[i] != NULL; i++)
1382                         if (strcmp(p + 1, memcards[i]) == 0)
1383                                 { memcard2_sel = i; break; }
1384
1385         me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1386
1387         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1388
1389         return 0;
1390 }
1391
1392 // --------- main menu help ----------
1393
1394 static void menu_bios_warn(void)
1395 {
1396         int inp;
1397         static const char msg[] =
1398                 "You don't seem to have copied any BIOS files to\n"
1399                 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1400                 "While many games work fine with fake (HLE) BIOS,\n"
1401                 "others (like MGS and FF8) require BIOS to work.\n"
1402                 "After copying the file, you'll also need to\n"
1403                 "select it in the emu's options->[BIOS/Plugins]\n\n"
1404                 "The file is usually named SCPH1001.BIN, but\n"
1405                 "other not compressed files can be used too.\n\n"
1406                 "Press (B) or (X) to continue";
1407
1408         while (1)
1409         {
1410                 draw_menu_message(msg, NULL);
1411
1412                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1413                 if (inp & (PBTN_MBACK|PBTN_MOK))
1414                         return;
1415         }
1416 }
1417
1418 // ------------ main menu ------------
1419
1420 void OnFile_Exit();
1421
1422 static void draw_frame_main(void)
1423 {
1424         if (CdromId[0] != 0) {
1425                 char buff[64];
1426                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1427                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1428                          Config.HLE ? "HLE" : "BIOS");
1429                 smalltext_out16(4, 1, buff, 0x105f);
1430         }
1431 }
1432
1433 static void draw_frame_credits(void)
1434 {
1435         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1436 }
1437
1438 static const char credits_text[] = 
1439         "PCSX-ReARMed\n\n"
1440         "(C) 1999-2003 PCSX Team\n"
1441         "(C) 2005-2009 PCSX-df Team\n"
1442         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1443         "GPU and SPU code by Pete Bernert\n"
1444         "  and the P.E.Op.S. team\n"
1445         "ARM recompiler (C) 2009-2011 Ari64\n"
1446         "PCSX4ALL plugins by PCSX4ALL team\n"
1447         "  Chui, Franxis, Unai\n\n"
1448         "integration, optimization and\n"
1449         "  frontend (C) 2010-2011 notaz\n";
1450
1451 static int reset_game(void)
1452 {
1453         // sanity check
1454         if (bios_sel == 0 && !Config.HLE)
1455                 return -1;
1456
1457         ClosePlugins();
1458         OpenPlugins();
1459         SysReset();
1460         if (CheckCdrom() != -1) {
1461                 LoadCdrom();
1462         }
1463         return 0;
1464 }
1465
1466 static int reload_plugins(const char *cdimg)
1467 {
1468         pl_fbdev_buf = NULL;
1469
1470         ClosePlugins();
1471
1472         set_cd_image(cdimg);
1473         LoadPlugins();
1474         pcnt_hook_plugins();
1475         NetOpened = 0;
1476         if (OpenPlugins() == -1) {
1477                 me_update_msg("failed to open plugins");
1478                 return -1;
1479         }
1480         plugin_call_rearmed_cbs();
1481
1482         CdromId[0] = '\0';
1483         CdromLabel[0] = '\0';
1484
1485         return 0;
1486 }
1487
1488 static int run_bios(void)
1489 {
1490         if (bios_sel == 0)
1491                 return -1;
1492
1493         ready_to_go = 0;
1494         if (reload_plugins(NULL) != 0)
1495                 return -1;
1496         SysReset();
1497
1498         ready_to_go = 1;
1499         return 0;
1500 }
1501
1502 static int run_exe(void)
1503 {
1504         const char *fname;
1505
1506         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1507         if (fname == NULL)
1508                 return -1;
1509
1510         ready_to_go = 0;
1511         if (reload_plugins(NULL) != 0)
1512                 return -1;
1513
1514         SysReset();
1515         if (Load(fname) != 0) {
1516                 me_update_msg("exe load failed, bad file?");
1517                 printf("meh\n");
1518                 return -1;
1519         }
1520
1521         ready_to_go = 1;
1522         return 0;
1523 }
1524
1525 static int run_cd_image(const char *fname)
1526 {
1527         ready_to_go = 0;
1528         reload_plugins(fname);
1529
1530         if (CheckCdrom() == -1) {
1531                 // Only check the CD if we are starting the console with a CD
1532                 ClosePlugins();
1533                 me_update_msg("unsupported/invalid CD image");
1534                 return -1;
1535         }
1536
1537         SysReset();
1538
1539         // Read main executable directly from CDRom and start it
1540         if (LoadCdrom() == -1) {
1541                 ClosePlugins();
1542                 me_update_msg("failed to load CD image");
1543                 return -1;
1544         }
1545
1546         ready_to_go = 1;
1547         return 0;
1548 }
1549
1550 static int romsel_run(void)
1551 {
1552         int prev_gpu, prev_spu;
1553         const char *fname;
1554
1555         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1556         if (fname == NULL)
1557                 return -1;
1558
1559         printf("selected file: %s\n", fname);
1560
1561         new_dynarec_clear_full();
1562
1563         if (run_cd_image(fname) != 0)
1564                 return -1;
1565
1566         prev_gpu = gpu_plugsel;
1567         prev_spu = spu_plugsel;
1568         if (menu_load_config(1) != 0)
1569                 menu_load_config(0);
1570
1571         // check for plugin changes, have to repeat
1572         // loading if game config changed plugins to reload them
1573         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1574                 printf("plugin change detected, reloading plugins..\n");
1575                 if (run_cd_image(fname) != 0)
1576                         return -1;
1577         }
1578
1579         strcpy(last_selected_fname, rom_fname_reload);
1580         return 0;
1581 }
1582
1583 static int swap_cd_image(void)
1584 {
1585         char *fname;
1586
1587         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1588         if (fname == NULL)
1589                 return -1;
1590
1591         printf("selected file: %s\n", fname);
1592
1593         CdromId[0] = '\0';
1594         CdromLabel[0] = '\0';
1595
1596         set_cd_image(fname);
1597         if (ReloadCdromPlugin() < 0) {
1598                 me_update_msg("failed to load cdr plugin");
1599                 return -1;
1600         }
1601         if (CDR_open() < 0) {
1602                 me_update_msg("failed to open cdr plugin");
1603                 return -1;
1604         }
1605
1606         SetCdOpenCaseTime(time(NULL) + 2);
1607         LidInterrupt();
1608
1609         strcpy(last_selected_fname, rom_fname_reload);
1610         return 0;
1611 }
1612
1613 static int main_menu_handler(int id, int keys)
1614 {
1615         switch (id)
1616         {
1617         case MA_MAIN_RESUME_GAME:
1618                 if (ready_to_go)
1619                         return 1;
1620                 break;
1621         case MA_MAIN_SAVE_STATE:
1622                 if (ready_to_go)
1623                         return menu_loop_savestate(0);
1624                 break;
1625         case MA_MAIN_LOAD_STATE:
1626                 if (ready_to_go)
1627                         return menu_loop_savestate(1);
1628                 break;
1629         case MA_MAIN_RESET_GAME:
1630                 if (ready_to_go && reset_game() == 0)
1631                         return 1;
1632                 break;
1633         case MA_MAIN_LOAD_ROM:
1634                 if (romsel_run() == 0)
1635                         return 1;
1636                 break;
1637         case MA_MAIN_SWAP_CD:
1638                 if (swap_cd_image() == 0)
1639                         return 1;
1640                 break;
1641         case MA_MAIN_RUN_BIOS:
1642                 if (run_bios() == 0)
1643                         return 1;
1644                 break;
1645         case MA_MAIN_RUN_EXE:
1646                 if (run_exe() == 0)
1647                         return 1;
1648                 break;
1649         case MA_MAIN_CREDITS:
1650                 draw_menu_message(credits_text, draw_frame_credits);
1651                 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1652                 break;
1653         case MA_MAIN_EXIT:
1654                 OnFile_Exit();
1655                 break;
1656         default:
1657                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1658                 break;
1659         }
1660
1661         return 0;
1662 }
1663
1664 static menu_entry e_menu_main2[] =
1665 {
1666         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,     main_menu_handler),
1667         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,    main_menu_handler),
1668         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,     main_menu_handler),
1669         mee_handler   ("Memcard manager",    menu_loop_memcards),
1670         mee_end,
1671 };
1672
1673 static int main_menu2_handler(int id, int keys)
1674 {
1675         static int sel = 0;
1676
1677         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
1678         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1679
1680         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1681 }
1682
1683 static const char h_extra[] = "Change CD, manage memcards..\n";
1684
1685 static menu_entry e_menu_main[] =
1686 {
1687         mee_label     (""),
1688         mee_label     (""),
1689         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
1690         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
1691         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
1692         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
1693         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
1694         mee_handler   ("Options",            menu_loop_options),
1695         mee_handler   ("Controls",           menu_loop_keyconfig),
1696         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
1697         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
1698         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
1699         mee_end,
1700 };
1701
1702 // ----------------------------
1703
1704 static void menu_leave_emu(void);
1705
1706 void menu_loop(void)
1707 {
1708         static int sel = 0;
1709
1710         menu_leave_emu();
1711
1712         if (bioses[1] == NULL && !warned_about_bios) {
1713                 menu_bios_warn();
1714                 warned_about_bios = 1;
1715         }
1716
1717         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1718         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
1719         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
1720         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
1721
1722         in_set_config_int(0, IN_CFG_BLOCKING, 1);
1723
1724         do {
1725                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
1726         } while (!ready_to_go);
1727
1728         /* wait until menu, ok, back is released */
1729         while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1730                 ;
1731
1732         in_set_config_int(0, IN_CFG_BLOCKING, 0);
1733
1734         menu_prepare_emu();
1735 }
1736
1737 static int qsort_strcmp(const void *p1, const void *p2)
1738 {
1739         char * const *s1 = (char * const *)p1;
1740         char * const *s2 = (char * const *)p2;
1741         return strcasecmp(*s1, *s2);
1742 }
1743
1744 static void scan_bios_plugins(void)
1745 {
1746         char fname[MAXPATHLEN];
1747         struct dirent *ent;
1748         int bios_i, gpu_i, spu_i, mc_i;
1749         char *p;
1750         DIR *dir;
1751
1752         bioses[0] = "HLE";
1753         gpu_plugins[0] = "builtin_gpu";
1754         spu_plugins[0] = "builtin_spu";
1755         memcards[0] = "(none)";
1756         bios_i = gpu_i = spu_i = mc_i = 1;
1757
1758         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1759         dir = opendir(fname);
1760         if (dir == NULL) {
1761                 perror("scan_bios_plugins bios opendir");
1762                 goto do_plugins;
1763         }
1764
1765         while (1) {
1766                 struct stat st;
1767
1768                 errno = 0;
1769                 ent = readdir(dir);
1770                 if (ent == NULL) {
1771                         if (errno != 0)
1772                                 perror("readdir");
1773                         break;
1774                 }
1775
1776                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1777                         continue;
1778
1779                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1780                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1781                         printf("bad BIOS file: %s\n", ent->d_name);
1782                         continue;
1783                 }
1784
1785                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1786                         bioses[bios_i++] = strdup(ent->d_name);
1787                         continue;
1788                 }
1789
1790                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1791         }
1792
1793         closedir(dir);
1794
1795 do_plugins:
1796         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1797         dir = opendir(fname);
1798         if (dir == NULL) {
1799                 perror("scan_bios_plugins plugins opendir");
1800                 goto do_memcards;
1801         }
1802
1803         while (1) {
1804                 void *h, *tmp;
1805
1806                 errno = 0;
1807                 ent = readdir(dir);
1808                 if (ent == NULL) {
1809                         if (errno != 0)
1810                                 perror("readdir");
1811                         break;
1812                 }
1813                 p = strstr(ent->d_name, ".so");
1814                 if (p == NULL)
1815                         continue;
1816
1817                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1818                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1819                 if (h == NULL) {
1820                         fprintf(stderr, "%s\n", dlerror());
1821                         continue;
1822                 }
1823
1824                 // now what do we have here?
1825                 tmp = dlsym(h, "GPUinit");
1826                 if (tmp) {
1827                         dlclose(h);
1828                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1829                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1830                         continue;
1831                 }
1832
1833                 tmp = dlsym(h, "SPUinit");
1834                 if (tmp) {
1835                         dlclose(h);
1836                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1837                                 spu_plugins[spu_i++] = strdup(ent->d_name);
1838                         continue;
1839                 }
1840
1841                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1842                 dlclose(h);
1843         }
1844
1845         closedir(dir);
1846
1847 do_memcards:
1848         dir = opendir("." MEMCARD_DIR);
1849         if (dir == NULL) {
1850                 perror("scan_bios_plugins memcards opendir");
1851                 return;
1852         }
1853
1854         while (1) {
1855                 struct stat st;
1856
1857                 errno = 0;
1858                 ent = readdir(dir);
1859                 if (ent == NULL) {
1860                         if (errno != 0)
1861                                 perror("readdir");
1862                         break;
1863                 }
1864
1865                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1866                         continue;
1867
1868                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
1869                 if (stat(fname, &st) != 0) {
1870                         printf("bad memcard file: %s\n", ent->d_name);
1871                         continue;
1872                 }
1873
1874                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
1875                         memcards[mc_i++] = strdup(ent->d_name);
1876                         continue;
1877                 }
1878
1879                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
1880         }
1881
1882         if (mc_i > 2)
1883                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
1884
1885         closedir(dir);
1886 }
1887
1888 void menu_init(void)
1889 {
1890         char buff[MAXPATHLEN];
1891
1892         strcpy(last_selected_fname, "/media");
1893
1894         scan_bios_plugins();
1895         pnd_menu_init();
1896         menu_init_common();
1897
1898         menu_set_defconfig();
1899         menu_load_config(0);
1900         last_psx_w = 320;
1901         last_psx_h = 240;
1902         last_psx_bpp = 16;
1903
1904         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1905         if (g_menubg_src_ptr == NULL)
1906                 exit(1);
1907         emu_make_path(buff, "skin/background.png", sizeof(buff));
1908         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1909 }
1910
1911 void menu_notify_mode_change(int w, int h, int bpp)
1912 {
1913         last_psx_w = w;
1914         last_psx_h = h;
1915         last_psx_bpp = bpp;
1916
1917         if (scaling == SCALE_1_1) {
1918                 g_layer_x = 800/2 - w/2;  g_layer_y = 480/2 - h/2;
1919                 g_layer_w = w; g_layer_h = h;
1920         }
1921 }
1922
1923 static void menu_leave_emu(void)
1924 {
1925         if (GPU_close != NULL) {
1926                 int ret = GPU_close();
1927                 if (ret)
1928                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1929         }
1930
1931         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1932         if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
1933                 int x = max(0, g_menuscreen_w - last_psx_w);
1934                 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1935                 int w = min(g_menuscreen_w, last_psx_w);
1936                 int h = min(g_menuscreen_h, last_psx_h);
1937                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
1938                 u16 *s = pl_fbdev_buf;
1939
1940                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
1941                         menu_darken_bg(d, s, w, 0);
1942         }
1943
1944         if (ready_to_go)
1945                 cpu_clock = get_cpu_clock();
1946
1947         plat_video_menu_enter(ready_to_go);
1948 }
1949
1950 void menu_prepare_emu(void)
1951 {
1952         R3000Acpu *prev_cpu = psxCpu;
1953
1954         plat_video_menu_leave();
1955
1956         switch (scaling) {
1957         case SCALE_1_1:
1958                 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
1959                 break;
1960         case SCALE_4_3:
1961                 g_layer_x = 80;  g_layer_y = 0;
1962                 g_layer_w = 640; g_layer_h = 480;
1963                 break;
1964         case SCALE_FULLSCREEN:
1965                 g_layer_x = 0;   g_layer_y = 0;
1966                 g_layer_w = 800; g_layer_h = 480;
1967                 break;
1968         case SCALE_CUSTOM:
1969                 break;
1970         }
1971
1972         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1973         if (psxCpu != prev_cpu)
1974                 // note that this does not really reset, just clears drc caches
1975                 psxCpu->Reset();
1976
1977         // core doesn't care about Config.Cdda changes,
1978         // so handle them manually here
1979         if (Config.Cdda)
1980                 CDR_stop();
1981
1982         menu_sync_config();
1983         apply_lcdrate(Config.PsxType);
1984         apply_filter(filter);
1985         apply_cpu_clock();
1986
1987         // push config to GPU plugin
1988         plugin_call_rearmed_cbs();
1989
1990         if (GPU_open != NULL) {
1991                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1992                 if (ret)
1993                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1994         }
1995
1996         dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
1997 }
1998
1999 void me_update_msg(const char *msg)
2000 {
2001         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2002         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2003
2004         menu_error_time = plat_get_ticks_ms();
2005         lprintf("msg: %s\n", menu_error_msg);
2006 }
2007