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