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