frontend: bios warning bugfix
[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
1148 static menu_entry e_menu_options[] =
1149 {
1150 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1151 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1152         mee_onoff     ("Frameskip",                0, UseFrameSkip, 1),
1153         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1154         mee_enum      ("Region",                   0, region, men_region),
1155         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1156         mee_handler   ("[Display]",                menu_loop_gfx_options),
1157         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1158         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1159         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1160         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1161         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1162         mee_end,
1163 };
1164
1165 static int menu_loop_options(int id, int keys)
1166 {
1167         static int sel = 0;
1168         int i;
1169
1170         i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1171         e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1172         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1173
1174         me_loop(e_menu_options, &sel, NULL);
1175
1176         return 0;
1177 }
1178
1179 // ------------ debug menu ------------
1180
1181 static void draw_frame_debug(GPUFreeze_t *gpuf)
1182 {
1183         int w = min(g_menuscreen_w, 1024);
1184         int h = min(g_menuscreen_h, 512);
1185         u16 *d = g_menuscreen_ptr;
1186         u16 *s = (u16 *)gpuf->psxVRam;
1187         char buff[64];
1188         int ty = 1;
1189
1190         gpuf->ulFreezeVersion = 1;
1191         if (GPU_freeze != NULL)
1192                 GPU_freeze(1, gpuf);
1193
1194         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1195                 bgr555_to_rgb565(d, s, w * 2);
1196
1197         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1198         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1199         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1200         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1201         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1202 }
1203
1204 static void debug_menu_loop(void)
1205 {
1206         GPUFreeze_t *gpuf;
1207         int inp;
1208
1209         gpuf = malloc(sizeof(*gpuf));
1210         if (gpuf == NULL)
1211                 return;
1212
1213         while (1)
1214         {
1215                 menu_draw_begin(0);
1216                 draw_frame_debug(gpuf);
1217                 menu_draw_end();
1218
1219                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1220                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1221                 if (inp & PBTN_MBACK)
1222                         break;
1223         }
1224
1225         free(gpuf);
1226 }
1227
1228 // ------------ main menu ------------
1229
1230 static void menu_bios_warn(void)
1231 {
1232         int inp;
1233         static const char msg[] =
1234                 "You don't seem to have copied any BIOS files to\n"
1235                 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1236                 "While many games work fine with fake (HLE) BIOS,\n"
1237                 "others (like MGS and FF8) require BIOS to work.\n"
1238                 "After copying the file, you'll also need to\n"
1239                 "select it in the emu's options->[BIOS/Plugins]\n\n"
1240                 "The file is usually named SCPH1001.BIN, but\n"
1241                 "other not compressed files can be used too.\n\n"
1242                 "Press (B) or (X) to continue";
1243
1244         while (1)
1245         {
1246                 draw_menu_message(msg, NULL);
1247
1248                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1249                 if (inp & (PBTN_MBACK|PBTN_MOK))
1250                         return;
1251         }
1252 }
1253
1254 // ------------ main menu ------------
1255
1256 void OnFile_Exit();
1257
1258 static void draw_frame_main(void)
1259 {
1260         if (CdromId[0] != 0) {
1261                 char buff[64];
1262                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1263                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1264                          Config.HLE ? "HLE" : "BIOS");
1265                 smalltext_out16(4, 1, buff, 0x105f);
1266         }
1267 }
1268
1269 static void draw_frame_credits(void)
1270 {
1271         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1272 }
1273
1274 static const char credits_text[] = 
1275         "PCSX-ReARMed\n\n"
1276         "(C) 1999-2003 PCSX Team\n"
1277         "(C) 2005-2009 PCSX-df Team\n"
1278         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1279         "GPU and SPU code by Pete Bernert\n"
1280         "  and the P.E.Op.S. team\n"
1281         "ARM recompiler (C) 2009-2011 Ari64\n"
1282         "PCSX4ALL plugins by PCSX4ALL team\n"
1283         "  Chui, Franxis, Unai\n\n"
1284         "integration, optimization and\n"
1285         "  frontend (C) 2010-2011 notaz\n";
1286
1287 static int reset_game(void)
1288 {
1289         // sanity check
1290         if (bios_sel == 0 && !Config.HLE)
1291                 return -1;
1292
1293         ClosePlugins();
1294         OpenPlugins();
1295         SysReset();
1296         if (CheckCdrom() != -1) {
1297                 LoadCdrom();
1298         }
1299         return 0;
1300 }
1301
1302 static int run_bios(void)
1303 {
1304         if (bios_sel == 0)
1305                 return -1;
1306
1307         ready_to_go = 0;
1308         pl_fbdev_buf = NULL;
1309
1310         ClosePlugins();
1311         set_cd_image(NULL);
1312         LoadPlugins();
1313         pcnt_hook_plugins();
1314         NetOpened = 0;
1315         if (OpenPlugins() == -1) {
1316                 me_update_msg("failed to open plugins");
1317                 return -1;
1318         }
1319         plugin_call_rearmed_cbs();
1320
1321         CdromId[0] = '\0';
1322         CdromLabel[0] = '\0';
1323
1324         SysReset();
1325
1326         ready_to_go = 1;
1327         return 0;
1328 }
1329
1330 static int run_cd_image(const char *fname)
1331 {
1332         ready_to_go = 0;
1333         pl_fbdev_buf = NULL;
1334
1335         ClosePlugins();
1336         set_cd_image(fname);
1337         LoadPlugins();
1338         pcnt_hook_plugins();
1339         NetOpened = 0;
1340         if (OpenPlugins() == -1) {
1341                 me_update_msg("failed to open plugins");
1342                 return -1;
1343         }
1344         plugin_call_rearmed_cbs();
1345
1346         if (CheckCdrom() == -1) {
1347                 // Only check the CD if we are starting the console with a CD
1348                 ClosePlugins();
1349                 me_update_msg("unsupported/invalid CD image");
1350                 return -1;
1351         }
1352
1353         SysReset();
1354
1355         // Read main executable directly from CDRom and start it
1356         if (LoadCdrom() == -1) {
1357                 ClosePlugins();
1358                 me_update_msg("failed to load CD image");
1359                 return -1;
1360         }
1361
1362         ready_to_go = 1;
1363         return 0;
1364 }
1365
1366 static int romsel_run(void)
1367 {
1368         int prev_gpu, prev_spu;
1369         char *fname;
1370
1371         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1372         if (fname == NULL)
1373                 return -1;
1374
1375         printf("selected file: %s\n", fname);
1376
1377         new_dynarec_clear_full();
1378
1379         if (run_cd_image(fname) != 0)
1380                 return -1;
1381
1382         prev_gpu = gpu_plugsel;
1383         prev_spu = spu_plugsel;
1384         if (menu_load_config(1) != 0)
1385                 menu_load_config(0);
1386
1387         // check for plugin changes, have to repeat
1388         // loading if game config changed plugins to reload them
1389         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1390                 printf("plugin change detected, reloading plugins..\n");
1391                 if (run_cd_image(fname) != 0)
1392                         return -1;
1393         }
1394
1395         strcpy(last_selected_fname, rom_fname_reload);
1396         return 0;
1397 }
1398
1399 static int swap_cd_image(void)
1400 {
1401         char *fname;
1402
1403         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1404         if (fname == NULL)
1405                 return -1;
1406
1407         printf("selected file: %s\n", fname);
1408
1409         CdromId[0] = '\0';
1410         CdromLabel[0] = '\0';
1411
1412         set_cd_image(fname);
1413         if (ReloadCdromPlugin() < 0) {
1414                 me_update_msg("failed to load cdr plugin");
1415                 return -1;
1416         }
1417         if (CDR_open() < 0) {
1418                 me_update_msg("failed to open cdr plugin");
1419                 return -1;
1420         }
1421
1422         SetCdOpenCaseTime(time(NULL) + 2);
1423         LidInterrupt();
1424
1425         strcpy(last_selected_fname, rom_fname_reload);
1426         return 0;
1427 }
1428
1429 static int main_menu_handler(int id, int keys)
1430 {
1431         switch (id)
1432         {
1433         case MA_MAIN_RESUME_GAME:
1434                 if (ready_to_go)
1435                         return 1;
1436                 break;
1437         case MA_MAIN_SAVE_STATE:
1438                 if (ready_to_go)
1439                         return menu_loop_savestate(0);
1440                 break;
1441         case MA_MAIN_LOAD_STATE:
1442                 if (ready_to_go)
1443                         return menu_loop_savestate(1);
1444                 break;
1445         case MA_MAIN_RESET_GAME:
1446                 if (ready_to_go && reset_game() == 0)
1447                         return 1;
1448                 break;
1449         case MA_MAIN_LOAD_ROM:
1450                 if (romsel_run() == 0)
1451                         return 1;
1452                 break;
1453         case MA_MAIN_SWAP_CD:
1454                 if (swap_cd_image() == 0)
1455                         return 1;
1456                 break;
1457         case MA_MAIN_RUN_BIOS:
1458                 if (run_bios() == 0)
1459                         return 1;
1460                 break;
1461         case MA_MAIN_CREDITS:
1462                 draw_menu_message(credits_text, draw_frame_credits);
1463                 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1464                 break;
1465         case MA_MAIN_EXIT:
1466                 OnFile_Exit();
1467                 break;
1468         default:
1469                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1470                 break;
1471         }
1472
1473         return 0;
1474 }
1475
1476 static menu_entry e_menu_main[] =
1477 {
1478         mee_label     (""),
1479         mee_label     (""),
1480         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
1481         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
1482         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
1483         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
1484         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
1485         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,     main_menu_handler),
1486         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,    main_menu_handler),
1487         mee_handler   ("Options",            menu_loop_options),
1488         mee_handler   ("Controls",           menu_loop_keyconfig),
1489         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
1490         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
1491         mee_end,
1492 };
1493
1494 // ----------------------------
1495
1496 static void menu_leave_emu(void);
1497
1498 void menu_loop(void)
1499 {
1500         static int sel = 0;
1501
1502         menu_leave_emu();
1503
1504         if (bioses[1] == NULL && !warned_about_bios) {
1505                 menu_bios_warn();
1506                 warned_about_bios = 1;
1507         }
1508
1509         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1510         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
1511         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
1512         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
1513         me_enable(e_menu_main, MA_MAIN_SWAP_CD,  ready_to_go);
1514         me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1515
1516         in_set_config_int(0, IN_CFG_BLOCKING, 1);
1517
1518         do {
1519                 me_loop(e_menu_main, &sel, draw_frame_main);
1520         } while (!ready_to_go);
1521
1522         /* wait until menu, ok, back is released */
1523         while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1524                 ;
1525
1526         in_set_config_int(0, IN_CFG_BLOCKING, 0);
1527
1528         menu_prepare_emu();
1529 }
1530
1531 static void scan_bios_plugins(void)
1532 {
1533         char fname[MAXPATHLEN];
1534         struct dirent *ent;
1535         int bios_i, gpu_i, spu_i;
1536         char *p;
1537         DIR *dir;
1538
1539         bioses[0] = "HLE";
1540         gpu_plugins[0] = "builtin_gpu";
1541         spu_plugins[0] = "builtin_spu";
1542         bios_i = gpu_i = spu_i = 1;
1543
1544         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1545         dir = opendir(fname);
1546         if (dir == NULL) {
1547                 perror("scan_bios_plugins bios opendir");
1548                 goto do_plugins;
1549         }
1550
1551         while (1) {
1552                 struct stat st;
1553
1554                 errno = 0;
1555                 ent = readdir(dir);
1556                 if (ent == NULL) {
1557                         if (errno != 0)
1558                                 perror("readdir");
1559                         break;
1560                 }
1561
1562                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1563                         continue;
1564
1565                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1566                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1567                         printf("bad BIOS file: %s\n", ent->d_name);
1568                         continue;
1569                 }
1570
1571                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1572                         bioses[bios_i++] = strdup(ent->d_name);
1573                         continue;
1574                 }
1575
1576                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1577         }
1578
1579         closedir(dir);
1580
1581 do_plugins:
1582         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1583         dir = opendir(fname);
1584         if (dir == NULL) {
1585                 perror("scan_bios_plugins opendir");
1586                 return;
1587         }
1588
1589         while (1) {
1590                 void *h, *tmp;
1591
1592                 errno = 0;
1593                 ent = readdir(dir);
1594                 if (ent == NULL) {
1595                         if (errno != 0)
1596                                 perror("readdir");
1597                         break;
1598                 }
1599                 p = strstr(ent->d_name, ".so");
1600                 if (p == NULL)
1601                         continue;
1602
1603                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1604                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1605                 if (h == NULL) {
1606                         fprintf(stderr, "%s\n", dlerror());
1607                         continue;
1608                 }
1609
1610                 // now what do we have here?
1611                 tmp = dlsym(h, "GPUinit");
1612                 if (tmp) {
1613                         dlclose(h);
1614                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1615                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1616                         continue;
1617                 }
1618
1619                 tmp = dlsym(h, "SPUinit");
1620                 if (tmp) {
1621                         dlclose(h);
1622                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1623                                 spu_plugins[spu_i++] = strdup(ent->d_name);
1624                         continue;
1625                 }
1626
1627                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1628                 dlclose(h);
1629         }
1630
1631         closedir(dir);
1632 }
1633
1634 void menu_init(void)
1635 {
1636         char buff[MAXPATHLEN];
1637
1638         strcpy(last_selected_fname, "/media");
1639
1640         scan_bios_plugins();
1641         pnd_menu_init();
1642         menu_init_common();
1643
1644         menu_set_defconfig();
1645         menu_load_config(0);
1646         last_psx_w = 320;
1647         last_psx_h = 240;
1648         last_psx_bpp = 16;
1649
1650         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1651         if (g_menubg_src_ptr == NULL)
1652                 exit(1);
1653         emu_make_path(buff, "skin/background.png", sizeof(buff));
1654         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1655 }
1656
1657 void menu_notify_mode_change(int w, int h, int bpp)
1658 {
1659         last_psx_w = w;
1660         last_psx_h = h;
1661         last_psx_bpp = bpp;
1662
1663         if (scaling == SCALE_1_1) {
1664                 g_layer_x = 800/2 - w/2;  g_layer_y = 480/2 - h/2;
1665                 g_layer_w = w; g_layer_h = h;
1666         }
1667 }
1668
1669 static void menu_leave_emu(void)
1670 {
1671         if (GPU_close != NULL) {
1672                 int ret = GPU_close();
1673                 if (ret)
1674                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1675         }
1676
1677         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1678         if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
1679                 int x = max(0, g_menuscreen_w - last_psx_w);
1680                 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1681                 int w = min(g_menuscreen_w, last_psx_w);
1682                 int h = min(g_menuscreen_h, last_psx_h);
1683                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
1684                 u16 *s = pl_fbdev_buf;
1685
1686                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
1687                         menu_darken_bg(d, s, w, 0);
1688         }
1689
1690         if (ready_to_go)
1691                 cpu_clock = get_cpu_clock();
1692
1693         plat_video_menu_enter(ready_to_go);
1694 }
1695
1696 void menu_prepare_emu(void)
1697 {
1698         R3000Acpu *prev_cpu = psxCpu;
1699
1700         plat_video_menu_leave();
1701
1702         switch (scaling) {
1703         case SCALE_1_1:
1704                 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
1705                 break;
1706         case SCALE_4_3:
1707                 g_layer_x = 80;  g_layer_y = 0;
1708                 g_layer_w = 640; g_layer_h = 480;
1709                 break;
1710         case SCALE_FULLSCREEN:
1711                 g_layer_x = 0;   g_layer_y = 0;
1712                 g_layer_w = 800; g_layer_h = 480;
1713                 break;
1714         case SCALE_CUSTOM:
1715                 break;
1716         }
1717         apply_filter(filter);
1718         apply_cpu_clock();
1719
1720         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1721         if (psxCpu != prev_cpu)
1722                 // note that this does not really reset, just clears drc caches
1723                 psxCpu->Reset();
1724
1725         // core doesn't care about Config.Cdda changes,
1726         // so handle them manually here
1727         if (Config.Cdda)
1728                 CDR_stop();
1729
1730         menu_sync_config();
1731
1732         if (GPU_open != NULL) {
1733                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1734                 if (ret)
1735                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1736         }
1737
1738         dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
1739 }
1740
1741 void me_update_msg(const char *msg)
1742 {
1743         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1744         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1745
1746         menu_error_time = plat_get_ticks_ms();
1747         lprintf("msg: %s\n", menu_error_msg);
1748 }
1749