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