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