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