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