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