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