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