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