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