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