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