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