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