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