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