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