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