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