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