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