dfinput: return 0xff when the packet ends, like the real thing does
[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 void apply_lcdrate(int pal)
571 {
572         static int old = -1;
573         char buf[128];
574
575         if (pal == old)
576                 return;
577
578         snprintf(buf, sizeof(buf), "%s/op_lcdrate.sh %d",
579                         pnd_script_base, pal ? 50 : 60);
580         system(buf);
581         old = pal;
582 }
583
584 static menu_entry e_menu_gfx_options[];
585
586 static void pnd_menu_init(void)
587 {
588         struct dirent *ent;
589         int i, count = 0;
590         char **mfilters;
591         char buff[64];
592         DIR *dir;
593
594         cpu_clock_st = cpu_clock = get_cpu_clock();
595
596         dir = opendir("/etc/pandora/conf/dss_fir");
597         if (dir == NULL) {
598                 perror("filter opendir");
599                 return;
600         }
601
602         while (1) {
603                 errno = 0;
604                 ent = readdir(dir);
605                 if (ent == NULL) {
606                         if (errno != 0)
607                                 perror("readdir");
608                         break;
609                 }
610
611                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
612                         continue;
613
614                 count++;
615         }
616
617         if (count == 0)
618                 return;
619
620         mfilters = calloc(count + 1, sizeof(mfilters[0]));
621         if (mfilters == NULL)
622                 return;
623
624         rewinddir(dir);
625         for (i = 0; (ent = readdir(dir)); ) {
626                 size_t len;
627
628                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
629                         continue;
630
631                 len = strlen(ent->d_name);
632
633                 // skip pre-HF5 extra files
634                 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
635                         continue;
636                 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
637                         continue;
638
639                 // have to cut "_up_h" for pre-HF5
640                 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
641                         len -= 5;
642
643                 if (len > sizeof(buff) - 1)
644                         continue;
645
646                 strncpy(buff, ent->d_name, len);
647                 buff[len] = 0;
648                 mfilters[i] = strdup(buff);
649                 if (mfilters[i] != NULL)
650                         i++;
651         }
652         closedir(dir);
653
654         i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
655         e_menu_gfx_options[i].data = (void *)mfilters;
656         pnd_filter_list = mfilters;
657 }
658
659 void menu_finish(void)
660 {
661         cpu_clock = cpu_clock_st;
662         apply_cpu_clock();
663 }
664
665 // -------------- key config --------------
666
667 me_bind_action me_ctrl_actions[] =
668 {
669         { "UP      ", 1 << DKEY_UP},
670         { "DOWN    ", 1 << DKEY_DOWN },
671         { "LEFT    ", 1 << DKEY_LEFT },
672         { "RIGHT   ", 1 << DKEY_RIGHT },
673         { "TRIANGLE", 1 << DKEY_TRIANGLE },
674         { "CIRCLE  ", 1 << DKEY_CIRCLE },
675         { "CROSS   ", 1 << DKEY_CROSS },
676         { "SQUARE  ", 1 << DKEY_SQUARE },
677         { "L1      ", 1 << DKEY_L1 },
678         { "R1      ", 1 << DKEY_R1 },
679         { "L2      ", 1 << DKEY_L2 },
680         { "R2      ", 1 << DKEY_R2 },
681         { "L3      ", 1 << DKEY_L3 },
682         { "R3      ", 1 << DKEY_R3 },
683         { "START   ", 1 << DKEY_START },
684         { "SELECT  ", 1 << DKEY_SELECT },
685         { NULL,       0 }
686 };
687
688 me_bind_action emuctrl_actions[] =
689 {
690         { "Save State       ", 1 << SACTION_SAVE_STATE },
691         { "Load State       ", 1 << SACTION_LOAD_STATE },
692         { "Prev Save Slot   ", 1 << SACTION_PREV_SSLOT },
693         { "Next Save Slot   ", 1 << SACTION_NEXT_SSLOT },
694         { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
695         { "Take Screenshot  ", 1 << SACTION_SCREENSHOT },
696         { "Enter Menu       ", 1 << SACTION_ENTER_MENU },
697         { NULL,                0 }
698 };
699
700 static char *mystrip(char *str)
701 {
702         int i, len;
703
704         len = strlen(str);
705         for (i = 0; i < len; i++)
706                 if (str[i] != ' ') break;
707         if (i > 0) memmove(str, str + i, len - i + 1);
708
709         len = strlen(str);
710         for (i = len - 1; i >= 0; i--)
711                 if (str[i] != ' ') break;
712         str[i+1] = 0;
713
714         return str;
715 }
716
717 static void get_line(char *d, size_t size, const char *s)
718 {
719         const char *pe;
720         size_t len;
721
722         for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
723                 ;
724         len = pe - s;
725         if (len > size - 1)
726                 len = size - 1;
727         strncpy(d, s, len);
728         d[len] = 0;
729
730         mystrip(d);
731 }
732
733 static void keys_write_all(FILE *f)
734 {
735         int d;
736
737         for (d = 0; d < IN_MAX_DEVS; d++)
738         {
739                 const int *binds = in_get_dev_binds(d);
740                 const char *name = in_get_dev_name(d, 0, 0);
741                 int k, count = 0;
742
743                 if (binds == NULL || name == NULL)
744                         continue;
745
746                 fprintf(f, "binddev = %s\n", name);
747                 in_get_config(d, IN_CFG_BIND_COUNT, &count);
748
749                 for (k = 0; k < count; k++)
750                 {
751                         int i, kbinds, mask;
752                         char act[32];
753
754                         act[0] = act[31] = 0;
755                         name = in_get_key_name(d, k);
756
757                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
758                         for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
759                                 mask = me_ctrl_actions[i].mask;
760                                 if (mask & kbinds) {
761                                         strncpy(act, me_ctrl_actions[i].name, 31);
762                                         fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
763                                         kbinds &= ~mask;
764                                 }
765                                 mask = me_ctrl_actions[i].mask << 16;
766                                 if (mask & kbinds) {
767                                         strncpy(act, me_ctrl_actions[i].name, 31);
768                                         fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
769                                         kbinds &= ~mask;
770                                 }
771                         }
772
773                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
774                         for (i = 0; kbinds && i < ARRAY_SIZE(emuctrl_actions) - 1; i++) {
775                                 mask = emuctrl_actions[i].mask;
776                                 if (mask & kbinds) {
777                                         strncpy(act, emuctrl_actions[i].name, 31);
778                                         fprintf(f, "bind %s = %s\n", name, mystrip(act));
779                                         kbinds &= ~mask;
780                                 }
781                         }
782                 }
783         }
784 }
785
786 static int parse_bind_val(const char *val, int *type)
787 {
788         int i;
789
790         *type = IN_BINDTYPE_NONE;
791         if (val[0] == 0)
792                 return 0;
793         
794         if (strncasecmp(val, "player", 6) == 0)
795         {
796                 int player, shift = 0;
797                 player = atoi(val + 6) - 1;
798
799                 if ((unsigned int)player > 1)
800                         return -1;
801                 if (player == 1)
802                         shift = 16;
803
804                 *type = IN_BINDTYPE_PLAYER12;
805                 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
806                         if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
807                                 return me_ctrl_actions[i].mask << shift;
808                 }
809         }
810         for (i = 0; emuctrl_actions[i].name != NULL; i++) {
811                 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
812                         *type = IN_BINDTYPE_EMU;
813                         return emuctrl_actions[i].mask;
814                 }
815         }
816
817         return -1;
818 }
819
820 static void keys_load_all(const char *cfg)
821 {
822         char dev[256], key[128], *act;
823         const char *p;
824         int bind, bindtype;
825         int dev_id;
826
827         p = cfg;
828         while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
829                 p += 10;
830
831                 get_line(dev, sizeof(dev), p);
832                 dev_id = in_config_parse_dev(dev);
833                 if (dev_id < 0) {
834                         printf("input: can't handle dev: %s\n", dev);
835                         continue;
836                 }
837
838                 in_unbind_all(dev_id, -1, -1);
839                 while ((p = strstr(p, "bind"))) {
840                         if (strncmp(p, "binddev = ", 10) == 0)
841                                 break;
842
843                         p += 4;
844                         if (*p != ' ') {
845                                 printf("input: parse error: %16s..\n", p);
846                                 continue;
847                         }
848
849                         get_line(key, sizeof(key), p);
850                         act = strchr(key, '=');
851                         if (act == NULL) {
852                                 printf("parse failed: %16s..\n", p);
853                                 continue;
854                         }
855                         *act = 0;
856                         act++;
857                         mystrip(key);
858                         mystrip(act);
859
860                         bind = parse_bind_val(act, &bindtype);
861                         if (bind != -1 && bind != 0) {
862                                 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
863                                 in_config_bind_key(dev_id, key, bind, bindtype);
864                         }
865                         else
866                                 lprintf("config: unhandled action \"%s\"\n", act);
867                 }
868         }
869         in_clean_binds();
870 }
871
872 static int key_config_loop_wrap(int id, int keys)
873 {
874         switch (id) {
875                 case MA_CTRL_PLAYER1:
876                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
877                         break;
878                 case MA_CTRL_PLAYER2:
879                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
880                         break;
881                 case MA_CTRL_EMU:
882                         key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
883                         break;
884                 default:
885                         break;
886         }
887         return 0;
888 }
889
890 static const char *mgn_dev_name(int id, int *offs)
891 {
892         const char *name = NULL;
893         static int it = 0;
894
895         if (id == MA_CTRL_DEV_FIRST)
896                 it = 0;
897
898         for (; it < IN_MAX_DEVS; it++) {
899                 name = in_get_dev_name(it, 1, 1);
900                 if (name != NULL)
901                         break;
902         }
903
904         it++;
905         return name;
906 }
907
908 static const char *mgn_saveloadcfg(int id, int *offs)
909 {
910         return "";
911 }
912
913 static int mh_savecfg(int id, int keys)
914 {
915         if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
916                 me_update_msg("config saved");
917         else
918                 me_update_msg("failed to write config");
919
920         return 1;
921 }
922
923 static int mh_input_rescan(int id, int keys)
924 {
925         //menu_sync_config();
926         pandora_rescan_inputs();
927         me_update_msg("rescan complete.");
928
929         return 0;
930 }
931
932 static const char *men_in_type_sel[] = { "Standard (SCPH-1080)", "Analog (SCPH-1150)", NULL };
933 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
934
935 static menu_entry e_menu_keyconfig[] =
936 {
937         mee_handler_id("Player 1",          MA_CTRL_PLAYER1,    key_config_loop_wrap),
938         mee_handler_id("Player 2",          MA_CTRL_PLAYER2,    key_config_loop_wrap),
939         mee_handler_id("Emulator controls", MA_CTRL_EMU,        key_config_loop_wrap),
940         mee_label     (""),
941         mee_enum      ("Controller",        0, in_type_sel,     men_in_type_sel),
942         mee_onoff_h   ("Nubs as buttons",   0, in_evdev_allow_abs_only, 1, h_nub_btns),
943         mee_cust_nosave("Save global config",       MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
944         mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
945         mee_handler   ("Rescan devices",  mh_input_rescan),
946         mee_label     (""),
947         mee_label     ("Input devices:"),
948         mee_label_mk  (MA_CTRL_DEV_FIRST, mgn_dev_name),
949         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
950         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
951         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
952         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
953         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
954         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
955         mee_end,
956 };
957
958 static int menu_loop_keyconfig(int id, int keys)
959 {
960         static int sel = 0;
961
962 //      me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
963         me_loop(e_menu_keyconfig, &sel, NULL);
964         return 0;
965 }
966
967 // ------------ gfx options menu ------------
968
969 static const char *men_scaler[] = { "1x1", "scaled 4:3", "fullscreen", "custom", NULL };
970 static const char h_cscaler[]   = "Displays the scaler layer, you can resize it\n"
971                                   "using d-pad or move it using R+d-pad";
972 static const char *men_dummy[] = { NULL };
973
974 static int menu_loop_cscaler(int id, int keys)
975 {
976         unsigned int inp;
977
978         scaling = SCALE_CUSTOM;
979
980         omap_enable_layer(1);
981
982         for (;;)
983         {
984                 menu_draw_begin(0);
985                 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
986                 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
987                 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
988                 menu_draw_end();
989
990                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
991                 if (inp & PBTN_UP)    g_layer_y--;
992                 if (inp & PBTN_DOWN)  g_layer_y++;
993                 if (inp & PBTN_LEFT)  g_layer_x--;
994                 if (inp & PBTN_RIGHT) g_layer_x++;
995                 if (!(inp & PBTN_R)) {
996                         if (inp & PBTN_UP)    g_layer_h += 2;
997                         if (inp & PBTN_DOWN)  g_layer_h -= 2;
998                         if (inp & PBTN_LEFT)  g_layer_w += 2;
999                         if (inp & PBTN_RIGHT) g_layer_w -= 2;
1000                 }
1001                 if (inp & (PBTN_MOK|PBTN_MBACK))
1002                         break;
1003
1004                 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1005                         if (g_layer_x < 0)   g_layer_x = 0;
1006                         if (g_layer_x > 640) g_layer_x = 640;
1007                         if (g_layer_y < 0)   g_layer_y = 0;
1008                         if (g_layer_y > 420) g_layer_y = 420;
1009                         if (g_layer_w < 160) g_layer_w = 160;
1010                         if (g_layer_h < 60)  g_layer_h = 60;
1011                         if (g_layer_x + g_layer_w > 800)
1012                                 g_layer_w = 800 - g_layer_x;
1013                         if (g_layer_y + g_layer_h > 480)
1014                                 g_layer_h = 480 - g_layer_y;
1015                         omap_enable_layer(1);
1016                 }
1017         }
1018
1019         omap_enable_layer(0);
1020
1021         return 0;
1022 }
1023
1024 static menu_entry e_menu_gfx_options[] =
1025 {
1026         mee_enum      ("Scaler",                   0, scaling, men_scaler),
1027         mee_enum      ("Filter",                   MA_OPT_FILTERING, filter, men_dummy),
1028 //      mee_onoff     ("Vsync",                    0, vsync, 1),
1029         mee_cust_h    ("Setup custom scaler",      0, menu_loop_cscaler, NULL, h_cscaler),
1030         mee_end,
1031 };
1032
1033 static int menu_loop_gfx_options(int id, int keys)
1034 {
1035         static int sel = 0;
1036
1037         me_loop(e_menu_gfx_options, &sel, NULL);
1038
1039         return 0;
1040 }
1041
1042 // ------------ bios/plugins ------------
1043
1044 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1045 static const char h_gpu_0[]            = "Needed for Chrono Cross";
1046 static const char h_gpu_1[]            = "Capcom fighting games";
1047 static const char h_gpu_2[]            = "Black screens in Lunar";
1048 static const char h_gpu_3[]            = "Compatibility mode";
1049 static const char h_gpu_6[]            = "Pandemonium 2";
1050 static const char h_gpu_7[]            = "Skip every second frame";
1051 static const char h_gpu_8[]            = "Needed by Dark Forces";
1052 static const char h_gpu_9[]            = "better g-colors, worse textures";
1053 static const char h_gpu_10[]           = "Toggle busy flags after drawing";
1054
1055 static menu_entry e_menu_plugin_gpu[] =
1056 {
1057         mee_enum      ("Dithering",                  0, iUseDither, men_gpu_dithering),
1058         mee_onoff_h   ("Odd/even bit hack",          0, dwActFixes, 1<<0, h_gpu_0),
1059         mee_onoff_h   ("Expand screen width",        0, dwActFixes, 1<<1, h_gpu_1),
1060         mee_onoff_h   ("Ignore brightness color",    0, dwActFixes, 1<<2, h_gpu_2),
1061         mee_onoff_h   ("Disable coordinate check",   0, dwActFixes, 1<<3, h_gpu_3),
1062         mee_onoff_h   ("Lazy screen update",         0, dwActFixes, 1<<6, h_gpu_6),
1063         mee_onoff_h   ("Old frame skipping",         0, dwActFixes, 1<<7, h_gpu_7),
1064         mee_onoff_h   ("Repeated flat tex triangles ",0,dwActFixes, 1<<8, h_gpu_8),
1065         mee_onoff_h   ("Draw quads with triangles",  0, dwActFixes, 1<<9, h_gpu_9),
1066         mee_onoff_h   ("Fake 'gpu busy' states",     0, dwActFixes, 1<<10, h_gpu_10),
1067         mee_end,
1068 };
1069
1070 static int menu_loop_plugin_gpu(int id, int keys)
1071 {
1072         static int sel = 0;
1073         me_loop(e_menu_plugin_gpu, &sel, NULL);
1074         return 0;
1075 }
1076
1077 static const char *men_spu_reverb[] = { "Off", "Fake", "On", NULL };
1078 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1079 static const char h_spu_irq_wait[]  = "Wait for CPU (recommended set to ON)";
1080 static const char h_spu_thread[]    = "Run sound emulation in main thread (recommended)";
1081
1082 static menu_entry e_menu_plugin_spu[] =
1083 {
1084         mee_enum      ("Reverb",                    0, iUseReverb, men_spu_reverb),
1085         mee_enum      ("Interpolation",             0, iUseInterpolation, men_spu_interp),
1086         mee_onoff     ("Adjust XA pitch",           0, iXAPitch, 1),
1087         mee_onoff_h   ("SPU IRQ Wait",              0, iSPUIRQWait, 1, h_spu_irq_wait),
1088         mee_onoff_h   ("Sound in main thread",      0, iUseTimer, 2, h_spu_thread),
1089         mee_end,
1090 };
1091
1092 static int menu_loop_plugin_spu(int id, int keys)
1093 {
1094         static int sel = 0;
1095         me_loop(e_menu_plugin_spu, &sel, NULL);
1096         return 0;
1097 }
1098
1099 static const char h_bios[]       = "HLE is simulated BIOS. BIOS selection is saved in savestates\n"
1100                                    "and can't be changed there. Must save config and reload\n"
1101                                    "the game for change to take effect";
1102 static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1103                                    "for plugin change to take effect";
1104 static const char h_gpu[]        = "Configure built-in P.E.Op.S. SoftGL Driver V1.17";
1105 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1106
1107 static menu_entry e_menu_plugin_options[] =
1108 {
1109         mee_enum_h    ("BIOS",                          0, bios_sel, bioses, h_bios),
1110         mee_enum_h    ("GPU plugin",                    0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1111         mee_enum_h    ("SPU plugin",                    0, spu_plugsel, spu_plugins, h_plugin_xpu),
1112         mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu, h_gpu),
1113         mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1114         mee_end,
1115 };
1116
1117 static menu_entry e_menu_main[];
1118
1119 static int menu_loop_plugin_options(int id, int keys)
1120 {
1121         static int sel = 0;
1122         me_loop(e_menu_plugin_options, &sel, NULL);
1123
1124         // sync BIOS/plugins
1125         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1126         snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1127         snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1128         me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1129
1130         return 0;
1131 }
1132
1133 // ------------ adv options menu ------------
1134
1135 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
1136 static const char h_cfg_fl[]     = "Frame Limiter keeps the game from running too fast";
1137 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
1138 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
1139                                    "(proper .cue/.bin dump is needed otherwise)";
1140 static const char h_cfg_sio[]    = "This should be enabled for certain memcards/gamepads";
1141 static const char h_cfg_spuirq[] = "Compatibility tweak; should probably be left off";
1142 static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix";
1143 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix";
1144 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
1145                                    "Might be useful to overcome some dynarec bugs";
1146
1147 static menu_entry e_menu_adv_options[] =
1148 {
1149         mee_onoff_h   ("Show CPU load",          0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1150         mee_onoff_h   ("Disable Frame Limiter",  0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1151         mee_onoff_h   ("Disable XA Decoding",    0, Config.Xa, 1, h_cfg_xa),
1152         mee_onoff_h   ("Disable CD Audio",       0, Config.Cdda, 1, h_cfg_cdda),
1153         mee_onoff_h   ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1154         mee_onoff_h   ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1155         mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
1156         mee_onoff_h   ("Rootcounter hack 2",     0, Config.VSyncWA, 1, h_cfg_rcnt2),
1157         mee_onoff_h   ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1158         mee_end,
1159 };
1160
1161 static int menu_loop_adv_options(int id, int keys)
1162 {
1163         static int sel = 0;
1164         me_loop(e_menu_adv_options, &sel, NULL);
1165         return 0;
1166 }
1167
1168 // ------------ options menu ------------
1169
1170 static int mh_restore_defaults(int id, int keys)
1171 {
1172         menu_set_defconfig();
1173         me_update_msg("defaults restored");
1174         return 1;
1175 }
1176
1177 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
1178 /*
1179 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1180 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
1181                                         "loading state or both";
1182 */
1183 static const char h_restore_def[]     = "Switches back to default / recommended\n"
1184                                         "configuration";
1185 static const char h_frameskip[]       = "Warning: frameskip sometimes causes glitches\n";
1186
1187 static menu_entry e_menu_options[] =
1188 {
1189 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1190 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1191         mee_onoff_h   ("Frameskip",                0, UseFrameSkip, 1, h_frameskip),
1192         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1193         mee_enum      ("Region",                   0, region, men_region),
1194         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1195         mee_handler   ("[Display]",                menu_loop_gfx_options),
1196         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1197         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1198         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1199         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1200         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1201         mee_end,
1202 };
1203
1204 static int menu_loop_options(int id, int keys)
1205 {
1206         static int sel = 0;
1207         int i;
1208
1209         i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1210         e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1211         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1212
1213         me_loop(e_menu_options, &sel, NULL);
1214
1215         return 0;
1216 }
1217
1218 // ------------ debug menu ------------
1219
1220 static void draw_frame_debug(GPUFreeze_t *gpuf)
1221 {
1222         int w = min(g_menuscreen_w, 1024);
1223         int h = min(g_menuscreen_h, 512);
1224         u16 *d = g_menuscreen_ptr;
1225         u16 *s = (u16 *)gpuf->psxVRam;
1226         char buff[64];
1227         int ty = 1;
1228
1229         gpuf->ulFreezeVersion = 1;
1230         if (GPU_freeze != NULL)
1231                 GPU_freeze(1, gpuf);
1232
1233         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1234                 bgr555_to_rgb565(d, s, w * 2);
1235
1236         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1237         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1238         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1239         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1240         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1241 }
1242
1243 static void debug_menu_loop(void)
1244 {
1245         GPUFreeze_t *gpuf;
1246         int inp;
1247
1248         gpuf = malloc(sizeof(*gpuf));
1249         if (gpuf == NULL)
1250                 return;
1251
1252         while (1)
1253         {
1254                 menu_draw_begin(0);
1255                 draw_frame_debug(gpuf);
1256                 menu_draw_end();
1257
1258                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1259                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1260                 if (inp & PBTN_MBACK)
1261                         break;
1262         }
1263
1264         free(gpuf);
1265 }
1266
1267 // ------------ main menu ------------
1268
1269 static void menu_bios_warn(void)
1270 {
1271         int inp;
1272         static const char msg[] =
1273                 "You don't seem to have copied any BIOS files to\n"
1274                 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1275                 "While many games work fine with fake (HLE) BIOS,\n"
1276                 "others (like MGS and FF8) require BIOS to work.\n"
1277                 "After copying the file, you'll also need to\n"
1278                 "select it in the emu's options->[BIOS/Plugins]\n\n"
1279                 "The file is usually named SCPH1001.BIN, but\n"
1280                 "other not compressed files can be used too.\n\n"
1281                 "Press (B) or (X) to continue";
1282
1283         while (1)
1284         {
1285                 draw_menu_message(msg, NULL);
1286
1287                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1288                 if (inp & (PBTN_MBACK|PBTN_MOK))
1289                         return;
1290         }
1291 }
1292
1293 // ------------ main menu ------------
1294
1295 void OnFile_Exit();
1296
1297 static void draw_frame_main(void)
1298 {
1299         if (CdromId[0] != 0) {
1300                 char buff[64];
1301                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1302                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1303                          Config.HLE ? "HLE" : "BIOS");
1304                 smalltext_out16(4, 1, buff, 0x105f);
1305         }
1306 }
1307
1308 static void draw_frame_credits(void)
1309 {
1310         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1311 }
1312
1313 static const char credits_text[] = 
1314         "PCSX-ReARMed\n\n"
1315         "(C) 1999-2003 PCSX Team\n"
1316         "(C) 2005-2009 PCSX-df Team\n"
1317         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1318         "GPU and SPU code by Pete Bernert\n"
1319         "  and the P.E.Op.S. team\n"
1320         "ARM recompiler (C) 2009-2011 Ari64\n"
1321         "PCSX4ALL plugins by PCSX4ALL team\n"
1322         "  Chui, Franxis, Unai\n\n"
1323         "integration, optimization and\n"
1324         "  frontend (C) 2010-2011 notaz\n";
1325
1326 static int reset_game(void)
1327 {
1328         // sanity check
1329         if (bios_sel == 0 && !Config.HLE)
1330                 return -1;
1331
1332         ClosePlugins();
1333         OpenPlugins();
1334         SysReset();
1335         if (CheckCdrom() != -1) {
1336                 LoadCdrom();
1337         }
1338         return 0;
1339 }
1340
1341 static int run_bios(void)
1342 {
1343         if (bios_sel == 0)
1344                 return -1;
1345
1346         ready_to_go = 0;
1347         pl_fbdev_buf = NULL;
1348
1349         ClosePlugins();
1350         set_cd_image(NULL);
1351         LoadPlugins();
1352         pcnt_hook_plugins();
1353         NetOpened = 0;
1354         if (OpenPlugins() == -1) {
1355                 me_update_msg("failed to open plugins");
1356                 return -1;
1357         }
1358         plugin_call_rearmed_cbs();
1359
1360         CdromId[0] = '\0';
1361         CdromLabel[0] = '\0';
1362
1363         SysReset();
1364
1365         ready_to_go = 1;
1366         return 0;
1367 }
1368
1369 static int run_cd_image(const char *fname)
1370 {
1371         ready_to_go = 0;
1372         pl_fbdev_buf = NULL;
1373
1374         ClosePlugins();
1375         set_cd_image(fname);
1376         LoadPlugins();
1377         pcnt_hook_plugins();
1378         NetOpened = 0;
1379         if (OpenPlugins() == -1) {
1380                 me_update_msg("failed to open plugins");
1381                 return -1;
1382         }
1383         plugin_call_rearmed_cbs();
1384
1385         if (CheckCdrom() == -1) {
1386                 // Only check the CD if we are starting the console with a CD
1387                 ClosePlugins();
1388                 me_update_msg("unsupported/invalid CD image");
1389                 return -1;
1390         }
1391
1392         SysReset();
1393
1394         // Read main executable directly from CDRom and start it
1395         if (LoadCdrom() == -1) {
1396                 ClosePlugins();
1397                 me_update_msg("failed to load CD image");
1398                 return -1;
1399         }
1400
1401         ready_to_go = 1;
1402         return 0;
1403 }
1404
1405 static int romsel_run(void)
1406 {
1407         int prev_gpu, prev_spu;
1408         char *fname;
1409
1410         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1411         if (fname == NULL)
1412                 return -1;
1413
1414         printf("selected file: %s\n", fname);
1415
1416         new_dynarec_clear_full();
1417
1418         if (run_cd_image(fname) != 0)
1419                 return -1;
1420
1421         prev_gpu = gpu_plugsel;
1422         prev_spu = spu_plugsel;
1423         if (menu_load_config(1) != 0)
1424                 menu_load_config(0);
1425
1426         // check for plugin changes, have to repeat
1427         // loading if game config changed plugins to reload them
1428         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1429                 printf("plugin change detected, reloading plugins..\n");
1430                 if (run_cd_image(fname) != 0)
1431                         return -1;
1432         }
1433
1434         strcpy(last_selected_fname, rom_fname_reload);
1435         return 0;
1436 }
1437
1438 static int swap_cd_image(void)
1439 {
1440         char *fname;
1441
1442         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1443         if (fname == NULL)
1444                 return -1;
1445
1446         printf("selected file: %s\n", fname);
1447
1448         CdromId[0] = '\0';
1449         CdromLabel[0] = '\0';
1450
1451         set_cd_image(fname);
1452         if (ReloadCdromPlugin() < 0) {
1453                 me_update_msg("failed to load cdr plugin");
1454                 return -1;
1455         }
1456         if (CDR_open() < 0) {
1457                 me_update_msg("failed to open cdr plugin");
1458                 return -1;
1459         }
1460
1461         SetCdOpenCaseTime(time(NULL) + 2);
1462         LidInterrupt();
1463
1464         strcpy(last_selected_fname, rom_fname_reload);
1465         return 0;
1466 }
1467
1468 static int main_menu_handler(int id, int keys)
1469 {
1470         switch (id)
1471         {
1472         case MA_MAIN_RESUME_GAME:
1473                 if (ready_to_go)
1474                         return 1;
1475                 break;
1476         case MA_MAIN_SAVE_STATE:
1477                 if (ready_to_go)
1478                         return menu_loop_savestate(0);
1479                 break;
1480         case MA_MAIN_LOAD_STATE:
1481                 if (ready_to_go)
1482                         return menu_loop_savestate(1);
1483                 break;
1484         case MA_MAIN_RESET_GAME:
1485                 if (ready_to_go && reset_game() == 0)
1486                         return 1;
1487                 break;
1488         case MA_MAIN_LOAD_ROM:
1489                 if (romsel_run() == 0)
1490                         return 1;
1491                 break;
1492         case MA_MAIN_SWAP_CD:
1493                 if (swap_cd_image() == 0)
1494                         return 1;
1495                 break;
1496         case MA_MAIN_RUN_BIOS:
1497                 if (run_bios() == 0)
1498                         return 1;
1499                 break;
1500         case MA_MAIN_CREDITS:
1501                 draw_menu_message(credits_text, draw_frame_credits);
1502                 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1503                 break;
1504         case MA_MAIN_EXIT:
1505                 OnFile_Exit();
1506                 break;
1507         default:
1508                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1509                 break;
1510         }
1511
1512         return 0;
1513 }
1514
1515 static menu_entry e_menu_main[] =
1516 {
1517         mee_label     (""),
1518         mee_label     (""),
1519         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
1520         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
1521         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
1522         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
1523         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
1524         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,     main_menu_handler),
1525         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,    main_menu_handler),
1526         mee_handler   ("Options",            menu_loop_options),
1527         mee_handler   ("Controls",           menu_loop_keyconfig),
1528         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
1529         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
1530         mee_end,
1531 };
1532
1533 // ----------------------------
1534
1535 static void menu_leave_emu(void);
1536
1537 void menu_loop(void)
1538 {
1539         static int sel = 0;
1540
1541         menu_leave_emu();
1542
1543         if (bioses[1] == NULL && !warned_about_bios) {
1544                 menu_bios_warn();
1545                 warned_about_bios = 1;
1546         }
1547
1548         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1549         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
1550         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
1551         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
1552         me_enable(e_menu_main, MA_MAIN_SWAP_CD,  ready_to_go);
1553         me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1554
1555         in_set_config_int(0, IN_CFG_BLOCKING, 1);
1556
1557         do {
1558                 me_loop(e_menu_main, &sel, draw_frame_main);
1559         } while (!ready_to_go);
1560
1561         /* wait until menu, ok, back is released */
1562         while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1563                 ;
1564
1565         in_set_config_int(0, IN_CFG_BLOCKING, 0);
1566
1567         menu_prepare_emu();
1568 }
1569
1570 static void scan_bios_plugins(void)
1571 {
1572         char fname[MAXPATHLEN];
1573         struct dirent *ent;
1574         int bios_i, gpu_i, spu_i;
1575         char *p;
1576         DIR *dir;
1577
1578         bioses[0] = "HLE";
1579         gpu_plugins[0] = "builtin_gpu";
1580         spu_plugins[0] = "builtin_spu";
1581         bios_i = gpu_i = spu_i = 1;
1582
1583         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1584         dir = opendir(fname);
1585         if (dir == NULL) {
1586                 perror("scan_bios_plugins bios opendir");
1587                 goto do_plugins;
1588         }
1589
1590         while (1) {
1591                 struct stat st;
1592
1593                 errno = 0;
1594                 ent = readdir(dir);
1595                 if (ent == NULL) {
1596                         if (errno != 0)
1597                                 perror("readdir");
1598                         break;
1599                 }
1600
1601                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1602                         continue;
1603
1604                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1605                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1606                         printf("bad BIOS file: %s\n", ent->d_name);
1607                         continue;
1608                 }
1609
1610                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1611                         bioses[bios_i++] = strdup(ent->d_name);
1612                         continue;
1613                 }
1614
1615                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1616         }
1617
1618         closedir(dir);
1619
1620 do_plugins:
1621         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1622         dir = opendir(fname);
1623         if (dir == NULL) {
1624                 perror("scan_bios_plugins opendir");
1625                 return;
1626         }
1627
1628         while (1) {
1629                 void *h, *tmp;
1630
1631                 errno = 0;
1632                 ent = readdir(dir);
1633                 if (ent == NULL) {
1634                         if (errno != 0)
1635                                 perror("readdir");
1636                         break;
1637                 }
1638                 p = strstr(ent->d_name, ".so");
1639                 if (p == NULL)
1640                         continue;
1641
1642                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1643                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1644                 if (h == NULL) {
1645                         fprintf(stderr, "%s\n", dlerror());
1646                         continue;
1647                 }
1648
1649                 // now what do we have here?
1650                 tmp = dlsym(h, "GPUinit");
1651                 if (tmp) {
1652                         dlclose(h);
1653                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1654                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1655                         continue;
1656                 }
1657
1658                 tmp = dlsym(h, "SPUinit");
1659                 if (tmp) {
1660                         dlclose(h);
1661                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1662                                 spu_plugins[spu_i++] = strdup(ent->d_name);
1663                         continue;
1664                 }
1665
1666                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1667                 dlclose(h);
1668         }
1669
1670         closedir(dir);
1671 }
1672
1673 void menu_init(void)
1674 {
1675         char buff[MAXPATHLEN];
1676
1677         strcpy(last_selected_fname, "/media");
1678
1679         scan_bios_plugins();
1680         pnd_menu_init();
1681         menu_init_common();
1682
1683         menu_set_defconfig();
1684         menu_load_config(0);
1685         last_psx_w = 320;
1686         last_psx_h = 240;
1687         last_psx_bpp = 16;
1688
1689         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1690         if (g_menubg_src_ptr == NULL)
1691                 exit(1);
1692         emu_make_path(buff, "skin/background.png", sizeof(buff));
1693         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1694 }
1695
1696 void menu_notify_mode_change(int w, int h, int bpp)
1697 {
1698         last_psx_w = w;
1699         last_psx_h = h;
1700         last_psx_bpp = bpp;
1701
1702         if (scaling == SCALE_1_1) {
1703                 g_layer_x = 800/2 - w/2;  g_layer_y = 480/2 - h/2;
1704                 g_layer_w = w; g_layer_h = h;
1705         }
1706 }
1707
1708 static void menu_leave_emu(void)
1709 {
1710         if (GPU_close != NULL) {
1711                 int ret = GPU_close();
1712                 if (ret)
1713                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1714         }
1715
1716         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1717         if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
1718                 int x = max(0, g_menuscreen_w - last_psx_w);
1719                 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1720                 int w = min(g_menuscreen_w, last_psx_w);
1721                 int h = min(g_menuscreen_h, last_psx_h);
1722                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
1723                 u16 *s = pl_fbdev_buf;
1724
1725                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
1726                         menu_darken_bg(d, s, w, 0);
1727         }
1728
1729         if (ready_to_go)
1730                 cpu_clock = get_cpu_clock();
1731
1732         plat_video_menu_enter(ready_to_go);
1733 }
1734
1735 void menu_prepare_emu(void)
1736 {
1737         R3000Acpu *prev_cpu = psxCpu;
1738
1739         plat_video_menu_leave();
1740
1741         switch (scaling) {
1742         case SCALE_1_1:
1743                 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
1744                 break;
1745         case SCALE_4_3:
1746                 g_layer_x = 80;  g_layer_y = 0;
1747                 g_layer_w = 640; g_layer_h = 480;
1748                 break;
1749         case SCALE_FULLSCREEN:
1750                 g_layer_x = 0;   g_layer_y = 0;
1751                 g_layer_w = 800; g_layer_h = 480;
1752                 break;
1753         case SCALE_CUSTOM:
1754                 break;
1755         }
1756
1757         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1758         if (psxCpu != prev_cpu)
1759                 // note that this does not really reset, just clears drc caches
1760                 psxCpu->Reset();
1761
1762         // core doesn't care about Config.Cdda changes,
1763         // so handle them manually here
1764         if (Config.Cdda)
1765                 CDR_stop();
1766
1767         menu_sync_config();
1768         apply_lcdrate(Config.PsxType);
1769         apply_filter(filter);
1770         apply_cpu_clock();
1771
1772         if (GPU_open != NULL) {
1773                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1774                 if (ret)
1775                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1776         }
1777
1778         dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
1779 }
1780
1781 void me_update_msg(const char *msg)
1782 {
1783         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1784         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1785
1786         menu_error_time = plat_get_ticks_ms();
1787         lprintf("msg: %s\n", menu_error_msg);
1788 }
1789