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