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