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