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