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