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