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