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