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