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