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