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