frontend: menu: show savestate date
[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 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <unistd.h>
19
20 #include "main.h"
21 #include "menu.h"
22 #include "config.h"
23 #include "plugin.h"
24 #include "plugin_lib.h"
25 #include "omap.h"
26 #include "plat.h"
27 #include "pcnt.h"
28 #include "cspace.h"
29 #include "common/plat.h"
30 #include "common/input.h"
31 #include "linux/in_evdev.h"
32 #include "../libpcsxcore/misc.h"
33 #include "../libpcsxcore/cdrom.h"
34 #include "../libpcsxcore/cdriso.h"
35 #include "../libpcsxcore/psemu_plugin_defs.h"
36 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
37 #include "../plugins/dfinput/main.h"
38 #include "revision.h"
39
40 #define REARMED_BIRTHDAY_TIME 1293306830        /* 25 Dec 2010 */
41
42 #define array_size(x) (sizeof(x) / sizeof(x[0]))
43
44 typedef enum
45 {
46         MA_NONE = 1,
47         MA_MAIN_RESUME_GAME,
48         MA_MAIN_SAVE_STATE,
49         MA_MAIN_LOAD_STATE,
50         MA_MAIN_RESET_GAME,
51         MA_MAIN_LOAD_ROM,
52         MA_MAIN_SWAP_CD,
53         MA_MAIN_SWAP_CD_MULTI,
54         MA_MAIN_RUN_BIOS,
55         MA_MAIN_RUN_EXE,
56         MA_MAIN_CONTROLS,
57         MA_MAIN_CREDITS,
58         MA_MAIN_EXIT,
59         MA_CTRL_PLAYER1,
60         MA_CTRL_PLAYER2,
61         MA_CTRL_ANALOG,
62         MA_CTRL_EMU,
63         MA_CTRL_DEV_FIRST,
64         MA_CTRL_DEV_NEXT,
65         MA_CTRL_NUBS_BTNS,
66         MA_CTRL_DEADZONE,
67         MA_CTRL_VIBRATION,
68         MA_CTRL_DONE,
69         MA_OPT_SAVECFG,
70         MA_OPT_SAVECFG_GAME,
71         MA_OPT_CPU_CLOCKS,
72         MA_OPT_FILTERING,
73         MA_OPT_DISP_OPTS,
74 } menu_id;
75
76 enum {
77         SCALE_1_1,
78         SCALE_4_3,
79         SCALE_4_3v2,
80         SCALE_FULLSCREEN,
81         SCALE_CUSTOM,
82 };
83
84 static int last_psx_w, last_psx_h, last_psx_bpp;
85 static int scaling, filter, cpu_clock, cpu_clock_st, volume_boost, frameskip;
86 static char rom_fname_reload[MAXPATHLEN];
87 static char last_selected_fname[MAXPATHLEN];
88 static int warned_about_bios, region, in_type_sel1, in_type_sel2;
89 static int psx_clock;
90 static int memcard1_sel, memcard2_sel;
91 int g_opts;
92 int analog_deadzone; // for Caanoo
93
94 #ifdef __ARM_ARCH_7A__
95 #define DEFAULT_PSX_CLOCK 57
96 #define DEFAULT_PSX_CLOCK_S "57"
97 #else
98 #define DEFAULT_PSX_CLOCK 50
99 #define DEFAULT_PSX_CLOCK_S "50"
100 #endif
101
102 // sound plugin
103 extern int iUseReverb;
104 extern int iUseInterpolation;
105 extern int iXAPitch;
106 extern int iSPUIRQWait;
107 extern int iUseTimer;
108 extern int iVolume;
109
110 static const char *bioses[24];
111 static const char *gpu_plugins[16];
112 static const char *spu_plugins[16];
113 static const char *memcards[32];
114 static int bios_sel, gpu_plugsel, spu_plugsel;
115
116
117 static int min(int x, int y) { return x < y ? x : y; }
118 static int max(int x, int y) { return x > y ? x : y; }
119
120 void emu_make_path(char *buff, const char *end, int size)
121 {
122         int pos, end_len;
123
124         end_len = strlen(end);
125         pos = plat_get_root_dir(buff, size);
126         strncpy(buff + pos, end, size - pos);
127         buff[size - 1] = 0;
128         if (pos + end_len > size - 1)
129                 printf("Warning: path truncated: %s\n", buff);
130 }
131
132 static int emu_check_save_file(int slot, int *time)
133 {
134         char fname[MAXPATHLEN];
135         struct stat status;
136         int ret;
137         
138         ret = emu_check_state(slot);
139         if (ret != 0 || time == NULL)
140                 return ret == 0 ? 1 : 0;
141
142         ret = get_state_filename(fname, sizeof(fname), slot);
143         if (ret != 0)
144                 return 1;
145
146         ret = stat(fname, &status);
147         if (ret != 0)
148                 return 1;
149
150         if (status.st_mtime < REARMED_BIRTHDAY_TIME)
151                 return 1; // probably bad rtc like on some Caanoos
152
153         *time = status.st_mtime;
154
155         return 1;
156 }
157
158 static int emu_save_load_game(int load, int unused)
159 {
160         int ret;
161
162         if (load) {
163                 ret = emu_load_state(state_slot);
164
165                 // reflect hle/bios mode from savestate
166                 if (Config.HLE)
167                         bios_sel = 0;
168                 else if (bios_sel == 0 && bioses[1] != NULL)
169                         // XXX: maybe find the right bios instead
170                         bios_sel = 1;
171         }
172         else
173                 ret = emu_save_state(state_slot);
174
175         return ret;
176 }
177
178 // propagate menu settings to the emu vars
179 static void menu_sync_config(void)
180 {
181         static int allow_abs_only_old;
182
183         Config.PsxAuto = 1;
184         if (region > 0) {
185                 Config.PsxAuto = 0;
186                 Config.PsxType = region - 1;
187         }
188         cycle_multiplier = 10000 / psx_clock;
189
190         switch (in_type_sel1) {
191         case 1:  in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
192         case 2:  in_type1 = PSE_PAD_TYPE_GUNCON;    break;
193         default: in_type1 = PSE_PAD_TYPE_STANDARD;
194         }
195         switch (in_type_sel2) {
196         case 1:  in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
197         case 2:  in_type2 = PSE_PAD_TYPE_GUNCON;    break;
198         default: in_type2 = PSE_PAD_TYPE_STANDARD;
199         }
200         if (in_evdev_allow_abs_only != allow_abs_only_old) {
201                 in_probe();
202                 allow_abs_only_old = in_evdev_allow_abs_only;
203         }
204
205         iVolume = 768 + 128 * volume_boost;
206         pl_rearmed_cbs.frameskip = frameskip - 1;
207         pl_timing_prepare(Config.PsxType);
208 }
209
210 static void menu_set_defconfig(void)
211 {
212         emu_set_default_config();
213
214         g_opts = 0;
215         scaling = SCALE_4_3;
216         volume_boost = 0;
217         frameskip = 0;
218         analog_deadzone = 50;
219         psx_clock = DEFAULT_PSX_CLOCK;
220
221         region = 0;
222         in_type_sel1 = in_type_sel2 = 0;
223         in_evdev_allow_abs_only = 0;
224
225         menu_sync_config();
226 }
227
228 #define CE_CONFIG_STR(val) \
229         { #val, 0, Config.val }
230
231 #define CE_CONFIG_VAL(val) \
232         { #val, sizeof(Config.val), &Config.val }
233
234 #define CE_STR(val) \
235         { #val, 0, val }
236
237 #define CE_INTVAL(val) \
238         { #val, sizeof(val), &val }
239
240 #define CE_INTVAL_P(val) \
241         { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
242
243 // 'versioned' var, used when defaults change
244 #define CE_INTVAL_V(val, ver) \
245         { #val #ver, sizeof(val), &val }
246
247 #define CE_INTVAL_PV(val, ver) \
248         { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
249
250 static const struct {
251         const char *name;
252         size_t len;
253         void *val;
254 } config_data[] = {
255         CE_CONFIG_STR(Bios),
256         CE_CONFIG_STR(Gpu),
257         CE_CONFIG_STR(Spu),
258 //      CE_CONFIG_STR(Cdr),
259         CE_CONFIG_VAL(Xa),
260         CE_CONFIG_VAL(Sio),
261         CE_CONFIG_VAL(Mdec),
262         CE_CONFIG_VAL(Cdda),
263         CE_CONFIG_VAL(Debug),
264         CE_CONFIG_VAL(PsxOut),
265         CE_CONFIG_VAL(SpuIrq),
266         CE_CONFIG_VAL(RCntFix),
267         CE_CONFIG_VAL(VSyncWA),
268         CE_CONFIG_VAL(Cpu),
269         CE_CONFIG_VAL(CdrReschedule),
270         CE_INTVAL(region),
271         CE_INTVAL_V(scaling, 2),
272         CE_INTVAL(g_layer_x),
273         CE_INTVAL(g_layer_y),
274         CE_INTVAL(g_layer_w),
275         CE_INTVAL(g_layer_h),
276         CE_INTVAL(filter),
277         CE_INTVAL(state_slot),
278         CE_INTVAL(cpu_clock),
279         CE_INTVAL(g_opts),
280         CE_INTVAL(in_type_sel1),
281         CE_INTVAL(in_type_sel2),
282         CE_INTVAL(analog_deadzone),
283         CE_INTVAL_V(frameskip, 3),
284         CE_INTVAL_P(gpu_peops.iUseDither),
285         CE_INTVAL_P(gpu_peops.dwActFixes),
286         CE_INTVAL_P(gpu_unai.abe_hack),
287         CE_INTVAL_P(gpu_unai.no_light),
288         CE_INTVAL_P(gpu_unai.no_blend),
289         CE_INTVAL_V(iUseReverb, 3),
290         CE_INTVAL_V(iXAPitch, 3),
291         CE_INTVAL_V(iUseInterpolation, 3),
292         CE_INTVAL_V(iSPUIRQWait, 3),
293         CE_INTVAL_V(iUseTimer, 3),
294         CE_INTVAL(warned_about_bios),
295         CE_INTVAL(in_evdev_allow_abs_only),
296         CE_INTVAL(volume_boost),
297         CE_INTVAL(psx_clock),
298         CE_INTVAL(new_dynarec_hacks),
299         CE_INTVAL(in_enable_vibration),
300 };
301
302 static char *get_cd_label(void)
303 {
304         static char trimlabel[33];
305         int j;
306
307         strncpy(trimlabel, CdromLabel, 32);
308         trimlabel[32] = 0;
309         for (j = 31; j >= 0; j--)
310                 if (trimlabel[j] == ' ')
311                         trimlabel[j] = 0;
312
313         return trimlabel;
314 }
315
316 static void make_cfg_fname(char *buf, size_t size, int is_game)
317 {
318         if (is_game)
319                 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
320         else
321                 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
322 }
323
324 static void keys_write_all(FILE *f);
325
326 static int menu_write_config(int is_game)
327 {
328         char cfgfile[MAXPATHLEN];
329         FILE *f;
330         int i;
331
332         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
333         f = fopen(cfgfile, "w");
334         if (f == NULL) {
335                 printf("menu_write_config: failed to open: %s\n", cfgfile);
336                 return -1;
337         }
338
339         for (i = 0; i < ARRAY_SIZE(config_data); i++) {
340                 fprintf(f, "%s = ", config_data[i].name);
341                 switch (config_data[i].len) {
342                 case 0:
343                         fprintf(f, "%s\n", (char *)config_data[i].val);
344                         break;
345                 case 1:
346                         fprintf(f, "%x\n", *(u8 *)config_data[i].val);
347                         break;
348                 case 2:
349                         fprintf(f, "%x\n", *(u16 *)config_data[i].val);
350                         break;
351                 case 4:
352                         fprintf(f, "%x\n", *(u32 *)config_data[i].val);
353                         break;
354                 default:
355                         printf("menu_write_config: unhandled len %d for %s\n",
356                                  config_data[i].len, config_data[i].name);
357                         break;
358                 }
359         }
360
361         if (!is_game)
362                 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
363
364         keys_write_all(f);
365         fclose(f);
366
367         return 0;
368 }
369
370 static void parse_str_val(char *cval, const char *src)
371 {
372         char *tmp;
373         strncpy(cval, src, MAXPATHLEN);
374         cval[MAXPATHLEN - 1] = 0;
375         tmp = strchr(cval, '\n');
376         if (tmp == NULL)
377                 tmp = strchr(cval, '\r');
378         if (tmp != NULL)
379                 *tmp = 0;
380 }
381
382 static void keys_load_all(const char *cfg);
383
384 static int menu_load_config(int is_game)
385 {
386         char cfgfile[MAXPATHLEN];
387         int i, ret = -1;
388         long size;
389         char *cfg;
390         FILE *f;
391
392         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
393         f = fopen(cfgfile, "r");
394         if (f == NULL) {
395                 printf("menu_load_config: failed to open: %s\n", cfgfile);
396                 goto fail;
397         }
398
399         fseek(f, 0, SEEK_END);
400         size = ftell(f);
401         if (size <= 0) {
402                 printf("bad size %ld: %s\n", size, cfgfile);
403                 goto fail;
404         }
405
406         cfg = malloc(size + 1);
407         if (cfg == NULL)
408                 goto fail;
409
410         fseek(f, 0, SEEK_SET);
411         if (fread(cfg, 1, size, f) != size) {
412                 printf("failed to read: %s\n", cfgfile);
413                 goto fail_read;
414         }
415         cfg[size] = 0;
416
417         for (i = 0; i < ARRAY_SIZE(config_data); i++) {
418                 char *tmp, *tmp2;
419                 u32 val;
420
421                 tmp = strstr(cfg, config_data[i].name);
422                 if (tmp == NULL)
423                         continue;
424                 tmp += strlen(config_data[i].name);
425                 if (strncmp(tmp, " = ", 3) != 0)
426                         continue;
427                 tmp += 3;
428
429                 if (config_data[i].len == 0) {
430                         parse_str_val(config_data[i].val, tmp);
431                         continue;
432                 }
433
434                 tmp2 = NULL;
435                 val = strtoul(tmp, &tmp2, 16);
436                 if (tmp2 == NULL || tmp == tmp2)
437                         continue; // parse failed
438
439                 switch (config_data[i].len) {
440                 case 1:
441                         *(u8 *)config_data[i].val = val;
442                         break;
443                 case 2:
444                         *(u16 *)config_data[i].val = val;
445                         break;
446                 case 4:
447                         *(u32 *)config_data[i].val = val;
448                         break;
449                 default:
450                         printf("menu_load_config: unhandled len %d for %s\n",
451                                  config_data[i].len, config_data[i].name);
452                         break;
453                 }
454         }
455
456         if (!is_game) {
457                 char *tmp = strstr(cfg, "lastcdimg = ");
458                 if (tmp != NULL) {
459                         tmp += 12;
460                         parse_str_val(last_selected_fname, tmp);
461                 }
462         }
463
464         keys_load_all(cfg);
465         ret = 0;
466 fail_read:
467         free(cfg);
468 fail:
469         if (f != NULL)
470                 fclose(f);
471
472         menu_sync_config();
473
474         // sync plugins
475         for (i = bios_sel = 0; bioses[i] != NULL; i++)
476                 if (strcmp(Config.Bios, bioses[i]) == 0)
477                         { bios_sel = i; break; }
478
479         for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
480                 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
481                         { gpu_plugsel = i; break; }
482
483         for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
484                 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
485                         { spu_plugsel = i; break; }
486
487         return ret;
488 }
489
490 // rrrr rggg gggb bbbb
491 static unsigned short fname2color(const char *fname)
492 {
493         static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z",
494                                             ".bz", ".znx", ".pbp", ".cbn" };
495         static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub",
496                                             ".table", ".index", ".sbi" };
497         const char *ext = strrchr(fname, '.');
498         int i;
499
500         if (ext == NULL)
501                 return 0xffff;
502         for (i = 0; i < array_size(cdimg_exts); i++)
503                 if (strcasecmp(ext, cdimg_exts[i]) == 0)
504                         return 0x7bff;
505         for (i = 0; i < array_size(other_exts); i++)
506                 if (strcasecmp(ext, other_exts[i]) == 0)
507                         return 0xa514;
508         return 0xffff;
509 }
510
511 static void draw_savestate_bg(int slot);
512
513 static const char *filter_exts[] = {
514         ".mp3", ".MP3", ".txt", ".htm", "html", ".jpg", ".pnd"
515 };
516
517 #define MENU_ALIGN_LEFT
518 #ifdef __ARM_ARCH_7A__ // assume hires device
519 #define MENU_X2 1
520 #else
521 #define MENU_X2 0
522 #endif
523
524 #define menu_init menu_init_common
525 #include "common/menu.c"
526 #undef menu_init
527
528 // a bit of black magic here
529 static void draw_savestate_bg(int slot)
530 {
531         static const int psx_widths[8]  = { 256, 368, 320, 384, 512, 512, 640, 640 };
532         int x, y, w, h;
533         char fname[MAXPATHLEN];
534         GPUFreeze_t *gpu;
535         u16 *s, *d;
536         gzFile f;
537         int ret;
538         u32 tmp;
539
540         ret = get_state_filename(fname, sizeof(fname), slot);
541         if (ret != 0)
542                 return;
543
544         f = gzopen(fname, "rb");
545         if (f == NULL)
546                 return;
547
548         if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
549                 fprintf(stderr, "gzseek failed\n");
550                 gzclose(f);
551                 return;
552         }
553
554         gpu = malloc(sizeof(*gpu));
555         if (gpu == NULL) {
556                 gzclose(f);
557                 return;
558         }
559
560         ret = gzread(f, gpu, sizeof(*gpu));
561         gzclose(f);
562         if (ret != sizeof(*gpu)) {
563                 fprintf(stderr, "gzread failed\n");
564                 goto out;
565         }
566
567         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
568
569         if (gpu->ulStatus & 0x800000)
570                 goto out; // disabled
571
572         x = gpu->ulControl[5] & 0x3ff;
573         y = (gpu->ulControl[5] >> 10) & 0x1ff;
574         s = (u16 *)gpu->psxVRam + y * 1024 + (x & ~1);
575         w = psx_widths[(gpu->ulStatus >> 16) & 7];
576         tmp = gpu->ulControl[7];
577         h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
578         if (gpu->ulStatus & 0x80000) // doubleheight
579                 h *= 2;
580
581         x = max(0, g_menuscreen_w - w) & ~3;
582         y = max(0, g_menuscreen_h / 2 - h / 2);
583         w = min(g_menuscreen_w, w);
584         h = min(g_menuscreen_h, h);
585         d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
586
587         for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
588                 if (gpu->ulStatus & 0x200000)
589                         bgr888_to_rgb565(d, s, w * 3);
590                 else
591                         bgr555_to_rgb565(d, s, w * 2);
592 #ifndef __ARM_ARCH_7A__
593                 // better darken this on small screens
594                 menu_darken_bg(d, d, w * 2, 0);
595 #endif
596         }
597
598 out:
599         free(gpu);
600 }
601
602 // ---------- XXX: pandora specific -----------
603
604 static const char pnd_script_base[] = "sudo -n /usr/pandora/scripts";
605 static char **pnd_filter_list;
606
607 static void apply_filter(int which)
608 {
609         static int old = -1;
610         char buf[128];
611         int i;
612
613         if (pnd_filter_list == NULL || which == old)
614                 return;
615
616         for (i = 0; i < which; i++)
617                 if (pnd_filter_list[i] == NULL)
618                         return;
619
620         if (pnd_filter_list[i] == NULL)
621                 return;
622
623         snprintf(buf, sizeof(buf), "%s/op_videofir.sh %s", pnd_script_base, pnd_filter_list[i]);
624         system(buf);
625         old = which;
626 }
627
628 static void apply_lcdrate(int pal)
629 {
630         static int old = -1;
631         char buf[128];
632
633         if (pal == old)
634                 return;
635
636         snprintf(buf, sizeof(buf), "%s/op_lcdrate.sh %d",
637                         pnd_script_base, pal ? 50 : 60);
638         system(buf);
639         old = pal;
640 }
641
642 static menu_entry e_menu_gfx_options[];
643
644 static void pnd_menu_init(void)
645 {
646         struct dirent *ent;
647         int i, count = 0;
648         char **mfilters;
649         char buff[64];
650         DIR *dir;
651
652         cpu_clock_st = cpu_clock = plat_cpu_clock_get();
653
654         dir = opendir("/etc/pandora/conf/dss_fir");
655         if (dir == NULL) {
656                 perror("filter opendir");
657                 return;
658         }
659
660         while (1) {
661                 errno = 0;
662                 ent = readdir(dir);
663                 if (ent == NULL) {
664                         if (errno != 0)
665                                 perror("readdir");
666                         break;
667                 }
668
669                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
670                         continue;
671
672                 count++;
673         }
674
675         if (count == 0)
676                 return;
677
678         mfilters = calloc(count + 1, sizeof(mfilters[0]));
679         if (mfilters == NULL)
680                 return;
681
682         rewinddir(dir);
683         for (i = 0; (ent = readdir(dir)); ) {
684                 size_t len;
685
686                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
687                         continue;
688
689                 len = strlen(ent->d_name);
690
691                 // skip pre-HF5 extra files
692                 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
693                         continue;
694                 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
695                         continue;
696
697                 // have to cut "_up_h" for pre-HF5
698                 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
699                         len -= 5;
700
701                 if (len > sizeof(buff) - 1)
702                         continue;
703
704                 strncpy(buff, ent->d_name, len);
705                 buff[len] = 0;
706                 mfilters[i] = strdup(buff);
707                 if (mfilters[i] != NULL)
708                         i++;
709         }
710         closedir(dir);
711
712         i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
713         e_menu_gfx_options[i].data = (void *)mfilters;
714         pnd_filter_list = mfilters;
715 }
716
717 void menu_finish(void)
718 {
719         plat_cpu_clock_apply(cpu_clock_st);
720 }
721
722 // -------------- key config --------------
723
724 me_bind_action me_ctrl_actions[] =
725 {
726         { "UP      ", 1 << DKEY_UP},
727         { "DOWN    ", 1 << DKEY_DOWN },
728         { "LEFT    ", 1 << DKEY_LEFT },
729         { "RIGHT   ", 1 << DKEY_RIGHT },
730         { "TRIANGLE", 1 << DKEY_TRIANGLE },
731         { "CIRCLE  ", 1 << DKEY_CIRCLE },
732         { "CROSS   ", 1 << DKEY_CROSS },
733         { "SQUARE  ", 1 << DKEY_SQUARE },
734         { "L1      ", 1 << DKEY_L1 },
735         { "R1      ", 1 << DKEY_R1 },
736         { "L2      ", 1 << DKEY_L2 },
737         { "R2      ", 1 << DKEY_R2 },
738         { "L3      ", 1 << DKEY_L3 },
739         { "R3      ", 1 << DKEY_R3 },
740         { "START   ", 1 << DKEY_START },
741         { "SELECT  ", 1 << DKEY_SELECT },
742         { NULL,       0 }
743 };
744
745 me_bind_action emuctrl_actions[] =
746 {
747         { "Save State       ", 1 << SACTION_SAVE_STATE },
748         { "Load State       ", 1 << SACTION_LOAD_STATE },
749         { "Prev Save Slot   ", 1 << SACTION_PREV_SSLOT },
750         { "Next Save Slot   ", 1 << SACTION_NEXT_SSLOT },
751         { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
752         { "Take Screenshot  ", 1 << SACTION_SCREENSHOT },
753         { "Enter Menu       ", 1 << SACTION_ENTER_MENU },
754         { "Gun Trigger      ", 1 << SACTION_GUN_TRIGGER },
755         { "Gun A button     ", 1 << SACTION_GUN_A },
756         { "Gun B button     ", 1 << SACTION_GUN_B },
757         { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
758 #ifndef __ARM_ARCH_7A__ /* XXX */
759         { "Volume Up        ", 1 << SACTION_VOLUME_UP },
760         { "Volume Down      ", 1 << SACTION_VOLUME_DOWN },
761 #endif
762         { NULL,                0 }
763 };
764
765 static char *mystrip(char *str)
766 {
767         int i, len;
768
769         len = strlen(str);
770         for (i = 0; i < len; i++)
771                 if (str[i] != ' ') break;
772         if (i > 0) memmove(str, str + i, len - i + 1);
773
774         len = strlen(str);
775         for (i = len - 1; i >= 0; i--)
776                 if (str[i] != ' ') break;
777         str[i+1] = 0;
778
779         return str;
780 }
781
782 static void get_line(char *d, size_t size, const char *s)
783 {
784         const char *pe;
785         size_t len;
786
787         for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
788                 ;
789         len = pe - s;
790         if (len > size - 1)
791                 len = size - 1;
792         strncpy(d, s, len);
793         d[len] = 0;
794
795         mystrip(d);
796 }
797
798 static void keys_write_all(FILE *f)
799 {
800         int d;
801
802         for (d = 0; d < IN_MAX_DEVS; d++)
803         {
804                 const int *binds = in_get_dev_binds(d);
805                 const char *name = in_get_dev_name(d, 0, 0);
806                 int k, count = 0;
807
808                 if (binds == NULL || name == NULL)
809                         continue;
810
811                 fprintf(f, "binddev = %s\n", name);
812                 in_get_config(d, IN_CFG_BIND_COUNT, &count);
813
814                 for (k = 0; k < count; k++)
815                 {
816                         int i, kbinds, mask;
817                         char act[32];
818
819                         act[0] = act[31] = 0;
820                         name = in_get_key_name(d, k);
821
822                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
823                         for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
824                                 mask = me_ctrl_actions[i].mask;
825                                 if (mask & kbinds) {
826                                         strncpy(act, me_ctrl_actions[i].name, 31);
827                                         fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
828                                         kbinds &= ~mask;
829                                 }
830                                 mask = me_ctrl_actions[i].mask << 16;
831                                 if (mask & kbinds) {
832                                         strncpy(act, me_ctrl_actions[i].name, 31);
833                                         fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
834                                         kbinds &= ~mask;
835                                 }
836                         }
837
838                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
839                         for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
840                                 mask = emuctrl_actions[i].mask;
841                                 if (mask & kbinds) {
842                                         strncpy(act, emuctrl_actions[i].name, 31);
843                                         fprintf(f, "bind %s = %s\n", name, mystrip(act));
844                                         kbinds &= ~mask;
845                                 }
846                         }
847                 }
848
849                 for (k = 0; k < array_size(in_adev); k++)
850                 {
851                         if (in_adev[k] == d)
852                                 fprintf(f, "bind_analog = %d\n", k);
853                 }
854         }
855 }
856
857 static int parse_bind_val(const char *val, int *type)
858 {
859         int i;
860
861         *type = IN_BINDTYPE_NONE;
862         if (val[0] == 0)
863                 return 0;
864         
865         if (strncasecmp(val, "player", 6) == 0)
866         {
867                 int player, shift = 0;
868                 player = atoi(val + 6) - 1;
869
870                 if ((unsigned int)player > 1)
871                         return -1;
872                 if (player == 1)
873                         shift = 16;
874
875                 *type = IN_BINDTYPE_PLAYER12;
876                 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
877                         if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
878                                 return me_ctrl_actions[i].mask << shift;
879                 }
880         }
881         for (i = 0; emuctrl_actions[i].name != NULL; i++) {
882                 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
883                         *type = IN_BINDTYPE_EMU;
884                         return emuctrl_actions[i].mask;
885                 }
886         }
887
888         return -1;
889 }
890
891 static void keys_load_all(const char *cfg)
892 {
893         char dev[256], key[128], *act;
894         const char *p;
895         int bind, bindtype;
896         int ret, dev_id;
897
898         p = cfg;
899         while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
900                 p += 10;
901
902                 get_line(dev, sizeof(dev), p);
903                 dev_id = in_config_parse_dev(dev);
904                 if (dev_id < 0) {
905                         printf("input: can't handle dev: %s\n", dev);
906                         continue;
907                 }
908
909                 in_unbind_all(dev_id, -1, -1);
910                 while ((p = strstr(p, "bind"))) {
911                         if (strncmp(p, "binddev = ", 10) == 0)
912                                 break;
913
914                         if (strncmp(p, "bind_analog", 11) == 0) {
915                                 ret = sscanf(p, "bind_analog = %d", &bind);
916                                 p += 11;
917                                 if (ret != 1) {
918                                         printf("input: parse error: %16s..\n", p);
919                                         continue;
920                                 }
921                                 if ((unsigned int)bind >= array_size(in_adev)) {
922                                         printf("input: analog id %d out of range\n", bind);
923                                         continue;
924                                 }
925                                 in_adev[bind] = dev_id;
926                                 continue;
927                         }
928
929                         p += 4;
930                         if (*p != ' ') {
931                                 printf("input: parse error: %16s..\n", p);
932                                 continue;
933                         }
934
935                         get_line(key, sizeof(key), p);
936                         act = strchr(key, '=');
937                         if (act == NULL) {
938                                 printf("parse failed: %16s..\n", p);
939                                 continue;
940                         }
941                         *act = 0;
942                         act++;
943                         mystrip(key);
944                         mystrip(act);
945
946                         bind = parse_bind_val(act, &bindtype);
947                         if (bind != -1 && bind != 0) {
948                                 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
949                                 in_config_bind_key(dev_id, key, bind, bindtype);
950                         }
951                         else
952                                 lprintf("config: unhandled action \"%s\"\n", act);
953                 }
954         }
955         in_clean_binds();
956 }
957
958 static int key_config_loop_wrap(int id, int keys)
959 {
960         switch (id) {
961                 case MA_CTRL_PLAYER1:
962                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
963                         break;
964                 case MA_CTRL_PLAYER2:
965                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
966                         break;
967                 case MA_CTRL_EMU:
968                         key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
969                         break;
970                 default:
971                         break;
972         }
973         return 0;
974 }
975
976 static const char *adevnames[IN_MAX_DEVS + 2];
977 static int stick_sel[2];
978
979 static menu_entry e_menu_keyconfig_analog[] =
980 {
981         mee_enum ("Left stick (L3)",  0, stick_sel[0], adevnames),
982         mee_range("  X axis",    0, in_adev_axis[0][0], 0, 7),
983         mee_range("  Y axis",    0, in_adev_axis[0][1], 0, 7),
984         mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
985         mee_range("  X axis",    0, in_adev_axis[1][0], 0, 7),
986         mee_range("  Y axis",    0, in_adev_axis[1][1], 0, 7),
987         mee_end,
988 };
989
990 static int key_config_analog(int id, int keys)
991 {
992         int i, d, count, sel = 0;
993         int sel2dev_map[IN_MAX_DEVS];
994
995         memset(adevnames, 0, sizeof(adevnames));
996         memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
997         memset(stick_sel, 0, sizeof(stick_sel));
998
999         adevnames[0] = "None";
1000         i = 1;
1001         for (d = 0; d < IN_MAX_DEVS; d++)
1002         {
1003                 const char *name = in_get_dev_name(d, 0, 1);
1004                 if (name == NULL)
1005                         continue;
1006
1007                 count = 0;
1008                 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1009                 if (count == 0)
1010                         continue;
1011
1012                 if (in_adev[0] == d) stick_sel[0] = i;
1013                 if (in_adev[1] == d) stick_sel[1] = i;
1014                 sel2dev_map[i] = d;
1015                 adevnames[i++] = name;
1016         }
1017         adevnames[i] = NULL;
1018
1019         me_loop(e_menu_keyconfig_analog, &sel);
1020
1021         in_adev[0] = sel2dev_map[stick_sel[0]];
1022         in_adev[1] = sel2dev_map[stick_sel[1]];
1023
1024         return 0;
1025 }
1026
1027 static const char *mgn_dev_name(int id, int *offs)
1028 {
1029         const char *name = NULL;
1030         static int it = 0;
1031
1032         if (id == MA_CTRL_DEV_FIRST)
1033                 it = 0;
1034
1035         for (; it < IN_MAX_DEVS; it++) {
1036                 name = in_get_dev_name(it, 1, 1);
1037                 if (name != NULL)
1038                         break;
1039         }
1040
1041         it++;
1042         return name;
1043 }
1044
1045 static const char *mgn_saveloadcfg(int id, int *offs)
1046 {
1047         return "";
1048 }
1049
1050 static int mh_savecfg(int id, int keys)
1051 {
1052         if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1053                 me_update_msg("config saved");
1054         else
1055                 me_update_msg("failed to write config");
1056
1057         return 1;
1058 }
1059
1060 static int mh_input_rescan(int id, int keys)
1061 {
1062         //menu_sync_config();
1063         in_probe();
1064         me_update_msg("rescan complete.");
1065
1066         return 0;
1067 }
1068
1069 static const char *men_in_type_sel[] = {
1070         "Standard (SCPH-1080)",
1071         "Analog (SCPH-1150)",
1072         "GunCon",
1073         NULL
1074 };
1075 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
1076 static const char h_notsgun[]  = "Don't trigger (shoot) when touching screen in gun games.";
1077 static const char h_vibration[]= "Must select analog above and enable this ingame too.";
1078
1079 static menu_entry e_menu_keyconfig[] =
1080 {
1081         mee_handler_id("Player 1",              MA_CTRL_PLAYER1,    key_config_loop_wrap),
1082         mee_handler_id("Player 2",              MA_CTRL_PLAYER2,    key_config_loop_wrap),
1083         mee_handler_id("Analog controls",       MA_CTRL_ANALOG,     key_config_analog),
1084         mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU,        key_config_loop_wrap),
1085         mee_label     (""),
1086         mee_enum      ("Port 1 device",     0, in_type_sel1,    men_in_type_sel),
1087         mee_enum      ("Port 2 device",     0, in_type_sel2,    men_in_type_sel),
1088         mee_onoff_h   ("Nubs as buttons",   MA_CTRL_NUBS_BTNS,  in_evdev_allow_abs_only, 1, h_nub_btns),
1089         mee_onoff_h   ("Vibration",         MA_CTRL_VIBRATION,  in_enable_vibration, 1, h_vibration),
1090         mee_range     ("Analog deadzone",   MA_CTRL_DEADZONE,   analog_deadzone, 1, 99),
1091         mee_onoff_h   ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
1092         mee_cust_nosave("Save global config",       MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1093         mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1094         mee_handler   ("Rescan devices:",  mh_input_rescan),
1095         mee_label     (""),
1096         mee_label_mk  (MA_CTRL_DEV_FIRST, mgn_dev_name),
1097         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1098         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1099         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1100         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1101         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1102         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1103         mee_end,
1104 };
1105
1106 static int menu_loop_keyconfig(int id, int keys)
1107 {
1108         static int sel = 0;
1109
1110 //      me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1111         me_loop(e_menu_keyconfig, &sel);
1112         return 0;
1113 }
1114
1115 // ------------ gfx options menu ------------
1116
1117 static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
1118 static const char h_cscaler[]   = "Displays the scaler layer, you can resize it\n"
1119                                   "using d-pad or move it using R+d-pad";
1120 static const char *men_dummy[] = { NULL };
1121
1122 static int menu_loop_cscaler(int id, int keys)
1123 {
1124         unsigned int inp;
1125
1126         scaling = SCALE_CUSTOM;
1127
1128         omap_enable_layer(1);
1129
1130         for (;;)
1131         {
1132                 menu_draw_begin(0);
1133                 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1134                 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1135                 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1136                 menu_draw_end();
1137
1138                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
1139                 if (inp & PBTN_UP)    g_layer_y--;
1140                 if (inp & PBTN_DOWN)  g_layer_y++;
1141                 if (inp & PBTN_LEFT)  g_layer_x--;
1142                 if (inp & PBTN_RIGHT) g_layer_x++;
1143                 if (!(inp & PBTN_R)) {
1144                         if (inp & PBTN_UP)    g_layer_h += 2;
1145                         if (inp & PBTN_DOWN)  g_layer_h -= 2;
1146                         if (inp & PBTN_LEFT)  g_layer_w += 2;
1147                         if (inp & PBTN_RIGHT) g_layer_w -= 2;
1148                 }
1149                 if (inp & (PBTN_MOK|PBTN_MBACK))
1150                         break;
1151
1152                 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1153                         if (g_layer_x < 0)   g_layer_x = 0;
1154                         if (g_layer_x > 640) g_layer_x = 640;
1155                         if (g_layer_y < 0)   g_layer_y = 0;
1156                         if (g_layer_y > 420) g_layer_y = 420;
1157                         if (g_layer_w < 160) g_layer_w = 160;
1158                         if (g_layer_h < 60)  g_layer_h = 60;
1159                         if (g_layer_x + g_layer_w > 800)
1160                                 g_layer_w = 800 - g_layer_x;
1161                         if (g_layer_y + g_layer_h > 480)
1162                                 g_layer_h = 480 - g_layer_y;
1163                         omap_enable_layer(1);
1164                 }
1165         }
1166
1167         omap_enable_layer(0);
1168
1169         return 0;
1170 }
1171
1172 static menu_entry e_menu_gfx_options[] =
1173 {
1174         mee_enum      ("Scaler",                   0, scaling, men_scaler),
1175         mee_enum      ("Filter",                   MA_OPT_FILTERING, filter, men_dummy),
1176 //      mee_onoff     ("Vsync",                    0, vsync, 1),
1177         mee_cust_h    ("Setup custom scaler",      0, menu_loop_cscaler, NULL, h_cscaler),
1178         mee_end,
1179 };
1180
1181 static int menu_loop_gfx_options(int id, int keys)
1182 {
1183         static int sel = 0;
1184
1185         me_loop(e_menu_gfx_options, &sel);
1186
1187         return 0;
1188 }
1189
1190 // ------------ bios/plugins ------------
1191
1192 static menu_entry e_menu_plugin_gpu_unai[] =
1193 {
1194         mee_onoff     ("Abe's Odyssey hack",         0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1195         mee_onoff     ("Disable lighting",           0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1196         mee_onoff     ("Disable blending",           0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1197         mee_end,
1198 };
1199
1200 static int menu_loop_plugin_gpu_unai(int id, int keys)
1201 {
1202         int sel = 0;
1203         me_loop(e_menu_plugin_gpu_unai, &sel);
1204         return 0;
1205 }
1206
1207 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1208 static const char h_gpu_0[]            = "Needed for Chrono Cross";
1209 static const char h_gpu_1[]            = "Capcom fighting games";
1210 static const char h_gpu_2[]            = "Black screens in Lunar";
1211 static const char h_gpu_3[]            = "Compatibility mode";
1212 static const char h_gpu_6[]            = "Pandemonium 2";
1213 static const char h_gpu_7[]            = "Skip every second frame";
1214 static const char h_gpu_8[]            = "Needed by Dark Forces";
1215 static const char h_gpu_9[]            = "better g-colors, worse textures";
1216 static const char h_gpu_10[]           = "Toggle busy flags after drawing";
1217
1218 static menu_entry e_menu_plugin_gpu_peops[] =
1219 {
1220         mee_enum      ("Dithering",                  0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1221         mee_onoff_h   ("Odd/even bit hack",          0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1222         mee_onoff_h   ("Expand screen width",        0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1223         mee_onoff_h   ("Ignore brightness color",    0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1224         mee_onoff_h   ("Disable coordinate check",   0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1225         mee_onoff_h   ("Lazy screen update",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1226         mee_onoff_h   ("Old frame skipping",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1227         mee_onoff_h   ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1228         mee_onoff_h   ("Draw quads with triangles",  0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1229         mee_onoff_h   ("Fake 'gpu busy' states",     0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1230         mee_end,
1231 };
1232
1233 static int menu_loop_plugin_gpu_peops(int id, int keys)
1234 {
1235         static int sel = 0;
1236         me_loop(e_menu_plugin_gpu_peops, &sel);
1237         return 0;
1238 }
1239
1240 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1241 static const char h_spu_volboost[]  = "Large values cause distortion";
1242 static const char h_spu_irq_wait[]  = "Wait for CPU (recommended set to ON)";
1243 static const char h_spu_thread[]    = "Run sound emulation in main thread (recommended)";
1244
1245 static menu_entry e_menu_plugin_spu[] =
1246 {
1247         mee_range_h   ("Volume boost",              0, volume_boost, -5, 30, h_spu_volboost),
1248         mee_onoff     ("Reverb",                    0, iUseReverb, 2),
1249         mee_enum      ("Interpolation",             0, iUseInterpolation, men_spu_interp),
1250         mee_onoff     ("Adjust XA pitch",           0, iXAPitch, 1),
1251         mee_onoff_h   ("SPU IRQ Wait",              0, iSPUIRQWait, 1, h_spu_irq_wait),
1252         mee_onoff_h   ("Sound in main thread",      0, iUseTimer, 2, h_spu_thread),
1253         mee_end,
1254 };
1255
1256 static int menu_loop_plugin_spu(int id, int keys)
1257 {
1258         static int sel = 0;
1259         me_loop(e_menu_plugin_spu, &sel);
1260         return 0;
1261 }
1262
1263 static const char h_bios[]       = "HLE is simulated BIOS. BIOS selection is saved in\n"
1264                                    "savestates and can't be changed there. Must save\n"
1265                                    "config and reload the game for change to take effect";
1266 static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1267                                    "for plugin change to take effect";
1268 static const char h_gpu_peops[]  = "Configure P.E.Op.S. SoftGL Driver V1.17";
1269 static const char h_gpu_unai[]   = "Configure Unai/PCSX4ALL Team GPU plugin";
1270 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1271
1272 static menu_entry e_menu_plugin_options[] =
1273 {
1274         mee_enum_h    ("BIOS",                          0, bios_sel, bioses, h_bios),
1275         mee_enum_h    ("GPU plugin",                    0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1276         mee_enum_h    ("SPU plugin",                    0, spu_plugsel, spu_plugins, h_plugin_xpu),
1277         mee_handler_h ("Configure gpu_peops plugin",    menu_loop_plugin_gpu_peops, h_gpu_peops),
1278         mee_handler_h ("Configure PCSX4ALL GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1279         mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1280         mee_end,
1281 };
1282
1283 static menu_entry e_menu_main2[];
1284
1285 static int menu_loop_plugin_options(int id, int keys)
1286 {
1287         static int sel = 0;
1288         me_loop(e_menu_plugin_options, &sel);
1289
1290         // sync BIOS/plugins
1291         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1292         snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1293         snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1294         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1295
1296         return 0;
1297 }
1298
1299 // ------------ adv options menu ------------
1300
1301 static const char h_cfg_psxclk[]  = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n";
1302 static const char h_cfg_nosmc[]   = "Will cause crashes when loading, break memcards";
1303 static const char h_cfg_gteunn[]  = "May cause graphical glitches";
1304 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1305
1306 static menu_entry e_menu_speed_hacks[] =
1307 {
1308         mee_range_h   ("PSX CPU clock, %%",        0, psx_clock, 1, 500, h_cfg_psxclk),
1309         mee_onoff_h   ("Disable SMC checks",       0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1310         mee_onoff_h   ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1311         mee_onoff_h   ("Disable GTE flags",        0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1312         mee_end,
1313 };
1314
1315 static int menu_loop_speed_hacks(int id, int keys)
1316 {
1317         static int sel = 0;
1318         me_loop(e_menu_speed_hacks, &sel);
1319         return 0;
1320 }
1321
1322 static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
1323 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
1324 static const char h_cfg_spu[]    = "Shows active SPU channels\n"
1325                                    "(green: normal, red: fmod, blue: noise)";
1326 static const char h_cfg_fl[]     = "Frame Limiter keeps the game from running too fast";
1327 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
1328 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
1329                                    "(proper .cue/.bin dump is needed otherwise)";
1330 static const char h_cfg_sio[]    = "You should not need this, breaks games";
1331 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1332 static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1333                                    "(timing hack, breaks other games)";
1334 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix\n"
1335                                    "(timing hack, breaks other games)";
1336 static const char h_cfg_cdrr[]   = "Compatibility tweak (CD timing hack, breaks FMVs)";
1337 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
1338                                    "Might be useful to overcome some dynarec bugs";
1339 static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
1340                                    "must reload game for any change to take effect";
1341
1342 static menu_entry e_menu_adv_options[] =
1343 {
1344         mee_onoff_h   ("Show CPU load",          0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1345         mee_onoff_h   ("Show SPU channels",      0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1346         mee_onoff_h   ("Disable Frame Limiter",  0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1347         mee_onoff_h   ("Disable XA Decoding",    0, Config.Xa, 1, h_cfg_xa),
1348         mee_onoff_h   ("Disable CD Audio",       0, Config.Cdda, 1, h_cfg_cdda),
1349         mee_onoff_h   ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1350         mee_onoff_h   ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1351         //mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
1352         mee_onoff_h   ("Rootcounter hack 2",     0, Config.VSyncWA, 1, h_cfg_rcnt2),
1353         mee_enum_h    ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
1354         mee_onoff_h   ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1355         mee_handler_h ("[Speed hacks]",             menu_loop_speed_hacks, h_cfg_shacks),
1356         mee_end,
1357 };
1358
1359 static int menu_loop_adv_options(int id, int keys)
1360 {
1361         static int sel = 0;
1362         me_loop(e_menu_adv_options, &sel);
1363         return 0;
1364 }
1365
1366 // ------------ options menu ------------
1367
1368 static int mh_restore_defaults(int id, int keys)
1369 {
1370         menu_set_defconfig();
1371         me_update_msg("defaults restored");
1372         return 1;
1373 }
1374
1375 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
1376 static const char *men_frameskip[]    = { "Auto", "Off", "1", "2", "3", NULL };
1377 /*
1378 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1379 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
1380                                         "loading state or both";
1381 */
1382 static const char h_restore_def[]     = "Switches back to default / recommended\n"
1383                                         "configuration";
1384 static const char h_frameskip[]       = "Warning: frameskip sometimes causes glitches\n";
1385
1386 static menu_entry e_menu_options[] =
1387 {
1388 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1389 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1390         mee_enum_h    ("Frameskip",                0, frameskip, men_frameskip, h_frameskip),
1391         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1392         mee_enum      ("Region",                   0, region, men_region),
1393         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1394         mee_handler_id("[Display]",                MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1395         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1396         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1397         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1398         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1399         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1400         mee_end,
1401 };
1402
1403 static int menu_loop_options(int id, int keys)
1404 {
1405         static int sel = 0;
1406         int i;
1407
1408         i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1409         e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1410         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1411
1412         me_loop(e_menu_options, &sel);
1413
1414         return 0;
1415 }
1416
1417 // ------------ debug menu ------------
1418
1419 static void draw_frame_debug(GPUFreeze_t *gpuf)
1420 {
1421         int w = min(g_menuscreen_w, 1024);
1422         int h = min(g_menuscreen_h, 512);
1423         u16 *d = g_menuscreen_ptr;
1424         u16 *s = (u16 *)gpuf->psxVRam;
1425         char buff[64];
1426         int ty = 1;
1427
1428         gpuf->ulFreezeVersion = 1;
1429         if (GPU_freeze != NULL)
1430                 GPU_freeze(1, gpuf);
1431
1432         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1433                 bgr555_to_rgb565(d, s, w * 2);
1434
1435         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1436         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1437         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1438         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1439         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1440 }
1441
1442 static void debug_menu_loop(void)
1443 {
1444         GPUFreeze_t *gpuf;
1445         int inp;
1446
1447         gpuf = malloc(sizeof(*gpuf));
1448         if (gpuf == NULL)
1449                 return;
1450
1451         while (1)
1452         {
1453                 menu_draw_begin(0);
1454                 draw_frame_debug(gpuf);
1455                 menu_draw_end();
1456
1457                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1458                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1459                 if (inp & PBTN_MBACK)
1460                         break;
1461         }
1462
1463         free(gpuf);
1464 }
1465
1466 // --------- memcard manager ---------
1467
1468 static void draw_mc_icon(int dx, int dy, const u16 *s)
1469 {
1470         u16 *d;
1471         int x, y, l, p;
1472         
1473         d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1474
1475         for (y = 0; y < 16; y++, s += 16) {
1476                 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1477                         for (x = 0; x < 16; x++) {
1478                                 p = s[x];
1479                                 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1480                                         | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1481                         }
1482                 }
1483         }
1484 }
1485
1486 static void draw_mc_bg(void)
1487 {
1488         McdBlock *blocks1, *blocks2;
1489         int maxicons = 15;
1490         int i, y, row2;
1491
1492         blocks1 = malloc(15 * sizeof(blocks1[0]));
1493         blocks2 = malloc(15 * sizeof(blocks1[0]));
1494         if (blocks1 == NULL || blocks2 == NULL)
1495                 goto out;
1496
1497         for (i = 0; i < 15; i++) {
1498                 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1499                 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1500         }
1501
1502         menu_draw_begin(1);
1503
1504         memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1505
1506         y = g_menuscreen_h / 2 - 15 * 32 / 2;
1507         if (y < 0) {
1508                 // doesn't fit..
1509                 y = 0;
1510                 maxicons = g_menuscreen_h / 32;
1511         }
1512
1513         row2 = g_menuscreen_w / 2;
1514         for (i = 0; i < maxicons; i++) {
1515                 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1516                 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1517
1518                 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1519                 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1520         }
1521
1522         menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1523
1524         menu_draw_end();
1525 out:
1526         free(blocks1);
1527         free(blocks2);
1528 }
1529
1530 static void handle_memcard_sel(void)
1531 {
1532         Config.Mcd1[0] = 0;
1533         if (memcard1_sel != 0)
1534                 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1535         Config.Mcd2[0] = 0;
1536         if (memcard2_sel != 0)
1537                 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1538         LoadMcds(Config.Mcd1, Config.Mcd2);
1539         draw_mc_bg();
1540 }
1541
1542 static menu_entry e_memcard_options[] =
1543 {
1544         mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1545         mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1546         mee_end,
1547 };
1548
1549 static int menu_loop_memcards(int id, int keys)
1550 {
1551         static int sel = 0;
1552         char *p;
1553         int i;
1554
1555         memcard1_sel = memcard2_sel = 0;
1556         p = strrchr(Config.Mcd1, '/');
1557         if (p != NULL)
1558                 for (i = 0; memcards[i] != NULL; i++)
1559                         if (strcmp(p + 1, memcards[i]) == 0)
1560                                 { memcard1_sel = i; break; }
1561         p = strrchr(Config.Mcd2, '/');
1562         if (p != NULL)
1563                 for (i = 0; memcards[i] != NULL; i++)
1564                         if (strcmp(p + 1, memcards[i]) == 0)
1565                                 { memcard2_sel = i; break; }
1566
1567         me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1568
1569         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1570
1571         return 0;
1572 }
1573
1574 // --------- main menu help ----------
1575
1576 static void menu_bios_warn(void)
1577 {
1578         int inp;
1579         static const char msg[] =
1580                 "You don't seem to have copied any BIOS\n"
1581                 "files to\n"
1582 #ifdef __ARM_ARCH_7A__ // XXX
1583                 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1584 #else
1585                 "pcsx_rearmed/bios/\n\n"
1586 #endif
1587                 "While many games work fine with fake\n"
1588                 "(HLE) BIOS, others (like MGS and FF8)\n"
1589                 "require BIOS to work.\n"
1590                 "After copying the file, you'll also need\n"
1591                 "to select it in the emu's menu:\n"
1592                 "options->[BIOS/Plugins]\n\n"
1593                 "The file is usually named SCPH1001.BIN,\n"
1594                 "but other not compressed files can be\n"
1595                 "used too.\n\n"
1596                 "Press (B) or (X) to continue";
1597
1598         while (1)
1599         {
1600                 draw_menu_message(msg, NULL);
1601
1602                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1603                 if (inp & (PBTN_MBACK|PBTN_MOK))
1604                         return;
1605         }
1606 }
1607
1608 // ------------ main menu ------------
1609
1610 void OnFile_Exit();
1611
1612 static void draw_frame_main(void)
1613 {
1614         struct tm *tmp;
1615         time_t ltime;
1616         char ltime_s[16];
1617         char buff[64];
1618
1619         if (CdromId[0] != 0) {
1620                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1621                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1622                          Config.HLE ? "HLE" : "BIOS");
1623                 smalltext_out16(4, 1, buff, 0x105f);
1624         }
1625
1626         if (ready_to_go) {
1627                 ltime = time(NULL);
1628                 tmp = localtime(&ltime);
1629                 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1630                 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, plat_get_bat_capacity());
1631                 smalltext_out16(4, 1 + me_sfont_h, buff, 0x105f);
1632         }
1633 }
1634
1635 static void draw_frame_credits(void)
1636 {
1637         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1638 }
1639
1640 static const char credits_text[] = 
1641         "PCSX-ReARMed\n\n"
1642         "(C) 1999-2003 PCSX Team\n"
1643         "(C) 2005-2009 PCSX-df Team\n"
1644         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1645         "GPU and SPU code by Pete Bernert\n"
1646         "  and the P.E.Op.S. team\n"
1647         "ARM recompiler (C) 2009-2011 Ari64\n"
1648         "PCSX4ALL plugins by PCSX4ALL team\n"
1649         "  Chui, Franxis, Unai\n\n"
1650         "integration, optimization and\n"
1651         "  frontend (C) 2010-2011 notaz\n";
1652
1653 static int reset_game(void)
1654 {
1655         // sanity check
1656         if (bios_sel == 0 && !Config.HLE)
1657                 return -1;
1658
1659         ClosePlugins();
1660         OpenPlugins();
1661         SysReset();
1662         if (CheckCdrom() != -1) {
1663                 LoadCdrom();
1664         }
1665         return 0;
1666 }
1667
1668 static int reload_plugins(const char *cdimg)
1669 {
1670         pl_vout_buf = NULL;
1671
1672         ClosePlugins();
1673
1674         set_cd_image(cdimg);
1675         LoadPlugins();
1676         pcnt_hook_plugins();
1677         NetOpened = 0;
1678         if (OpenPlugins() == -1) {
1679                 me_update_msg("failed to open plugins");
1680                 return -1;
1681         }
1682         plugin_call_rearmed_cbs();
1683
1684         cdrIsoMultidiskCount = 1;
1685         CdromId[0] = '\0';
1686         CdromLabel[0] = '\0';
1687
1688         return 0;
1689 }
1690
1691 static int run_bios(void)
1692 {
1693         if (bios_sel == 0)
1694                 return -1;
1695
1696         ready_to_go = 0;
1697         if (reload_plugins(NULL) != 0)
1698                 return -1;
1699         SysReset();
1700
1701         ready_to_go = 1;
1702         return 0;
1703 }
1704
1705 static int run_exe(void)
1706 {
1707         const char *fname;
1708
1709         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1710         if (fname == NULL)
1711                 return -1;
1712
1713         ready_to_go = 0;
1714         if (reload_plugins(NULL) != 0)
1715                 return -1;
1716
1717         SysReset();
1718         if (Load(fname) != 0) {
1719                 me_update_msg("exe load failed, bad file?");
1720                 printf("meh\n");
1721                 return -1;
1722         }
1723
1724         ready_to_go = 1;
1725         return 0;
1726 }
1727
1728 static int run_cd_image(const char *fname)
1729 {
1730         ready_to_go = 0;
1731         reload_plugins(fname);
1732
1733         // always autodetect, menu_sync_config will override as needed
1734         Config.PsxAuto = 1;
1735
1736         if (CheckCdrom() == -1) {
1737                 // Only check the CD if we are starting the console with a CD
1738                 ClosePlugins();
1739                 me_update_msg("unsupported/invalid CD image");
1740                 return -1;
1741         }
1742
1743         SysReset();
1744
1745         // Read main executable directly from CDRom and start it
1746         if (LoadCdrom() == -1) {
1747                 ClosePlugins();
1748                 me_update_msg("failed to load CD image");
1749                 return -1;
1750         }
1751
1752         ready_to_go = 1;
1753         snprintf(hud_msg, sizeof(hud_msg), "Booting up...");
1754         hud_new_msg = 2;
1755         return 0;
1756 }
1757
1758 static int romsel_run(void)
1759 {
1760         int prev_gpu, prev_spu;
1761         const char *fname;
1762
1763         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1764         if (fname == NULL)
1765                 return -1;
1766
1767         printf("selected file: %s\n", fname);
1768
1769         new_dynarec_clear_full();
1770
1771         if (run_cd_image(fname) != 0)
1772                 return -1;
1773
1774         prev_gpu = gpu_plugsel;
1775         prev_spu = spu_plugsel;
1776         if (menu_load_config(1) != 0)
1777                 menu_load_config(0);
1778
1779         // check for plugin changes, have to repeat
1780         // loading if game config changed plugins to reload them
1781         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1782                 printf("plugin change detected, reloading plugins..\n");
1783                 if (run_cd_image(fname) != 0)
1784                         return -1;
1785         }
1786
1787         if (Config.HLE)
1788                 printf("note: running without BIOS, expect compatibility problems\n");
1789
1790         strcpy(last_selected_fname, rom_fname_reload);
1791         return 0;
1792 }
1793
1794 static int swap_cd_image(void)
1795 {
1796         char *fname;
1797
1798         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1799         if (fname == NULL)
1800                 return -1;
1801
1802         printf("selected file: %s\n", fname);
1803
1804         CdromId[0] = '\0';
1805         CdromLabel[0] = '\0';
1806
1807         set_cd_image(fname);
1808         if (ReloadCdromPlugin() < 0) {
1809                 me_update_msg("failed to load cdr plugin");
1810                 return -1;
1811         }
1812         if (CDR_open() < 0) {
1813                 me_update_msg("failed to open cdr plugin");
1814                 return -1;
1815         }
1816
1817         SetCdOpenCaseTime(time(NULL) + 2);
1818         LidInterrupt();
1819
1820         strcpy(last_selected_fname, rom_fname_reload);
1821         return 0;
1822 }
1823
1824 static int swap_cd_multidisk(void)
1825 {
1826         cdrIsoMultidiskSelect++;
1827         CdromId[0] = '\0';
1828         CdromLabel[0] = '\0';
1829
1830         CDR_close();
1831         if (CDR_open() < 0) {
1832                 me_update_msg("failed to open cdr plugin");
1833                 return -1;
1834         }
1835
1836         SetCdOpenCaseTime(time(NULL) + 2);
1837         LidInterrupt();
1838
1839         return 0;
1840 }
1841
1842 static int main_menu_handler(int id, int keys)
1843 {
1844         switch (id)
1845         {
1846         case MA_MAIN_RESUME_GAME:
1847                 if (ready_to_go)
1848                         return 1;
1849                 break;
1850         case MA_MAIN_SAVE_STATE:
1851                 if (ready_to_go)
1852                         return menu_loop_savestate(0);
1853                 break;
1854         case MA_MAIN_LOAD_STATE:
1855                 if (ready_to_go)
1856                         return menu_loop_savestate(1);
1857                 break;
1858         case MA_MAIN_RESET_GAME:
1859                 if (ready_to_go && reset_game() == 0)
1860                         return 1;
1861                 break;
1862         case MA_MAIN_LOAD_ROM:
1863                 if (romsel_run() == 0)
1864                         return 1;
1865                 break;
1866         case MA_MAIN_SWAP_CD:
1867                 if (swap_cd_image() == 0)
1868                         return 1;
1869                 break;
1870         case MA_MAIN_SWAP_CD_MULTI:
1871                 if (swap_cd_multidisk() == 0)
1872                         return 1;
1873                 break;
1874         case MA_MAIN_RUN_BIOS:
1875                 if (run_bios() == 0)
1876                         return 1;
1877                 break;
1878         case MA_MAIN_RUN_EXE:
1879                 if (run_exe() == 0)
1880                         return 1;
1881                 break;
1882         case MA_MAIN_CREDITS:
1883                 draw_menu_message(credits_text, draw_frame_credits);
1884                 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1885                 break;
1886         case MA_MAIN_EXIT:
1887                 OnFile_Exit();
1888                 break;
1889         default:
1890                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1891                 break;
1892         }
1893
1894         return 0;
1895 }
1896
1897 static menu_entry e_menu_main2[] =
1898 {
1899         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
1900         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
1901         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
1902         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
1903         mee_handler   ("Memcard manager",    menu_loop_memcards),
1904         mee_end,
1905 };
1906
1907 static int main_menu2_handler(int id, int keys)
1908 {
1909         static int sel = 0;
1910
1911         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
1912         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
1913         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1914
1915         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1916 }
1917
1918 static const char h_extra[] = "Change CD, manage memcards..\n";
1919
1920 static menu_entry e_menu_main[] =
1921 {
1922         mee_label     (""),
1923         mee_label     (""),
1924         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
1925         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
1926         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
1927         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
1928         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
1929         mee_handler   ("Options",            menu_loop_options),
1930         mee_handler   ("Controls",           menu_loop_keyconfig),
1931         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
1932         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
1933         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
1934         mee_end,
1935 };
1936
1937 // ----------------------------
1938
1939 static void menu_leave_emu(void);
1940
1941 void menu_loop(void)
1942 {
1943         static int sel = 0;
1944
1945         menu_leave_emu();
1946
1947         if (bioses[1] == NULL && !warned_about_bios) {
1948                 menu_bios_warn();
1949                 warned_about_bios = 1;
1950         }
1951
1952         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1953         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
1954         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
1955         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
1956
1957         in_set_config_int(0, IN_CFG_BLOCKING, 1);
1958
1959         do {
1960                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
1961         } while (!ready_to_go);
1962
1963         /* wait until menu, ok, back is released */
1964         while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1965                 ;
1966
1967         in_set_config_int(0, IN_CFG_BLOCKING, 0);
1968
1969         menu_prepare_emu();
1970 }
1971
1972 static int qsort_strcmp(const void *p1, const void *p2)
1973 {
1974         char * const *s1 = (char * const *)p1;
1975         char * const *s2 = (char * const *)p2;
1976         return strcasecmp(*s1, *s2);
1977 }
1978
1979 static void scan_bios_plugins(void)
1980 {
1981         char fname[MAXPATHLEN];
1982         struct dirent *ent;
1983         int bios_i, gpu_i, spu_i, mc_i;
1984         char *p;
1985         DIR *dir;
1986
1987         bioses[0] = "HLE";
1988         gpu_plugins[0] = "builtin_gpu";
1989         spu_plugins[0] = "builtin_spu";
1990         memcards[0] = "(none)";
1991         bios_i = gpu_i = spu_i = mc_i = 1;
1992
1993         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1994         dir = opendir(fname);
1995         if (dir == NULL) {
1996                 perror("scan_bios_plugins bios opendir");
1997                 goto do_plugins;
1998         }
1999
2000         while (1) {
2001                 struct stat st;
2002
2003                 errno = 0;
2004                 ent = readdir(dir);
2005                 if (ent == NULL) {
2006                         if (errno != 0)
2007                                 perror("readdir");
2008                         break;
2009                 }
2010
2011                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2012                         continue;
2013
2014                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2015                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2016                         printf("bad BIOS file: %s\n", ent->d_name);
2017                         continue;
2018                 }
2019
2020                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2021                         bioses[bios_i++] = strdup(ent->d_name);
2022                         continue;
2023                 }
2024
2025                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2026         }
2027
2028         closedir(dir);
2029
2030 do_plugins:
2031         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2032         dir = opendir(fname);
2033         if (dir == NULL) {
2034                 perror("scan_bios_plugins plugins opendir");
2035                 goto do_memcards;
2036         }
2037
2038         while (1) {
2039                 void *h, *tmp;
2040
2041                 errno = 0;
2042                 ent = readdir(dir);
2043                 if (ent == NULL) {
2044                         if (errno != 0)
2045                                 perror("readdir");
2046                         break;
2047                 }
2048                 p = strstr(ent->d_name, ".so");
2049                 if (p == NULL)
2050                         continue;
2051
2052                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2053                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2054                 if (h == NULL) {
2055                         fprintf(stderr, "%s\n", dlerror());
2056                         continue;
2057                 }
2058
2059                 // now what do we have here?
2060                 tmp = dlsym(h, "GPUinit");
2061                 if (tmp) {
2062                         dlclose(h);
2063                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2064                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2065                         continue;
2066                 }
2067
2068                 tmp = dlsym(h, "SPUinit");
2069                 if (tmp) {
2070                         dlclose(h);
2071                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2072                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2073                         continue;
2074                 }
2075
2076                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2077                 dlclose(h);
2078         }
2079
2080         closedir(dir);
2081
2082 do_memcards:
2083         dir = opendir("." MEMCARD_DIR);
2084         if (dir == NULL) {
2085                 perror("scan_bios_plugins memcards opendir");
2086                 return;
2087         }
2088
2089         while (1) {
2090                 struct stat st;
2091
2092                 errno = 0;
2093                 ent = readdir(dir);
2094                 if (ent == NULL) {
2095                         if (errno != 0)
2096                                 perror("readdir");
2097                         break;
2098                 }
2099
2100                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2101                         continue;
2102
2103                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2104                 if (stat(fname, &st) != 0) {
2105                         printf("bad memcard file: %s\n", ent->d_name);
2106                         continue;
2107                 }
2108
2109                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2110                         memcards[mc_i++] = strdup(ent->d_name);
2111                         continue;
2112                 }
2113
2114                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2115         }
2116
2117         if (mc_i > 2)
2118                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2119
2120         closedir(dir);
2121 }
2122
2123 void menu_init(void)
2124 {
2125         char buff[MAXPATHLEN];
2126
2127         strcpy(last_selected_fname, "/media");
2128
2129         scan_bios_plugins();
2130         pnd_menu_init();
2131         menu_init_common();
2132
2133         menu_set_defconfig();
2134         menu_load_config(0);
2135         last_psx_w = 320;
2136         last_psx_h = 240;
2137         last_psx_bpp = 16;
2138
2139         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2140         if (g_menubg_src_ptr == NULL)
2141                 exit(1);
2142         emu_make_path(buff, "skin/background.png", sizeof(buff));
2143         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2144
2145 #ifndef __ARM_ARCH_7A__ /* XXX */
2146         me_enable(e_menu_options, MA_OPT_DISP_OPTS, 0);
2147         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2148 #else
2149         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
2150         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2151 #endif
2152 }
2153
2154 void menu_notify_mode_change(int w, int h, int bpp)
2155 {
2156         float mult;
2157         int imult;
2158
2159         last_psx_w = w;
2160         last_psx_h = h;
2161         last_psx_bpp = bpp;
2162
2163         switch (scaling) {
2164         case SCALE_1_1:
2165                 g_layer_w = w; g_layer_h = h;
2166                 break;
2167
2168         case SCALE_4_3v2:
2169                 if (h > g_menuscreen_h || (240 < h && h <= 360))
2170                         goto fractional_4_3;
2171
2172                 // 4:3 that prefers integer scaling
2173                 imult = g_menuscreen_h / h;
2174                 g_layer_w = w * imult;
2175                 g_layer_h = h * imult;
2176                 mult = (float)g_layer_w / (float)g_layer_h;
2177                 if (mult < 1.25f || mult > 1.666f)
2178                         g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2179                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2180                 break;
2181
2182         fractional_4_3:
2183         case SCALE_4_3:
2184                 mult = 240.0f / (float)h * 4.0f / 3.0f;
2185                 if (h > 256)
2186                         mult *= 2.0f;
2187                 g_layer_w = mult * (float)g_menuscreen_h;
2188                 g_layer_h = g_menuscreen_h;
2189                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2190                 break;
2191
2192         case SCALE_FULLSCREEN:
2193                 g_layer_w = g_menuscreen_w;
2194                 g_layer_h = g_menuscreen_h;
2195                 break;
2196
2197         default:
2198                 break;
2199         }
2200
2201         g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2202         g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2203         if (g_layer_x < 0) g_layer_x = 0;
2204         if (g_layer_y < 0) g_layer_y = 0;
2205         if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2206         if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2207 }
2208
2209 static void menu_leave_emu(void)
2210 {
2211         if (GPU_close != NULL) {
2212                 int ret = GPU_close();
2213                 if (ret)
2214                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2215         }
2216
2217         plat_video_menu_enter(ready_to_go);
2218
2219         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2220         if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
2221                 int x = max(0, g_menuscreen_w - last_psx_w);
2222                 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2223                 int w = min(g_menuscreen_w, last_psx_w);
2224                 int h = min(g_menuscreen_h, last_psx_h);
2225                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2226                 u16 *s = pl_vout_buf;
2227
2228                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
2229                         menu_darken_bg(d, s, w, 0);
2230         }
2231
2232         if (ready_to_go)
2233                 cpu_clock = plat_cpu_clock_get();
2234 }
2235
2236 void menu_prepare_emu(void)
2237 {
2238         R3000Acpu *prev_cpu = psxCpu;
2239
2240         plat_video_menu_leave();
2241
2242         menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2243
2244         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2245         if (psxCpu != prev_cpu)
2246                 // note that this does not really reset, just clears drc caches
2247                 psxCpu->Reset();
2248
2249         // core doesn't care about Config.Cdda changes,
2250         // so handle them manually here
2251         if (Config.Cdda)
2252                 CDR_stop();
2253
2254         menu_sync_config();
2255         apply_lcdrate(Config.PsxType);
2256         apply_filter(filter);
2257         plat_cpu_clock_apply(cpu_clock);
2258
2259         // push config to GPU plugin
2260         plugin_call_rearmed_cbs();
2261
2262         if (GPU_open != NULL) {
2263                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2264                 if (ret)
2265                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2266         }
2267
2268         dfinput_activate();
2269 }
2270
2271 void me_update_msg(const char *msg)
2272 {
2273         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2274         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2275
2276         menu_error_time = plat_get_ticks_ms();
2277         lprintf("msg: %s\n", menu_error_msg);
2278 }
2279