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