revive standalone build
[pcsx_rearmed.git] / frontend / menu.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2010-2015
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
6  *  - GNU GPL, version 2 or later.
7  *  - GNU LGPL, version 2.1 or later.
8  * See the COPYING file in the top-level directory.
9  */
10
11 #define _GNU_SOURCE 1
12 #include <stdio.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <dlfcn.h>
16 #include <zlib.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 #include <dirent.h>
21
22 #include "main.h"
23 #include "menu.h"
24 #include "config.h"
25 #include "plugin.h"
26 #include "plugin_lib.h"
27 #include "plat.h"
28 #include "pcnt.h"
29 #include "cspace.h"
30 #include "libpicofe/plat.h"
31 #include "libpicofe/input.h"
32 #include "libpicofe/linux/in_evdev.h"
33 #include "libpicofe/plat.h"
34 #include "../libpcsxcore/misc.h"
35 #include "../libpcsxcore/cdrom.h"
36 #include "../libpcsxcore/cdriso.h"
37 #include "../libpcsxcore/cheat.h"
38 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
39 #include "../plugins/dfinput/externals.h"
40 #include "../plugins/dfsound/spu_config.h"
41 #include "psemu_plugin_defs.h"
42 #include "arm_features.h"
43 #include "revision.h"
44
45 #define REARMED_BIRTHDAY_TIME 1293306830        /* 25 Dec 2010 */
46
47 #define array_size(x) (sizeof(x) / sizeof(x[0]))
48
49 typedef enum
50 {
51         MA_NONE = 1,
52         MA_MAIN_RESUME_GAME,
53         MA_MAIN_SAVE_STATE,
54         MA_MAIN_LOAD_STATE,
55         MA_MAIN_RESET_GAME,
56         MA_MAIN_LOAD_ROM,
57         MA_MAIN_SWAP_CD,
58         MA_MAIN_SWAP_CD_MULTI,
59         MA_MAIN_RUN_BIOS,
60         MA_MAIN_RUN_EXE,
61         MA_MAIN_LOAD_CHEATS,
62         MA_MAIN_CHEATS,
63         MA_MAIN_CONTROLS,
64         MA_MAIN_CREDITS,
65         MA_MAIN_EXIT,
66         MA_CTRL_PLAYER1,
67         MA_CTRL_PLAYER2,
68         MA_CTRL_ANALOG,
69         MA_CTRL_EMU,
70         MA_CTRL_DEV_FIRST,
71         MA_CTRL_DEV_NEXT,
72         MA_CTRL_NUBS_BTNS,
73         MA_CTRL_DEADZONE,
74         MA_CTRL_VIBRATION,
75         MA_CTRL_DONE,
76         MA_OPT_SAVECFG,
77         MA_OPT_SAVECFG_GAME,
78         MA_OPT_CPU_CLOCKS,
79         MA_OPT_SPU_THREAD,
80         MA_OPT_DISP_OPTS,
81         MA_OPT_VARSCALER,
82         MA_OPT_VARSCALER_C,
83         MA_OPT_SCALER2,
84         MA_OPT_HWFILTER,
85         MA_OPT_SWFILTER,
86         MA_OPT_GAMMA,
87         MA_OPT_VOUT_MODE,
88         MA_OPT_SCANLINES,
89         MA_OPT_SCANLINE_LEVEL,
90 } menu_id;
91
92 static int last_vout_w, last_vout_h, last_vout_bpp;
93 static int cpu_clock, cpu_clock_st, volume_boost, frameskip;
94 static char last_selected_fname[MAXPATHLEN];
95 static int config_save_counter, region, in_type_sel1, in_type_sel2;
96 static int psx_clock;
97 static int memcard1_sel = -1, memcard2_sel = -1;
98 extern int g_autostateld_opt;
99 int g_opts, g_scaler, g_gamma = 100;
100 int scanlines, scanline_level = 20;
101 int soft_scaling, analog_deadzone; // for Caanoo
102 int soft_filter;
103
104 #ifndef HAVE_PRE_ARMV7
105 #define DEFAULT_PSX_CLOCK (10000 / CYCLE_MULT_DEFAULT)
106 #define DEFAULT_PSX_CLOCK_S "57"
107 #else
108 #define DEFAULT_PSX_CLOCK 50
109 #define DEFAULT_PSX_CLOCK_S "50"
110 #endif
111
112 static const char *bioses[24];
113 static const char *gpu_plugins[16];
114 static const char *spu_plugins[16];
115 static const char *memcards[32];
116 static int bios_sel, gpu_plugsel, spu_plugsel;
117
118 #ifndef UI_FEATURES_H
119 #define MENU_BIOS_PATH "bios/"
120 #define MENU_SHOW_VARSCALER 0
121 #define MENU_SHOW_VOUTMODE 1
122 #define MENU_SHOW_SCALER2 0
123 #define MENU_SHOW_NUBS_BTNS 0
124 #define MENU_SHOW_VIBRATION 0
125 #define MENU_SHOW_DEADZONE 0
126 #define MENU_SHOW_MINIMIZE 0
127 #define MENU_SHOW_FULLSCREEN 1
128 #define MENU_SHOW_VOLUME 0
129 #endif
130
131 static int min(int x, int y) { return x < y ? x : y; }
132 static int max(int x, int y) { return x > y ? x : y; }
133
134 void emu_make_path(char *buff, const char *end, int size)
135 {
136         int pos, end_len;
137
138         end_len = strlen(end);
139         pos = plat_get_root_dir(buff, size);
140         strncpy(buff + pos, end, size - pos);
141         buff[size - 1] = 0;
142         if (pos + end_len > size - 1)
143                 printf("Warning: path truncated: %s\n", buff);
144 }
145
146 static int emu_check_save_file(int slot, int *time)
147 {
148         char fname[MAXPATHLEN];
149         struct stat status;
150         int ret;
151         
152         ret = emu_check_state(slot);
153         if (ret != 0 || time == NULL)
154                 return ret == 0 ? 1 : 0;
155
156         ret = get_state_filename(fname, sizeof(fname), slot);
157         if (ret != 0)
158                 return 1;
159
160         ret = stat(fname, &status);
161         if (ret != 0)
162                 return 1;
163
164         if (status.st_mtime < REARMED_BIRTHDAY_TIME)
165                 return 1; // probably bad rtc like on some Caanoos
166
167         *time = status.st_mtime;
168
169         return 1;
170 }
171
172 static int emu_save_load_game(int load, int unused)
173 {
174         int ret;
175
176         if (load) {
177                 ret = emu_load_state(state_slot);
178
179                 // reflect hle/bios mode from savestate
180                 if (Config.HLE)
181                         bios_sel = 0;
182                 else if (bios_sel == 0 && bioses[1] != NULL)
183                         // XXX: maybe find the right bios instead
184                         bios_sel = 1;
185         }
186         else
187                 ret = emu_save_state(state_slot);
188
189         return ret;
190 }
191
192 static void rm_namelist_entry(struct dirent **namelist,
193         int count, const char *name)
194 {
195         int i;
196
197         for (i = 1; i < count; i++) {
198                 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
199                         continue;
200
201                 if (strcmp(name, namelist[i]->d_name) == 0) {
202                         free(namelist[i]);
203                         namelist[i] = NULL;
204                         break;
205                 }
206         }
207 }
208
209 static int optional_cdimg_filter(struct dirent **namelist, int count,
210         const char *basedir)
211 {
212         const char *ext, *p;
213         char buf[256], buf2[256];
214         int i, d, ret, good_cue;
215         struct stat64 statf;
216         FILE *f;
217
218         if (count <= 1)
219                 return count;
220
221         for (i = 1; i < count; i++) {
222                 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
223                         continue;
224
225                 ext = strrchr(namelist[i]->d_name, '.');
226                 if (ext == NULL) {
227                         // should not happen but whatever
228                         free(namelist[i]);
229                         namelist[i] = NULL;
230                         continue;
231                 }
232                 ext++;
233
234                 // first find .cue files and remove files they reference
235                 if (strcasecmp(ext, "cue") == 0)
236                 {
237                         snprintf(buf, sizeof(buf), "%s/%s", basedir,
238                                 namelist[i]->d_name);
239
240                         f = fopen(buf, "r");
241                         if (f == NULL) {
242                                 free(namelist[i]);
243                                 namelist[i] = NULL;
244                                 continue;
245                         }
246
247                         good_cue = 0;
248                         while (fgets(buf, sizeof(buf), f)) {
249                                 ret = sscanf(buf, " FILE \"%256[^\"]\"", buf2);
250                                 if (ret != 1)
251                                         ret = sscanf(buf, " FILE %256s", buf2);
252                                 if (ret != 1)
253                                         continue;
254
255                                 p = strrchr(buf2, '/');
256                                 if (p == NULL)
257                                         p = strrchr(buf2, '\\');
258                                 if (p != NULL)
259                                         p++;
260                                 else
261                                         p = buf2;
262
263                                 snprintf(buf, sizeof(buf), "%s/%s", basedir, p);
264                                 ret = stat64(buf, &statf);
265                                 if (ret == 0) {
266                                         rm_namelist_entry(namelist, count, p);
267                                         good_cue = 1;
268                                 }
269                         }
270                         fclose(f);
271
272                         if (!good_cue) {
273                                 free(namelist[i]);
274                                 namelist[i] = NULL;
275                         }
276                         continue;
277                 }
278
279                 p = strcasestr(namelist[i]->d_name, "track");
280                 if (p != NULL) {
281                         ret = strtoul(p + 5, NULL, 10);
282                         if (ret > 1) {
283                                 free(namelist[i]);
284                                 namelist[i] = NULL;
285                                 continue;
286                         }
287                 }
288         }
289
290         // compact namelist
291         for (i = d = 1; i < count; i++)
292                 if (namelist[i] != NULL)
293                         namelist[d++] = namelist[i];
294
295         return d;
296 }
297
298 // propagate menu settings to the emu vars
299 static void menu_sync_config(void)
300 {
301         static int allow_abs_only_old;
302
303         Config.PsxAuto = 1;
304         if (region > 0) {
305                 Config.PsxAuto = 0;
306                 Config.PsxType = region - 1;
307         }
308         cycle_multiplier = 10000 / psx_clock;
309
310         switch (in_type_sel1) {
311         case 1:  in_type[0] = PSE_PAD_TYPE_ANALOGPAD; break;
312         case 2:  in_type[0] = PSE_PAD_TYPE_NEGCON;    break;
313         default: in_type[0] = PSE_PAD_TYPE_STANDARD;
314         }
315         switch (in_type_sel2) {
316         case 1:  in_type[1] = PSE_PAD_TYPE_ANALOGPAD; break;
317         case 2:  in_type[1] = PSE_PAD_TYPE_NEGCON;    break;
318         default: in_type[1] = PSE_PAD_TYPE_STANDARD;
319         }
320         if (in_evdev_allow_abs_only != allow_abs_only_old) {
321                 in_probe();
322                 allow_abs_only_old = in_evdev_allow_abs_only;
323         }
324
325         spu_config.iVolume = 768 + 128 * volume_boost;
326         pl_rearmed_cbs.frameskip = frameskip - 1;
327         pl_timing_prepare(Config.PsxType);
328 }
329
330 static void menu_set_defconfig(void)
331 {
332         emu_set_default_config();
333
334         g_opts = 0;
335         g_scaler = SCALE_4_3;
336         g_gamma = 100;
337         volume_boost = 0;
338         frameskip = 0;
339         analog_deadzone = 50;
340         soft_scaling = 1;
341         soft_filter = 0;
342         scanlines = 0;
343         scanline_level = 20;
344         plat_target.vout_fullscreen = 0;
345         psx_clock = DEFAULT_PSX_CLOCK;
346
347         region = 0;
348         in_type_sel1 = in_type_sel2 = 0;
349         in_evdev_allow_abs_only = 0;
350
351         menu_sync_config();
352 }
353
354 #define CE_CONFIG_STR(val) \
355         { #val, 0, Config.val }
356
357 #define CE_CONFIG_VAL(val) \
358         { #val, sizeof(Config.val), &Config.val }
359
360 #define CE_STR(val) \
361         { #val, 0, val }
362
363 #define CE_INTVAL(val) \
364         { #val, sizeof(val), &val }
365
366 #define CE_INTVAL_N(name, val) \
367         { name, sizeof(val), &val }
368
369 #define CE_INTVAL_P(val) \
370         { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
371
372 // 'versioned' var, used when defaults change
373 #define CE_CONFIG_STR_V(val, ver) \
374         { #val #ver, 0, Config.val }
375
376 #define CE_INTVAL_V(val, ver) \
377         { #val #ver, sizeof(val), &val }
378
379 #define CE_INTVAL_PV(val, ver) \
380         { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
381
382 static const struct {
383         const char *name;
384         size_t len;
385         void *val;
386 } config_data[] = {
387         CE_CONFIG_STR(Bios),
388         CE_CONFIG_STR_V(Gpu, 3),
389         CE_CONFIG_STR(Spu),
390 //      CE_CONFIG_STR(Cdr),
391         CE_CONFIG_VAL(Xa),
392 //      CE_CONFIG_VAL(Sio),
393         CE_CONFIG_VAL(Mdec),
394         CE_CONFIG_VAL(Cdda),
395         CE_CONFIG_VAL(Debug),
396         CE_CONFIG_VAL(PsxOut),
397         CE_CONFIG_VAL(SpuIrq),
398         CE_CONFIG_VAL(RCntFix),
399         CE_CONFIG_VAL(VSyncWA),
400         CE_CONFIG_VAL(icache_emulation),
401         CE_CONFIG_VAL(DisableStalls),
402         CE_CONFIG_VAL(Cpu),
403         CE_INTVAL(region),
404         CE_INTVAL_V(g_scaler, 3),
405         CE_INTVAL(g_gamma),
406         CE_INTVAL(g_layer_x),
407         CE_INTVAL(g_layer_y),
408         CE_INTVAL(g_layer_w),
409         CE_INTVAL(g_layer_h),
410         CE_INTVAL(soft_filter),
411         CE_INTVAL(scanlines),
412         CE_INTVAL(scanline_level),
413         CE_INTVAL(plat_target.vout_method),
414         CE_INTVAL(plat_target.hwfilter),
415         CE_INTVAL(plat_target.vout_fullscreen),
416         CE_INTVAL(state_slot),
417         CE_INTVAL(cpu_clock),
418         CE_INTVAL(g_opts),
419         CE_INTVAL(in_type_sel1),
420         CE_INTVAL(in_type_sel2),
421         CE_INTVAL(analog_deadzone),
422         CE_INTVAL(memcard1_sel),
423         CE_INTVAL(memcard2_sel),
424         CE_INTVAL(g_autostateld_opt),
425         CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
426         CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
427         CE_INTVAL_V(frameskip, 3),
428         CE_INTVAL_P(thread_rendering),
429         CE_INTVAL_P(gpu_peops.iUseDither),
430         CE_INTVAL_P(gpu_peops.dwActFixes),
431         CE_INTVAL_P(gpu_unai.ilace_force),
432         CE_INTVAL_P(gpu_unai.pixel_skip),
433         CE_INTVAL_P(gpu_unai.lighting),
434         CE_INTVAL_P(gpu_unai.fast_lighting),
435         CE_INTVAL_P(gpu_unai.blending),
436         CE_INTVAL_P(gpu_unai.dithering),
437         CE_INTVAL_P(gpu_unai.lineskip),
438         CE_INTVAL_P(gpu_unai.abe_hack),
439         CE_INTVAL_P(gpu_unai.no_light),
440         CE_INTVAL_P(gpu_unai.no_blend),
441         CE_INTVAL_P(gpu_unai.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 __ARM_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_onoff     ("Interlace",                  0, pl_rearmed_cbs.gpu_unai.ilace_force, 1),
1382         mee_onoff     ("Dithering",                  0, pl_rearmed_cbs.gpu_unai.dithering, 1),
1383         mee_onoff     ("Lighting",                   0, pl_rearmed_cbs.gpu_unai.lighting, 1),
1384         mee_onoff     ("Fast lighting",              0, pl_rearmed_cbs.gpu_unai.fast_lighting, 1),
1385         mee_onoff     ("Blending",                   0, pl_rearmed_cbs.gpu_unai.blending, 1),
1386         mee_onoff     ("Pixel skip",                 0, pl_rearmed_cbs.gpu_unai.pixel_skip, 1),
1387         mee_end,
1388 };
1389
1390 static int menu_loop_plugin_gpu_unai(int id, int keys)
1391 {
1392         int sel = 0;
1393         me_loop(e_menu_plugin_gpu_unai, &sel);
1394         return 0;
1395 }
1396
1397 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1398 //static const char h_gpu_0[]            = "Needed for Chrono Cross";
1399 static const char h_gpu_1[]            = "Capcom fighting games";
1400 static const char h_gpu_2[]            = "Black screens in Lunar";
1401 static const char h_gpu_3[]            = "Compatibility mode";
1402 static const char h_gpu_6[]            = "Pandemonium 2";
1403 //static const char h_gpu_7[]            = "Skip every second frame";
1404 static const char h_gpu_8[]            = "Needed by Dark Forces";
1405 static const char h_gpu_9[]            = "better g-colors, worse textures";
1406 static const char h_gpu_10[]           = "Toggle busy flags after drawing";
1407
1408 static menu_entry e_menu_plugin_gpu_peops[] =
1409 {
1410         mee_enum      ("Dithering",                  0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1411 //      mee_onoff_h   ("Odd/even bit hack",          0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1412         mee_onoff_h   ("Expand screen width",        0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1413         mee_onoff_h   ("Ignore brightness color",    0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1414         mee_onoff_h   ("Disable coordinate check",   0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1415         mee_onoff_h   ("Lazy screen update",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1416 //      mee_onoff_h   ("Old frame skipping",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1417         mee_onoff_h   ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1418         mee_onoff_h   ("Draw quads with triangles",  0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1419         mee_onoff_h   ("Fake 'gpu busy' states",     0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1420         mee_end,
1421 };
1422
1423 static int menu_loop_plugin_gpu_peops(int id, int keys)
1424 {
1425         static int sel = 0;
1426         me_loop(e_menu_plugin_gpu_peops, &sel);
1427         return 0;
1428 }
1429
1430 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1431         "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1432 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1433
1434 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1435 {
1436         mee_onoff     ("Dithering",                  0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1437         mee_enum      ("Texture Filtering",          0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1438         mee_enum      ("Framebuffer Textures",       0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1439         mee_onoff     ("Mask Detect",                0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1440         mee_onoff     ("Opaque Pass",                0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1441         mee_onoff     ("Advanced Blend",             0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1442         mee_onoff     ("Use Fast Mdec",              0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1443         mee_range     ("Texture RAM size (MB)",      0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1444         mee_onoff     ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1445         mee_label     ("Fixes/hacks:"),
1446         mee_onoff     ("FF7 cursor",                 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1447         mee_onoff     ("Direct FB updates",          0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1448         mee_onoff     ("Black brightness",           0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1449         mee_onoff     ("Swap front detection",       0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1450         mee_onoff     ("Disable coord check",        0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1451         mee_onoff     ("No blue glitches (LoD)",     0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1452         mee_onoff     ("Soft FB access",             0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1453         mee_onoff     ("FF9 rect",                   0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1454         mee_onoff     ("No subtr. blending",         0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1455         mee_onoff     ("Lazy upload (DW7)",          0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1456         mee_onoff     ("Additional uploads",         0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1457         mee_end,
1458 };
1459
1460 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1461 {
1462         static int sel = 0;
1463         me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1464         return 0;
1465 }
1466
1467 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1468 static const char h_spu_volboost[]  = "Large values cause distortion";
1469 static const char h_spu_tempo[]     = "Slows down audio if emu is too slow\n"
1470                                       "This is inaccurate and breaks games";
1471
1472 static menu_entry e_menu_plugin_spu[] =
1473 {
1474         mee_range_h   ("Volume boost",              0, volume_boost, -5, 30, h_spu_volboost),
1475         mee_onoff     ("Reverb",                    0, spu_config.iUseReverb, 1),
1476         mee_enum      ("Interpolation",             0, spu_config.iUseInterpolation, men_spu_interp),
1477         mee_onoff     ("Adjust XA pitch",           0, spu_config.iXAPitch, 1),
1478         mee_onoff_h   ("Adjust tempo",              0, spu_config.iTempo, 1, h_spu_tempo),
1479         mee_end,
1480 };
1481
1482 static int menu_loop_plugin_spu(int id, int keys)
1483 {
1484         static int sel = 0;
1485         me_loop(e_menu_plugin_spu, &sel);
1486         return 0;
1487 }
1488
1489 static const char h_bios[]       = "HLE is simulated BIOS. BIOS selection is saved in\n"
1490                                    "savestates and can't be changed there. Must save\n"
1491                                    "config and reload the game for change to take effect";
1492 static const char h_plugin_gpu[] = 
1493 #ifdef __ARM_NEON__
1494                                    "builtin_gpu is the NEON GPU, very fast and accurate\n"
1495 #endif
1496                                    "gpu_peops is Pete's soft GPU, slow but accurate\n"
1497                                    "gpu_unai is GPU from PCSX4ALL, fast but glitchy\n"
1498                                    "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1499                                    "must save config and reload the game if changed";
1500 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1501                                    "must save config and reload the game if changed";
1502 static const char h_gpu_peops[]  = "Configure P.E.Op.S. SoftGL Driver V1.17";
1503 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1504 static const char h_gpu_unai[]   = "Configure Unai/PCSX4ALL Team GPU plugin";
1505 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1506
1507 static menu_entry e_menu_plugin_options[] =
1508 {
1509         mee_enum_h    ("BIOS",                          0, bios_sel, bioses, h_bios),
1510         mee_enum_h    ("GPU plugin",                    0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1511         mee_enum_h    ("SPU plugin",                    0, spu_plugsel, spu_plugins, h_plugin_spu),
1512 #ifdef __ARM_NEON__
1513         mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1514 #endif
1515         mee_handler_h ("Configure gpu_peops plugin",    menu_loop_plugin_gpu_peops, h_gpu_peops),
1516         mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1517         mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1518         mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1519         mee_end,
1520 };
1521
1522 static menu_entry e_menu_main2[];
1523
1524 static int menu_loop_plugin_options(int id, int keys)
1525 {
1526         static int sel = 0;
1527         me_loop(e_menu_plugin_options, &sel);
1528
1529         // sync BIOS/plugins
1530         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1531         snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1532         snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1533         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1534
1535         return 0;
1536 }
1537
1538 // ------------ adv options menu ------------
1539
1540 #ifndef DRC_DISABLE
1541 static const char h_cfg_psxclk[]  = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1542                                     "(lower value - less work for the emu, may be faster)";
1543 static const char h_cfg_noch[]    = "Disables game-specific compatibility hacks";
1544 static const char h_cfg_nosmc[]   = "Will cause crashes when loading, break memcards";
1545 static const char h_cfg_gteunn[]  = "May cause graphical glitches";
1546 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1547 #endif
1548 static const char h_cfg_stalls[]  = "Will cause some games to run too fast";
1549
1550 static menu_entry e_menu_speed_hacks[] =
1551 {
1552 #ifndef DRC_DISABLE
1553         mee_range_h   ("PSX CPU clock, %%",        0, psx_clock, 1, 500, h_cfg_psxclk),
1554         mee_onoff_h   ("Disable compat hacks",     0, new_dynarec_hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1555         mee_onoff_h   ("Disable SMC checks",       0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1556         mee_onoff_h   ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1557         mee_onoff_h   ("Disable GTE flags",        0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1558 #endif
1559         mee_onoff_h   ("Disable CPU/GTE stalls",   0, Config.DisableStalls, 1, h_cfg_stalls),
1560         mee_end,
1561 };
1562
1563 static int menu_loop_speed_hacks(int id, int keys)
1564 {
1565         static int sel = 0;
1566         me_loop(e_menu_speed_hacks, &sel);
1567         return 0;
1568 }
1569
1570 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
1571 static const char h_cfg_spu[]    = "Shows active SPU channels\n"
1572                                    "(green: normal, red: fmod, blue: noise)";
1573 static const char h_cfg_fl[]     = "Frame Limiter keeps the game from running too fast";
1574 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
1575 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
1576                                    "(proper .cue/.bin dump is needed otherwise)";
1577 //static const char h_cfg_sio[]    = "You should not need this, breaks games";
1578 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1579 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix\n"
1580                                    "(timing hack, breaks other games)";
1581 #ifdef DRC_DISABLE
1582 static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1583                                    "(timing hack, breaks other games)";
1584 #else
1585 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
1586                                    "Might be useful to overcome some dynarec bugs";
1587 #endif
1588 static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1589 static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1590
1591 static menu_entry e_menu_adv_options[] =
1592 {
1593         mee_onoff_h   ("Show CPU load",          0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1594         mee_onoff_h   ("Show SPU channels",      0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1595         mee_onoff_h   ("Disable Frame Limiter",  0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1596         mee_onoff_h   ("Disable XA Decoding",    0, Config.Xa, 1, h_cfg_xa),
1597         mee_onoff_h   ("Disable CD Audio",       0, Config.Cdda, 1, h_cfg_cdda),
1598         //mee_onoff_h   ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1599         mee_onoff_h   ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1600         mee_onoff_h   ("ICache emulation",       0, Config.icache_emulation, 1, h_cfg_icache),
1601 #ifdef DRC_DISABLE
1602         mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
1603 #endif
1604         mee_onoff_h   ("Rootcounter hack 2",     0, Config.VSyncWA, 1, h_cfg_rcnt2),
1605 #ifndef DRC_DISABLE
1606         mee_onoff_h   ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1607 #endif
1608         mee_handler_h ("[Speed hacks]",             menu_loop_speed_hacks, h_cfg_shacks),
1609         mee_end,
1610 };
1611
1612 static int menu_loop_adv_options(int id, int keys)
1613 {
1614         static int sel = 0;
1615         me_loop(e_menu_adv_options, &sel);
1616         return 0;
1617 }
1618
1619 // ------------ options menu ------------
1620
1621 static int mh_restore_defaults(int id, int keys)
1622 {
1623         menu_set_defconfig();
1624         menu_update_msg("defaults restored");
1625         return 1;
1626 }
1627
1628 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
1629 static const char *men_frameskip[]    = { "Auto", "Off", "1", "2", "3", NULL };
1630 /*
1631 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1632 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
1633                                         "loading state or both";
1634 */
1635 static const char h_restore_def[]     = "Switches back to default / recommended\n"
1636                                         "configuration";
1637 static const char h_frameskip[]       = "Warning: frameskip sometimes causes glitches\n";
1638
1639 static menu_entry e_menu_options[] =
1640 {
1641 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1642 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1643         mee_enum_h    ("Frameskip",                0, frameskip, men_frameskip, h_frameskip),
1644         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1645         mee_enum      ("Region",                   0, region, men_region),
1646         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1647 #ifdef C64X_DSP
1648         mee_onoff     ("Use C64x DSP for sound",   MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1649 #else
1650         mee_onoff     ("Threaded SPU",             MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1651 #endif
1652         mee_handler_id("[Display]",                MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1653         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1654         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1655         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1656         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1657         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1658         mee_end,
1659 };
1660
1661 static int menu_loop_options(int id, int keys)
1662 {
1663         static int sel = 0;
1664
1665         me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1666         me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1667         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1668
1669         me_loop(e_menu_options, &sel);
1670
1671         return 0;
1672 }
1673
1674 // ------------ debug menu ------------
1675
1676 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1677 {
1678         int w = min(g_menuscreen_w, 1024);
1679         int h = min(g_menuscreen_h, 512);
1680         u16 *d = g_menuscreen_ptr;
1681         u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1682         char buff[64];
1683         int ty = 1;
1684
1685         gpuf->ulFreezeVersion = 1;
1686         if (GPU_freeze != NULL)
1687                 GPU_freeze(1, gpuf);
1688
1689         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1690                 bgr555_to_rgb565(d, s, w * 2);
1691
1692         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1693         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1694         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1695         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1696         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1697 }
1698
1699 static void debug_menu_loop(void)
1700 {
1701         int inp, df_x = 0, df_y = 0;
1702         GPUFreeze_t *gpuf;
1703
1704         gpuf = malloc(sizeof(*gpuf));
1705         if (gpuf == NULL)
1706                 return;
1707
1708         while (1)
1709         {
1710                 menu_draw_begin(0, 1);
1711                 draw_frame_debug(gpuf, df_x, df_y);
1712                 menu_draw_end();
1713
1714                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1715                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1716                 if      (inp & PBTN_MBACK) break;
1717                 else if (inp & PBTN_UP)    { if (df_y > 0) df_y--; }
1718                 else if (inp & PBTN_DOWN)  { if (df_y < 512 - g_menuscreen_h) df_y++; }
1719                 else if (inp & PBTN_LEFT)  { if (df_x > 0) df_x -= 2; }
1720                 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1721         }
1722
1723         free(gpuf);
1724 }
1725
1726 // --------- memcard manager ---------
1727
1728 static void draw_mc_icon(int dx, int dy, const u16 *s)
1729 {
1730         u16 *d;
1731         int x, y, l, p;
1732         
1733         d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1734
1735         for (y = 0; y < 16; y++, s += 16) {
1736                 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1737                         for (x = 0; x < 16; x++) {
1738                                 p = s[x];
1739                                 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1740                                         | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1741                         }
1742                 }
1743         }
1744 }
1745
1746 static void draw_mc_bg(void)
1747 {
1748         McdBlock *blocks1, *blocks2;
1749         int maxicons = 15;
1750         int i, y, row2;
1751
1752         blocks1 = malloc(15 * sizeof(blocks1[0]));
1753         blocks2 = malloc(15 * sizeof(blocks1[0]));
1754         if (blocks1 == NULL || blocks2 == NULL)
1755                 goto out;
1756
1757         for (i = 0; i < 15; i++) {
1758                 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1759                 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1760         }
1761
1762         menu_draw_begin(1, 1);
1763
1764         memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1765
1766         y = g_menuscreen_h / 2 - 15 * 32 / 2;
1767         if (y < 0) {
1768                 // doesn't fit..
1769                 y = 0;
1770                 maxicons = g_menuscreen_h / 32;
1771         }
1772
1773         row2 = g_menuscreen_w / 2;
1774         for (i = 0; i < maxicons; i++) {
1775                 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1776                 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1777
1778                 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1779                 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1780         }
1781
1782         menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1783
1784         menu_draw_end();
1785 out:
1786         free(blocks1);
1787         free(blocks2);
1788 }
1789
1790 static void handle_memcard_sel(void)
1791 {
1792         strcpy(Config.Mcd1, "none");
1793         if (memcard1_sel != 0)
1794                 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1795         strcpy(Config.Mcd2, "none");
1796         if (memcard2_sel != 0)
1797                 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1798         LoadMcds(Config.Mcd1, Config.Mcd2);
1799         draw_mc_bg();
1800 }
1801
1802 static menu_entry e_memcard_options[] =
1803 {
1804         mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1805         mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1806         mee_end,
1807 };
1808
1809 static int menu_loop_memcards(int id, int keys)
1810 {
1811         static int sel = 0;
1812         char *p;
1813         int i;
1814
1815         memcard1_sel = memcard2_sel = 0;
1816         p = strrchr(Config.Mcd1, '/');
1817         if (p != NULL)
1818                 for (i = 0; memcards[i] != NULL; i++)
1819                         if (strcmp(p + 1, memcards[i]) == 0)
1820                                 { memcard1_sel = i; break; }
1821         p = strrchr(Config.Mcd2, '/');
1822         if (p != NULL)
1823                 for (i = 0; memcards[i] != NULL; i++)
1824                         if (strcmp(p + 1, memcards[i]) == 0)
1825                                 { memcard2_sel = i; break; }
1826
1827         me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1828
1829         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1830
1831         return 0;
1832 }
1833
1834 // ------------ cheats menu ------------
1835
1836 static void draw_cheatlist(int sel)
1837 {
1838         int max_cnt, start, i, pos, active;
1839
1840         max_cnt = g_menuscreen_h / me_sfont_h;
1841         start = max_cnt / 2 - sel;
1842
1843         menu_draw_begin(1, 1);
1844
1845         for (i = 0; i < NumCheats; i++) {
1846                 pos = start + i;
1847                 if (pos < 0) continue;
1848                 if (pos >= max_cnt) break;
1849                 active = Cheats[i].Enabled;
1850                 smalltext_out16(14,                pos * me_sfont_h,
1851                         active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1852                 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1853                         Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1854         }
1855         pos = start + i;
1856         if (pos < max_cnt)
1857                 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1858
1859         text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1860         menu_draw_end();
1861 }
1862
1863 static void menu_loop_cheats(void)
1864 {
1865         static int menu_sel = 0;
1866         int inp;
1867
1868         for (;;)
1869         {
1870                 draw_cheatlist(menu_sel);
1871                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1872                                 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1873                 if (inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1874                 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1875                 if (inp &(PBTN_LEFT|PBTN_L))  { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1876                 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1877                 if (inp & PBTN_MOK) { // action
1878                         if (menu_sel < NumCheats)
1879                                 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1880                         else    break;
1881                 }
1882                 if (inp & PBTN_MBACK)
1883                         break;
1884         }
1885 }
1886
1887 // --------- main menu help ----------
1888
1889 static void menu_bios_warn(void)
1890 {
1891         int inp;
1892         static const char msg[] =
1893                 "You don't seem to have copied any BIOS\n"
1894                 "files to\n"
1895                 MENU_BIOS_PATH "\n\n"
1896
1897                 "While many games work fine with fake\n"
1898                 "(HLE) BIOS, others (like MGS and FF8)\n"
1899                 "require BIOS to work.\n"
1900                 "After copying the file, you'll also need\n"
1901                 "to select it in the emu's menu:\n"
1902                 "options->[BIOS/Plugins]\n\n"
1903                 "The file is usually named SCPH1001.BIN,\n"
1904                 "but other not compressed files can be\n"
1905                 "used too.\n\n"
1906                 "Press %s or %s to continue";
1907         char tmp_msg[sizeof(msg) + 64];
1908
1909         snprintf(tmp_msg, sizeof(tmp_msg), msg,
1910                 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1911         while (1)
1912         {
1913                 draw_menu_message(tmp_msg, NULL);
1914
1915                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
1916                 if (inp & (PBTN_MBACK|PBTN_MOK))
1917                         return;
1918         }
1919 }
1920
1921 // ------------ main menu ------------
1922
1923 static menu_entry e_menu_main[];
1924
1925 static void draw_frame_main(void)
1926 {
1927         struct tm *tmp;
1928         time_t ltime;
1929         int capacity;
1930         char ltime_s[16];
1931         char buff[64];
1932         char *out;
1933
1934         if (CdromId[0] != 0) {
1935                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1936                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1937                          Config.HLE ? "HLE" : "BIOS");
1938                 smalltext_out16(4, 1, buff, 0x105f);
1939         }
1940
1941         if (ready_to_go) {
1942                 capacity = plat_target_bat_capacity_get();
1943                 ltime = time(NULL);
1944                 tmp = localtime(&ltime);
1945                 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1946                 if (capacity >= 0) {
1947                         snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1948                         out = buff;
1949                 }
1950                 else
1951                         out = ltime_s;
1952                 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1953         }
1954 }
1955
1956 static void draw_frame_credits(void)
1957 {
1958         smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1959 }
1960
1961 static const char credits_text[] = 
1962         "PCSX-ReARMed\n\n"
1963         "(C) 1999-2003 PCSX Team\n"
1964         "(C) 2005-2009 PCSX-df Team\n"
1965         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1966         "ARM recompiler (C) 2009-2011 Ari64\n"
1967 #ifdef __ARM_NEON__
1968         "ARM NEON GPU (c) 2011-2012 Exophase\n"
1969 #endif
1970         "PEOpS GPU and SPU by Pete Bernert\n"
1971         "  and the P.E.Op.S. team\n"
1972         "PCSX4ALL plugin by PCSX4ALL team\n"
1973         "  Chui, Franxis, Unai\n\n"
1974         "integration, optimization and\n"
1975         "  frontend (C) 2010-2015 notaz\n";
1976
1977 static int reset_game(void)
1978 {
1979         // sanity check
1980         if (bios_sel == 0 && !Config.HLE)
1981                 return -1;
1982
1983         ClosePlugins();
1984         OpenPlugins();
1985         SysReset();
1986         if (CheckCdrom() != -1) {
1987                 LoadCdrom();
1988         }
1989         return 0;
1990 }
1991
1992 static int reload_plugins(const char *cdimg)
1993 {
1994         pl_vout_buf = NULL;
1995
1996         ClosePlugins();
1997
1998         set_cd_image(cdimg);
1999         LoadPlugins();
2000         pcnt_hook_plugins();
2001         NetOpened = 0;
2002         if (OpenPlugins() == -1) {
2003                 menu_update_msg("failed to open plugins");
2004                 return -1;
2005         }
2006         plugin_call_rearmed_cbs();
2007
2008         cdrIsoMultidiskCount = 1;
2009         CdromId[0] = '\0';
2010         CdromLabel[0] = '\0';
2011
2012         return 0;
2013 }
2014
2015 static int run_bios(void)
2016 {
2017         if (bios_sel == 0)
2018                 return -1;
2019
2020         ready_to_go = 0;
2021         if (reload_plugins(NULL) != 0)
2022                 return -1;
2023         SysReset();
2024
2025         ready_to_go = 1;
2026         return 0;
2027 }
2028
2029 static int run_exe(void)
2030 {
2031         const char *exts[] = { "exe", NULL };
2032         const char *fname;
2033
2034         fname = menu_loop_romsel(last_selected_fname,
2035                 sizeof(last_selected_fname), exts, NULL);
2036         if (fname == NULL)
2037                 return -1;
2038
2039         ready_to_go = 0;
2040         if (reload_plugins(NULL) != 0)
2041                 return -1;
2042
2043         SysReset();
2044         if (Load(fname) != 0) {
2045                 menu_update_msg("exe load failed, bad file?");
2046                 printf("meh\n");
2047                 return -1;
2048         }
2049
2050         ready_to_go = 1;
2051         return 0;
2052 }
2053
2054 static int run_cd_image(const char *fname)
2055 {
2056         int autoload_state = g_autostateld_opt;
2057
2058         ready_to_go = 0;
2059         reload_plugins(fname);
2060
2061         // always autodetect, menu_sync_config will override as needed
2062         Config.PsxAuto = 1;
2063
2064         if (CheckCdrom() == -1) {
2065                 // Only check the CD if we are starting the console with a CD
2066                 ClosePlugins();
2067                 menu_update_msg("unsupported/invalid CD image");
2068                 return -1;
2069         }
2070
2071         SysReset();
2072
2073         // Read main executable directly from CDRom and start it
2074         if (LoadCdrom() == -1) {
2075                 ClosePlugins();
2076                 menu_update_msg("failed to load CD image");
2077                 return -1;
2078         }
2079
2080         emu_on_new_cd(1);
2081         ready_to_go = 1;
2082
2083         if (autoload_state) {
2084                 unsigned int newest = 0;
2085                 int time, slot, newest_slot = -1;
2086
2087                 for (slot = 0; slot < 10; slot++) {
2088                         if (emu_check_save_file(slot, &time)) {
2089                                 if ((unsigned int)time > newest) {
2090                                         newest = time;
2091                                         newest_slot = slot;
2092                                 }
2093                         }
2094                 }
2095
2096                 if (newest_slot >= 0) {
2097                         lprintf("autoload slot %d\n", newest_slot);
2098                         emu_load_state(newest_slot);
2099                 }
2100                 else {
2101                         lprintf("no save to autoload.\n");
2102                 }
2103         }
2104
2105         return 0;
2106 }
2107
2108 static int romsel_run(void)
2109 {
2110         int prev_gpu, prev_spu;
2111         const char *fname;
2112
2113         fname = menu_loop_romsel(last_selected_fname,
2114                         sizeof(last_selected_fname), filter_exts,
2115                         optional_cdimg_filter);
2116         if (fname == NULL)
2117                 return -1;
2118
2119         printf("selected file: %s\n", fname);
2120
2121         new_dynarec_clear_full();
2122
2123         if (run_cd_image(fname) != 0)
2124                 return -1;
2125
2126         prev_gpu = gpu_plugsel;
2127         prev_spu = spu_plugsel;
2128         if (menu_load_config(1) != 0)
2129                 menu_load_config(0);
2130
2131         // check for plugin changes, have to repeat
2132         // loading if game config changed plugins to reload them
2133         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2134                 printf("plugin change detected, reloading plugins..\n");
2135                 if (run_cd_image(fname) != 0)
2136                         return -1;
2137         }
2138
2139         strcpy(last_selected_fname, fname);
2140         menu_do_last_cd_img(0);
2141         return 0;
2142 }
2143
2144 static int swap_cd_image(void)
2145 {
2146         const char *fname;
2147
2148         fname = menu_loop_romsel(last_selected_fname,
2149                         sizeof(last_selected_fname), filter_exts,
2150                         optional_cdimg_filter);
2151         if (fname == NULL)
2152                 return -1;
2153
2154         printf("selected file: %s\n", fname);
2155
2156         CdromId[0] = '\0';
2157         CdromLabel[0] = '\0';
2158
2159         set_cd_image(fname);
2160         if (ReloadCdromPlugin() < 0) {
2161                 menu_update_msg("failed to load cdr plugin");
2162                 return -1;
2163         }
2164         if (CDR_open() < 0) {
2165                 menu_update_msg("failed to open cdr plugin");
2166                 return -1;
2167         }
2168
2169         SetCdOpenCaseTime(time(NULL) + 2);
2170         LidInterrupt();
2171
2172         strcpy(last_selected_fname, fname);
2173         return 0;
2174 }
2175
2176 static int swap_cd_multidisk(void)
2177 {
2178         cdrIsoMultidiskSelect++;
2179         CdromId[0] = '\0';
2180         CdromLabel[0] = '\0';
2181
2182         CDR_close();
2183         if (CDR_open() < 0) {
2184                 menu_update_msg("failed to open cdr plugin");
2185                 return -1;
2186         }
2187
2188         SetCdOpenCaseTime(time(NULL) + 2);
2189         LidInterrupt();
2190
2191         return 0;
2192 }
2193
2194 static void load_pcsx_cht(void)
2195 {
2196         static const char *exts[] = { "cht", NULL };
2197         const char *fname;
2198         char msg[64];
2199
2200         fname = menu_loop_romsel(last_selected_fname,
2201                         sizeof(last_selected_fname), exts, NULL);
2202         if (fname == NULL)
2203                 return;
2204
2205         printf("selected cheat file: %s\n", fname);
2206         LoadCheats(fname);
2207
2208         if (NumCheats == 0 && NumCodes == 0)
2209                 menu_update_msg("failed to load cheats");
2210         else {
2211                 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2212                 menu_update_msg(msg);
2213         }
2214         me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2215 }
2216
2217 static int main_menu_handler(int id, int keys)
2218 {
2219         switch (id)
2220         {
2221         case MA_MAIN_RESUME_GAME:
2222                 if (ready_to_go)
2223                         return 1;
2224                 break;
2225         case MA_MAIN_SAVE_STATE:
2226                 if (ready_to_go)
2227                         return menu_loop_savestate(0);
2228                 break;
2229         case MA_MAIN_LOAD_STATE:
2230                 if (ready_to_go)
2231                         return menu_loop_savestate(1);
2232                 break;
2233         case MA_MAIN_RESET_GAME:
2234                 if (ready_to_go && reset_game() == 0)
2235                         return 1;
2236                 break;
2237         case MA_MAIN_LOAD_ROM:
2238                 if (romsel_run() == 0)
2239                         return 1;
2240                 break;
2241         case MA_MAIN_SWAP_CD:
2242                 if (swap_cd_image() == 0)
2243                         return 1;
2244                 break;
2245         case MA_MAIN_SWAP_CD_MULTI:
2246                 if (swap_cd_multidisk() == 0)
2247                         return 1;
2248                 break;
2249         case MA_MAIN_RUN_BIOS:
2250                 if (run_bios() == 0)
2251                         return 1;
2252                 break;
2253         case MA_MAIN_RUN_EXE:
2254                 if (run_exe() == 0)
2255                         return 1;
2256                 break;
2257         case MA_MAIN_CHEATS:
2258                 menu_loop_cheats();
2259                 break;
2260         case MA_MAIN_LOAD_CHEATS:
2261                 load_pcsx_cht();
2262                 break;
2263         case MA_MAIN_CREDITS:
2264                 draw_menu_message(credits_text, draw_frame_credits);
2265                 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2266                 break;
2267         case MA_MAIN_EXIT:
2268                 emu_core_ask_exit();
2269                 return 1;
2270         default:
2271                 lprintf("%s: something unknown selected\n", __FUNCTION__);
2272                 break;
2273         }
2274
2275         return 0;
2276 }
2277
2278 static menu_entry e_menu_main2[] =
2279 {
2280         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
2281         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2282         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
2283         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
2284         mee_handler   ("Memcard manager",    menu_loop_memcards),
2285         mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS,   main_menu_handler),
2286         mee_end,
2287 };
2288
2289 static int main_menu2_handler(int id, int keys)
2290 {
2291         static int sel = 0;
2292
2293         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
2294         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2295         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2296         me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2297
2298         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2299 }
2300
2301 static const char h_extra[] = "Change CD, manage memcards..\n";
2302
2303 static menu_entry e_menu_main[] =
2304 {
2305         mee_label     (""),
2306         mee_label     (""),
2307         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
2308         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
2309         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
2310         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
2311         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
2312         mee_handler   ("Options",            menu_loop_options),
2313         mee_handler   ("Controls",           menu_loop_keyconfig),
2314         mee_handler_id("Cheats",             MA_MAIN_CHEATS,      main_menu_handler),
2315         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
2316         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
2317         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
2318         mee_end,
2319 };
2320
2321 // ----------------------------
2322
2323 static void menu_leave_emu(void);
2324
2325 void menu_loop(void)
2326 {
2327         static int warned_about_bios = 0;
2328         static int sel = 0;
2329
2330         menu_leave_emu();
2331
2332         if (config_save_counter == 0) {
2333                 // assume first run
2334                 if (bioses[1] != NULL) {
2335                         // autoselect BIOS to make user's life easier
2336                         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2337                         bios_sel = 1;
2338                 }
2339                 else if (!warned_about_bios) {
2340                         menu_bios_warn();
2341                         warned_about_bios = 1;
2342                 }
2343         }
2344
2345         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2346         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
2347         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
2348         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
2349         me_enable(e_menu_main, MA_MAIN_CHEATS,      ready_to_go && NumCheats);
2350
2351         in_set_config_int(0, IN_CFG_BLOCKING, 1);
2352
2353         do {
2354                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2355         } while (!ready_to_go && !g_emu_want_quit);
2356
2357         /* wait until menu, ok, back is released */
2358         while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2359                 ;
2360
2361         in_set_config_int(0, IN_CFG_BLOCKING, 0);
2362
2363         menu_prepare_emu();
2364 }
2365
2366 static int qsort_strcmp(const void *p1, const void *p2)
2367 {
2368         char * const *s1 = (char * const *)p1;
2369         char * const *s2 = (char * const *)p2;
2370         return strcasecmp(*s1, *s2);
2371 }
2372
2373 static void scan_bios_plugins(void)
2374 {
2375         char fname[MAXPATHLEN];
2376         struct dirent *ent;
2377         int bios_i, gpu_i, spu_i, mc_i;
2378         char *p;
2379         DIR *dir;
2380
2381         bioses[0] = "HLE";
2382         gpu_plugins[0] = "builtin_gpu";
2383         spu_plugins[0] = "builtin_spu";
2384         memcards[0] = "(none)";
2385         bios_i = gpu_i = spu_i = mc_i = 1;
2386
2387         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2388         dir = opendir(fname);
2389         if (dir == NULL) {
2390                 perror("scan_bios_plugins bios opendir");
2391                 goto do_plugins;
2392         }
2393
2394         while (1) {
2395                 struct stat st;
2396
2397                 errno = 0;
2398                 ent = readdir(dir);
2399                 if (ent == NULL) {
2400                         if (errno != 0)
2401                                 perror("readdir");
2402                         break;
2403                 }
2404
2405                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2406                         continue;
2407
2408                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2409                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2410                         printf("bad BIOS file: %s\n", ent->d_name);
2411                         continue;
2412                 }
2413
2414                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2415                         bioses[bios_i++] = strdup(ent->d_name);
2416                         continue;
2417                 }
2418
2419                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2420         }
2421
2422         closedir(dir);
2423
2424 do_plugins:
2425         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2426         dir = opendir(fname);
2427         if (dir == NULL) {
2428                 perror("scan_bios_plugins plugins opendir");
2429                 goto do_memcards;
2430         }
2431
2432         while (1) {
2433                 void *h, *tmp;
2434
2435                 errno = 0;
2436                 ent = readdir(dir);
2437                 if (ent == NULL) {
2438                         if (errno != 0)
2439                                 perror("readdir");
2440                         break;
2441                 }
2442                 p = strstr(ent->d_name, ".so");
2443                 if (p == NULL)
2444                         continue;
2445
2446                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2447                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2448                 if (h == NULL) {
2449                         fprintf(stderr, "%s\n", dlerror());
2450                         continue;
2451                 }
2452
2453                 // now what do we have here?
2454                 tmp = dlsym(h, "GPUinit");
2455                 if (tmp) {
2456                         dlclose(h);
2457                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2458                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2459                         continue;
2460                 }
2461
2462                 tmp = dlsym(h, "SPUinit");
2463                 if (tmp) {
2464                         dlclose(h);
2465                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2466                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2467                         continue;
2468                 }
2469
2470                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2471                 dlclose(h);
2472         }
2473
2474         closedir(dir);
2475
2476 do_memcards:
2477         dir = opendir("." MEMCARD_DIR);
2478         if (dir == NULL) {
2479                 perror("scan_bios_plugins memcards opendir");
2480                 return;
2481         }
2482
2483         while (1) {
2484                 struct stat st;
2485
2486                 errno = 0;
2487                 ent = readdir(dir);
2488                 if (ent == NULL) {
2489                         if (errno != 0)
2490                                 perror("readdir");
2491                         break;
2492                 }
2493
2494                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2495                         continue;
2496
2497                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2498                 if (stat(fname, &st) != 0) {
2499                         printf("bad memcard file: %s\n", ent->d_name);
2500                         continue;
2501                 }
2502
2503                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2504                         memcards[mc_i++] = strdup(ent->d_name);
2505                         continue;
2506                 }
2507
2508                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2509         }
2510
2511         if (mc_i > 2)
2512                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2513
2514         closedir(dir);
2515 }
2516
2517 void menu_init(void)
2518 {
2519         char buff[MAXPATHLEN];
2520         int i;
2521
2522         cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2523
2524         scan_bios_plugins();
2525         menu_init_base();
2526
2527         menu_set_defconfig();
2528         menu_load_config(0);
2529         menu_do_last_cd_img(1);
2530         last_vout_w = 320;
2531         last_vout_h = 240;
2532         last_vout_bpp = 16;
2533
2534         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2535         g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2536         if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2537                 fprintf(stderr, "OOM\n");
2538                 exit(1);
2539         }
2540
2541         emu_make_path(buff, "skin/background.png", sizeof(buff));
2542         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2543
2544         i = plat_target.cpu_clock_set != NULL
2545                 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2546         me_enable(e_menu_gfx_options, MA_OPT_CPU_CLOCKS, i);
2547
2548         i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2549         e_menu_gfx_options[i].data = plat_target.vout_methods;
2550         me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2551                 plat_target.vout_methods != NULL);
2552
2553         i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2554         e_menu_gfx_options[i].data = plat_target.hwfilters;
2555         me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2556                 plat_target.hwfilters != NULL);
2557
2558         me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2559                 plat_target.gamma_set != NULL);
2560
2561 #ifdef HAVE_PRE_ARMV7
2562         me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2563 #endif
2564         me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2565         me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2566         me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2567         me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2568         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2569         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2570         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2571 }
2572
2573 void menu_notify_mode_change(int w, int h, int bpp)
2574 {
2575         last_vout_w = w;
2576         last_vout_h = h;
2577         last_vout_bpp = bpp;
2578 }
2579
2580 static void menu_leave_emu(void)
2581 {
2582         if (GPU_close != NULL) {
2583                 int ret = GPU_close();
2584                 if (ret)
2585                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2586         }
2587
2588         plat_video_menu_enter(ready_to_go);
2589
2590         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2591         if (pl_vout_buf != NULL && ready_to_go) {
2592                 int x = max(0, g_menuscreen_w - last_vout_w);
2593                 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2594                 int w = min(g_menuscreen_w, last_vout_w);
2595                 int h = min(g_menuscreen_h, last_vout_h);
2596                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2597                 char *s = pl_vout_buf;
2598
2599                 if (last_vout_bpp == 16) {
2600                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2601                                 menu_darken_bg(d, s, w, 0);
2602                 }
2603                 else {
2604                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2605                                 rgb888_to_rgb565(d, s, w * 3);
2606                                 menu_darken_bg(d, d, w, 0);
2607                         }
2608                 }
2609         }
2610
2611         if (ready_to_go)
2612                 cpu_clock = plat_target_cpu_clock_get();
2613 }
2614
2615 void menu_prepare_emu(void)
2616 {
2617         R3000Acpu *prev_cpu = psxCpu;
2618
2619         plat_video_menu_leave();
2620
2621         #ifndef DRC_DISABLE
2622         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2623         #else
2624         psxCpu = &psxInt;
2625         #endif
2626         if (psxCpu != prev_cpu) {
2627                 prev_cpu->Shutdown();
2628                 psxCpu->Init();
2629                 // note that this does not really reset, just clears drc caches
2630                 psxCpu->Reset();
2631         }
2632
2633         psxCpu->ApplyConfig();
2634
2635         // core doesn't care about Config.Cdda changes,
2636         // so handle them manually here
2637         if (Config.Cdda)
2638                 CDR_stop();
2639
2640         menu_sync_config();
2641         if (cpu_clock > 0)
2642                 plat_target_cpu_clock_set(cpu_clock);
2643
2644         // push config to GPU plugin
2645         plugin_call_rearmed_cbs();
2646
2647         if (GPU_open != NULL) {
2648                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2649                 if (ret)
2650                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2651         }
2652
2653         dfinput_activate();
2654 }
2655
2656 void menu_update_msg(const char *msg)
2657 {
2658         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2659         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2660
2661         menu_error_time = plat_get_ticks_ms();
2662         lprintf("msg: %s\n", menu_error_msg);
2663 }
2664
2665 void menu_finish(void)
2666 {
2667         if (cpu_clock_st > 0)
2668                 plat_target_cpu_clock_set(cpu_clock_st);
2669 }