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