libretro: handle regions
[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 void OnFile_Exit();
1836
1837 static void draw_frame_main(void)
1838 {
1839         struct tm *tmp;
1840         time_t ltime;
1841         int capacity;
1842         char ltime_s[16];
1843         char buff[64];
1844         char *out;
1845
1846         if (CdromId[0] != 0) {
1847                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1848                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1849                          Config.HLE ? "HLE" : "BIOS");
1850                 smalltext_out16(4, 1, buff, 0x105f);
1851         }
1852
1853         if (ready_to_go) {
1854                 capacity = plat_target_bat_capacity_get();
1855                 ltime = time(NULL);
1856                 tmp = localtime(&ltime);
1857                 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1858                 if (capacity >= 0) {
1859                         snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1860                         out = buff;
1861                 }
1862                 else
1863                         out = ltime_s;
1864                 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1865         }
1866 }
1867
1868 static void draw_frame_credits(void)
1869 {
1870         smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1871 }
1872
1873 static const char credits_text[] = 
1874         "PCSX-ReARMed\n\n"
1875         "(C) 1999-2003 PCSX Team\n"
1876         "(C) 2005-2009 PCSX-df Team\n"
1877         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1878         "ARM recompiler (C) 2009-2011 Ari64\n"
1879 #ifdef __ARM_NEON__
1880         "ARM NEON GPU (c) 2011-2012 Exophase\n"
1881 #endif
1882         "PEOpS GPU and SPU by Pete Bernert\n"
1883         "  and the P.E.Op.S. team\n"
1884         "PCSX4ALL plugin by PCSX4ALL team\n"
1885         "  Chui, Franxis, Unai\n\n"
1886         "integration, optimization and\n"
1887         "  frontend (C) 2010-2012 notaz\n";
1888
1889 static int reset_game(void)
1890 {
1891         // sanity check
1892         if (bios_sel == 0 && !Config.HLE)
1893                 return -1;
1894
1895         ClosePlugins();
1896         OpenPlugins();
1897         SysReset();
1898         if (CheckCdrom() != -1) {
1899                 LoadCdrom();
1900         }
1901         return 0;
1902 }
1903
1904 static int reload_plugins(const char *cdimg)
1905 {
1906         pl_vout_buf = NULL;
1907
1908         ClosePlugins();
1909
1910         set_cd_image(cdimg);
1911         LoadPlugins();
1912         pcnt_hook_plugins();
1913         NetOpened = 0;
1914         if (OpenPlugins() == -1) {
1915                 menu_update_msg("failed to open plugins");
1916                 return -1;
1917         }
1918         plugin_call_rearmed_cbs();
1919
1920         cdrIsoMultidiskCount = 1;
1921         CdromId[0] = '\0';
1922         CdromLabel[0] = '\0';
1923
1924         return 0;
1925 }
1926
1927 static int run_bios(void)
1928 {
1929         if (bios_sel == 0)
1930                 return -1;
1931
1932         ready_to_go = 0;
1933         if (reload_plugins(NULL) != 0)
1934                 return -1;
1935         SysReset();
1936
1937         ready_to_go = 1;
1938         return 0;
1939 }
1940
1941 static int run_exe(void)
1942 {
1943         const char *exts[] = { "exe", NULL };
1944         const char *fname;
1945
1946         fname = menu_loop_romsel(last_selected_fname,
1947                 sizeof(last_selected_fname), exts, NULL);
1948         if (fname == NULL)
1949                 return -1;
1950
1951         ready_to_go = 0;
1952         if (reload_plugins(NULL) != 0)
1953                 return -1;
1954
1955         SysReset();
1956         if (Load(fname) != 0) {
1957                 menu_update_msg("exe load failed, bad file?");
1958                 printf("meh\n");
1959                 return -1;
1960         }
1961
1962         ready_to_go = 1;
1963         return 0;
1964 }
1965
1966 static int run_cd_image(const char *fname)
1967 {
1968         ready_to_go = 0;
1969         reload_plugins(fname);
1970
1971         // always autodetect, menu_sync_config will override as needed
1972         Config.PsxAuto = 1;
1973
1974         if (CheckCdrom() == -1) {
1975                 // Only check the CD if we are starting the console with a CD
1976                 ClosePlugins();
1977                 menu_update_msg("unsupported/invalid CD image");
1978                 return -1;
1979         }
1980
1981         SysReset();
1982
1983         // Read main executable directly from CDRom and start it
1984         if (LoadCdrom() == -1) {
1985                 ClosePlugins();
1986                 menu_update_msg("failed to load CD image");
1987                 return -1;
1988         }
1989
1990         emu_on_new_cd(1);
1991         ready_to_go = 1;
1992
1993         return 0;
1994 }
1995
1996 static int romsel_run(void)
1997 {
1998         int prev_gpu, prev_spu;
1999         const char *fname;
2000
2001         fname = menu_loop_romsel(last_selected_fname,
2002                         sizeof(last_selected_fname), filter_exts,
2003                         optional_cdimg_filter);
2004         if (fname == NULL)
2005                 return -1;
2006
2007         printf("selected file: %s\n", fname);
2008
2009         new_dynarec_clear_full();
2010
2011         if (run_cd_image(fname) != 0)
2012                 return -1;
2013
2014         prev_gpu = gpu_plugsel;
2015         prev_spu = spu_plugsel;
2016         if (menu_load_config(1) != 0)
2017                 menu_load_config(0);
2018
2019         // check for plugin changes, have to repeat
2020         // loading if game config changed plugins to reload them
2021         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2022                 printf("plugin change detected, reloading plugins..\n");
2023                 if (run_cd_image(fname) != 0)
2024                         return -1;
2025         }
2026
2027         strcpy(last_selected_fname, fname);
2028         menu_do_last_cd_img(0);
2029         return 0;
2030 }
2031
2032 static int swap_cd_image(void)
2033 {
2034         const char *fname;
2035
2036         fname = menu_loop_romsel(last_selected_fname,
2037                         sizeof(last_selected_fname), filter_exts,
2038                         optional_cdimg_filter);
2039         if (fname == NULL)
2040                 return -1;
2041
2042         printf("selected file: %s\n", fname);
2043
2044         CdromId[0] = '\0';
2045         CdromLabel[0] = '\0';
2046
2047         set_cd_image(fname);
2048         if (ReloadCdromPlugin() < 0) {
2049                 menu_update_msg("failed to load cdr plugin");
2050                 return -1;
2051         }
2052         if (CDR_open() < 0) {
2053                 menu_update_msg("failed to open cdr plugin");
2054                 return -1;
2055         }
2056
2057         SetCdOpenCaseTime(time(NULL) + 2);
2058         LidInterrupt();
2059
2060         strcpy(last_selected_fname, fname);
2061         return 0;
2062 }
2063
2064 static int swap_cd_multidisk(void)
2065 {
2066         cdrIsoMultidiskSelect++;
2067         CdromId[0] = '\0';
2068         CdromLabel[0] = '\0';
2069
2070         CDR_close();
2071         if (CDR_open() < 0) {
2072                 menu_update_msg("failed to open cdr plugin");
2073                 return -1;
2074         }
2075
2076         SetCdOpenCaseTime(time(NULL) + 2);
2077         LidInterrupt();
2078
2079         return 0;
2080 }
2081
2082 static void load_pcsx_cht(void)
2083 {
2084         const char *exts[] = { "cht", NULL };
2085         const char *fname;
2086         char path[256];
2087
2088         path[0] = 0;
2089         fname = menu_loop_romsel(path, sizeof(path), exts, NULL);
2090         if (fname == NULL)
2091                 return;
2092
2093         printf("selected cheat file: %s\n", fname);
2094         LoadCheats(fname);
2095
2096         if (NumCheats == 0 && NumCodes == 0)
2097                 menu_update_msg("failed to load cheats");
2098         else {
2099                 snprintf(path, sizeof(path), "%d cheat(s) loaded", NumCheats + NumCodes);
2100                 menu_update_msg(path);
2101         }
2102         me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2103 }
2104
2105 static int main_menu_handler(int id, int keys)
2106 {
2107         switch (id)
2108         {
2109         case MA_MAIN_RESUME_GAME:
2110                 if (ready_to_go)
2111                         return 1;
2112                 break;
2113         case MA_MAIN_SAVE_STATE:
2114                 if (ready_to_go)
2115                         return menu_loop_savestate(0);
2116                 break;
2117         case MA_MAIN_LOAD_STATE:
2118                 if (ready_to_go)
2119                         return menu_loop_savestate(1);
2120                 break;
2121         case MA_MAIN_RESET_GAME:
2122                 if (ready_to_go && reset_game() == 0)
2123                         return 1;
2124                 break;
2125         case MA_MAIN_LOAD_ROM:
2126                 if (romsel_run() == 0)
2127                         return 1;
2128                 break;
2129         case MA_MAIN_SWAP_CD:
2130                 if (swap_cd_image() == 0)
2131                         return 1;
2132                 break;
2133         case MA_MAIN_SWAP_CD_MULTI:
2134                 if (swap_cd_multidisk() == 0)
2135                         return 1;
2136                 break;
2137         case MA_MAIN_RUN_BIOS:
2138                 if (run_bios() == 0)
2139                         return 1;
2140                 break;
2141         case MA_MAIN_RUN_EXE:
2142                 if (run_exe() == 0)
2143                         return 1;
2144                 break;
2145         case MA_MAIN_CHEATS:
2146                 menu_loop_cheats();
2147                 break;
2148         case MA_MAIN_LOAD_CHEATS:
2149                 load_pcsx_cht();
2150                 break;
2151         case MA_MAIN_CREDITS:
2152                 draw_menu_message(credits_text, draw_frame_credits);
2153                 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2154                 break;
2155         case MA_MAIN_EXIT:
2156                 OnFile_Exit();
2157                 break;
2158         default:
2159                 lprintf("%s: something unknown selected\n", __FUNCTION__);
2160                 break;
2161         }
2162
2163         return 0;
2164 }
2165
2166 static menu_entry e_menu_main2[] =
2167 {
2168         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
2169         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2170         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
2171         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
2172         mee_handler   ("Memcard manager",    menu_loop_memcards),
2173         mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS,   main_menu_handler),
2174         mee_end,
2175 };
2176
2177 static int main_menu2_handler(int id, int keys)
2178 {
2179         static int sel = 0;
2180
2181         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
2182         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2183         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2184         me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2185
2186         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2187 }
2188
2189 static const char h_extra[] = "Change CD, manage memcards..\n";
2190
2191 static menu_entry e_menu_main[] =
2192 {
2193         mee_label     (""),
2194         mee_label     (""),
2195         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
2196         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
2197         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
2198         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
2199         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
2200         mee_handler   ("Options",            menu_loop_options),
2201         mee_handler   ("Controls",           menu_loop_keyconfig),
2202         mee_handler_id("Cheats",             MA_MAIN_CHEATS,      main_menu_handler),
2203         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
2204         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
2205         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
2206         mee_end,
2207 };
2208
2209 // ----------------------------
2210
2211 static void menu_leave_emu(void);
2212
2213 void menu_loop(void)
2214 {
2215         static int warned_about_bios = 0;
2216         static int sel = 0;
2217
2218         menu_leave_emu();
2219
2220         if (config_save_counter == 0) {
2221                 // assume first run
2222                 if (bioses[1] != NULL) {
2223                         // autoselect BIOS to make user's life easier
2224                         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2225                         bios_sel = 1;
2226                 }
2227                 else if (!warned_about_bios) {
2228                         menu_bios_warn();
2229                         warned_about_bios = 1;
2230                 }
2231         }
2232
2233         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2234         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
2235         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
2236         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
2237         me_enable(e_menu_main, MA_MAIN_CHEATS,      ready_to_go && NumCheats);
2238
2239         in_set_config_int(0, IN_CFG_BLOCKING, 1);
2240
2241         do {
2242                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2243         } while (!ready_to_go);
2244
2245         /* wait until menu, ok, back is released */
2246         while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2247                 ;
2248
2249         in_set_config_int(0, IN_CFG_BLOCKING, 0);
2250
2251         menu_prepare_emu();
2252 }
2253
2254 static int qsort_strcmp(const void *p1, const void *p2)
2255 {
2256         char * const *s1 = (char * const *)p1;
2257         char * const *s2 = (char * const *)p2;
2258         return strcasecmp(*s1, *s2);
2259 }
2260
2261 static void scan_bios_plugins(void)
2262 {
2263         char fname[MAXPATHLEN];
2264         struct dirent *ent;
2265         int bios_i, gpu_i, spu_i, mc_i;
2266         char *p;
2267         DIR *dir;
2268
2269         bioses[0] = "HLE";
2270         gpu_plugins[0] = "builtin_gpu";
2271         spu_plugins[0] = "builtin_spu";
2272         memcards[0] = "(none)";
2273         bios_i = gpu_i = spu_i = mc_i = 1;
2274
2275         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2276         dir = opendir(fname);
2277         if (dir == NULL) {
2278                 perror("scan_bios_plugins bios opendir");
2279                 goto do_plugins;
2280         }
2281
2282         while (1) {
2283                 struct stat st;
2284
2285                 errno = 0;
2286                 ent = readdir(dir);
2287                 if (ent == NULL) {
2288                         if (errno != 0)
2289                                 perror("readdir");
2290                         break;
2291                 }
2292
2293                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2294                         continue;
2295
2296                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2297                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2298                         printf("bad BIOS file: %s\n", ent->d_name);
2299                         continue;
2300                 }
2301
2302                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2303                         bioses[bios_i++] = strdup(ent->d_name);
2304                         continue;
2305                 }
2306
2307                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2308         }
2309
2310         closedir(dir);
2311
2312 do_plugins:
2313         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2314         dir = opendir(fname);
2315         if (dir == NULL) {
2316                 perror("scan_bios_plugins plugins opendir");
2317                 goto do_memcards;
2318         }
2319
2320         while (1) {
2321                 void *h, *tmp;
2322
2323                 errno = 0;
2324                 ent = readdir(dir);
2325                 if (ent == NULL) {
2326                         if (errno != 0)
2327                                 perror("readdir");
2328                         break;
2329                 }
2330                 p = strstr(ent->d_name, ".so");
2331                 if (p == NULL)
2332                         continue;
2333
2334                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2335                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2336                 if (h == NULL) {
2337                         fprintf(stderr, "%s\n", dlerror());
2338                         continue;
2339                 }
2340
2341                 // now what do we have here?
2342                 tmp = dlsym(h, "GPUinit");
2343                 if (tmp) {
2344                         dlclose(h);
2345                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2346                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2347                         continue;
2348                 }
2349
2350                 tmp = dlsym(h, "SPUinit");
2351                 if (tmp) {
2352                         dlclose(h);
2353                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2354                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2355                         continue;
2356                 }
2357
2358                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2359                 dlclose(h);
2360         }
2361
2362         closedir(dir);
2363
2364 do_memcards:
2365         dir = opendir("." MEMCARD_DIR);
2366         if (dir == NULL) {
2367                 perror("scan_bios_plugins memcards opendir");
2368                 return;
2369         }
2370
2371         while (1) {
2372                 struct stat st;
2373
2374                 errno = 0;
2375                 ent = readdir(dir);
2376                 if (ent == NULL) {
2377                         if (errno != 0)
2378                                 perror("readdir");
2379                         break;
2380                 }
2381
2382                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2383                         continue;
2384
2385                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2386                 if (stat(fname, &st) != 0) {
2387                         printf("bad memcard file: %s\n", ent->d_name);
2388                         continue;
2389                 }
2390
2391                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2392                         memcards[mc_i++] = strdup(ent->d_name);
2393                         continue;
2394                 }
2395
2396                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2397         }
2398
2399         if (mc_i > 2)
2400                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2401
2402         closedir(dir);
2403 }
2404
2405 void menu_init(void)
2406 {
2407         char buff[MAXPATHLEN];
2408         int i;
2409
2410         cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2411
2412         scan_bios_plugins();
2413         menu_init_base();
2414
2415         menu_set_defconfig();
2416         menu_load_config(0);
2417         menu_do_last_cd_img(1);
2418         last_vout_w = 320;
2419         last_vout_h = 240;
2420         last_vout_bpp = 16;
2421
2422         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2423         g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2424         if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2425                 fprintf(stderr, "OOM\n");
2426                 exit(1);
2427         }
2428
2429         emu_make_path(buff, "skin/background.png", sizeof(buff));
2430         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2431
2432         i = plat_target.cpu_clock_set != NULL
2433                 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2434         me_enable(e_menu_gfx_options, MA_OPT_CPU_CLOCKS, i);
2435
2436         i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2437         e_menu_gfx_options[i].data = plat_target.vout_methods;
2438         me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2439                 plat_target.vout_methods != NULL);
2440
2441         i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2442         e_menu_gfx_options[i].data = plat_target.hwfilters;
2443         me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2444                 plat_target.hwfilters != NULL);
2445
2446         me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2447                 plat_target.gamma_set != NULL);
2448
2449 #ifndef __ARM_ARCH_7A__
2450         me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2451 #endif
2452         me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2453         me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2454         me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2455         me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2456         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2457         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2458         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2459 }
2460
2461 void menu_notify_mode_change(int w, int h, int bpp)
2462 {
2463         last_vout_w = w;
2464         last_vout_h = h;
2465         last_vout_bpp = bpp;
2466 }
2467
2468 static void menu_leave_emu(void)
2469 {
2470         if (GPU_close != NULL) {
2471                 int ret = GPU_close();
2472                 if (ret)
2473                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2474         }
2475
2476         plat_video_menu_enter(ready_to_go);
2477
2478         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2479         if (pl_vout_buf != NULL && ready_to_go) {
2480                 int x = max(0, g_menuscreen_w - last_vout_w);
2481                 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2482                 int w = min(g_menuscreen_w, last_vout_w);
2483                 int h = min(g_menuscreen_h, last_vout_h);
2484                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2485                 char *s = pl_vout_buf;
2486
2487                 if (last_vout_bpp == 16) {
2488                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2489                                 menu_darken_bg(d, s, w, 0);
2490                 }
2491                 else {
2492                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2493                                 rgb888_to_rgb565(d, s, w * 3);
2494                                 menu_darken_bg(d, d, w, 0);
2495                         }
2496                 }
2497         }
2498
2499         if (ready_to_go)
2500                 cpu_clock = plat_target_cpu_clock_get();
2501 }
2502
2503 void menu_prepare_emu(void)
2504 {
2505         R3000Acpu *prev_cpu = psxCpu;
2506
2507         plat_video_menu_leave();
2508
2509         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2510         if (psxCpu != prev_cpu) {
2511                 prev_cpu->Shutdown();
2512                 psxCpu->Init();
2513                 // note that this does not really reset, just clears drc caches
2514                 psxCpu->Reset();
2515         }
2516
2517         // core doesn't care about Config.Cdda changes,
2518         // so handle them manually here
2519         if (Config.Cdda)
2520                 CDR_stop();
2521
2522         menu_sync_config();
2523         if (cpu_clock > 0)
2524                 plat_target_cpu_clock_set(cpu_clock);
2525
2526         // push config to GPU plugin
2527         plugin_call_rearmed_cbs();
2528
2529         if (GPU_open != NULL) {
2530                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2531                 if (ret)
2532                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2533         }
2534
2535         dfinput_activate();
2536 }
2537
2538 void menu_update_msg(const char *msg)
2539 {
2540         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2541         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2542
2543         menu_error_time = plat_get_ticks_ms();
2544         lprintf("msg: %s\n", menu_error_msg);
2545 }
2546
2547 void menu_finish(void)
2548 {
2549         if (cpu_clock_st > 0)
2550                 plat_target_cpu_clock_set(cpu_clock_st);
2551 }