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