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