spu: fix first sample, don't play ahead
[pcsx_rearmed.git] / frontend / menu.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2010-2013
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
6  *  - GNU GPL, version 2 or later.
7  *  - GNU LGPL, version 2.1 or later.
8  * See the COPYING file in the top-level directory.
9  */
10
11 #define _GNU_SOURCE 1
12 #include <stdio.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <dlfcn.h>
16 #include <zlib.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <unistd.h>
20 #include <dirent.h>
21
22 #include "main.h"
23 #include "menu.h"
24 #include "config.h"
25 #include "plugin.h"
26 #include "plugin_lib.h"
27 #include "plat.h"
28 #include "pcnt.h"
29 #include "cspace.h"
30 #include "libpicofe/plat.h"
31 #include "libpicofe/input.h"
32 #include "libpicofe/linux/in_evdev.h"
33 #include "libpicofe/plat.h"
34 #include "../libpcsxcore/misc.h"
35 #include "../libpcsxcore/cdrom.h"
36 #include "../libpcsxcore/cdriso.h"
37 #include "../libpcsxcore/cheat.h"
38 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
39 #include "../plugins/dfinput/externals.h"
40 #include "../plugins/dfsound/spu_config.h"
41 #include "psemu_plugin_defs.h"
42 #include "revision.h"
43
44 #define REARMED_BIRTHDAY_TIME 1293306830        /* 25 Dec 2010 */
45
46 #define array_size(x) (sizeof(x) / sizeof(x[0]))
47
48 typedef enum
49 {
50         MA_NONE = 1,
51         MA_MAIN_RESUME_GAME,
52         MA_MAIN_SAVE_STATE,
53         MA_MAIN_LOAD_STATE,
54         MA_MAIN_RESET_GAME,
55         MA_MAIN_LOAD_ROM,
56         MA_MAIN_SWAP_CD,
57         MA_MAIN_SWAP_CD_MULTI,
58         MA_MAIN_RUN_BIOS,
59         MA_MAIN_RUN_EXE,
60         MA_MAIN_LOAD_CHEATS,
61         MA_MAIN_CHEATS,
62         MA_MAIN_CONTROLS,
63         MA_MAIN_CREDITS,
64         MA_MAIN_EXIT,
65         MA_CTRL_PLAYER1,
66         MA_CTRL_PLAYER2,
67         MA_CTRL_ANALOG,
68         MA_CTRL_EMU,
69         MA_CTRL_DEV_FIRST,
70         MA_CTRL_DEV_NEXT,
71         MA_CTRL_NUBS_BTNS,
72         MA_CTRL_DEADZONE,
73         MA_CTRL_VIBRATION,
74         MA_CTRL_DONE,
75         MA_OPT_SAVECFG,
76         MA_OPT_SAVECFG_GAME,
77         MA_OPT_CPU_CLOCKS,
78         MA_OPT_DISP_OPTS,
79         MA_OPT_VARSCALER,
80         MA_OPT_VARSCALER_C,
81         MA_OPT_SCALER2,
82         MA_OPT_HWFILTER,
83         MA_OPT_SWFILTER,
84         MA_OPT_GAMMA,
85         MA_OPT_VOUT_MODE,
86 } menu_id;
87
88 static int last_vout_w, last_vout_h, last_vout_bpp;
89 static int cpu_clock, cpu_clock_st, volume_boost, frameskip;
90 static char last_selected_fname[MAXPATHLEN];
91 static int config_save_counter, region, in_type_sel1, in_type_sel2;
92 static int psx_clock;
93 static int memcard1_sel, memcard2_sel;
94 int g_opts, g_scaler, g_gamma = 100;
95 int soft_scaling, analog_deadzone; // for Caanoo
96 int soft_filter;
97
98 #ifdef __ARM_ARCH_7A__
99 #define DEFAULT_PSX_CLOCK 57
100 #define DEFAULT_PSX_CLOCK_S "57"
101 #else
102 #define DEFAULT_PSX_CLOCK 50
103 #define DEFAULT_PSX_CLOCK_S "50"
104 #endif
105
106 static const char *bioses[24];
107 static const char *gpu_plugins[16];
108 static const char *spu_plugins[16];
109 static const char *memcards[32];
110 static int bios_sel, gpu_plugsel, spu_plugsel;
111
112 #ifndef UI_FEATURES_H
113 #define MENU_BIOS_PATH "bios/"
114 #define MENU_SHOW_VARSCALER 0
115 #define MENU_SHOW_VOUTMODE 1
116 #define MENU_SHOW_SCALER2 0
117 #define MENU_SHOW_NUBS_BTNS 0
118 #define MENU_SHOW_VIBRATION 0
119 #define MENU_SHOW_DEADZONE 0
120 #define MENU_SHOW_MINIMIZE 0
121 #define MENU_SHOW_FULLSCREEN 1
122 #define MENU_SHOW_VOLUME 0
123 #endif
124
125 static int min(int x, int y) { return x < y ? x : y; }
126 static int max(int x, int y) { return x > y ? x : y; }
127
128 void emu_make_path(char *buff, const char *end, int size)
129 {
130         int pos, end_len;
131
132         end_len = strlen(end);
133         pos = plat_get_root_dir(buff, size);
134         strncpy(buff + pos, end, size - pos);
135         buff[size - 1] = 0;
136         if (pos + end_len > size - 1)
137                 printf("Warning: path truncated: %s\n", buff);
138 }
139
140 static int emu_check_save_file(int slot, int *time)
141 {
142         char fname[MAXPATHLEN];
143         struct stat status;
144         int ret;
145         
146         ret = emu_check_state(slot);
147         if (ret != 0 || time == NULL)
148                 return ret == 0 ? 1 : 0;
149
150         ret = get_state_filename(fname, sizeof(fname), slot);
151         if (ret != 0)
152                 return 1;
153
154         ret = stat(fname, &status);
155         if (ret != 0)
156                 return 1;
157
158         if (status.st_mtime < REARMED_BIRTHDAY_TIME)
159                 return 1; // probably bad rtc like on some Caanoos
160
161         *time = status.st_mtime;
162
163         return 1;
164 }
165
166 static int emu_save_load_game(int load, int unused)
167 {
168         int ret;
169
170         if (load) {
171                 ret = emu_load_state(state_slot);
172
173                 // reflect hle/bios mode from savestate
174                 if (Config.HLE)
175                         bios_sel = 0;
176                 else if (bios_sel == 0 && bioses[1] != NULL)
177                         // XXX: maybe find the right bios instead
178                         bios_sel = 1;
179         }
180         else
181                 ret = emu_save_state(state_slot);
182
183         return ret;
184 }
185
186 static void rm_namelist_entry(struct dirent **namelist,
187         int count, const char *name)
188 {
189         int i;
190
191         for (i = 1; i < count; i++) {
192                 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
193                         continue;
194
195                 if (strcmp(name, namelist[i]->d_name) == 0) {
196                         free(namelist[i]);
197                         namelist[i] = NULL;
198                         break;
199                 }
200         }
201 }
202
203 static int optional_cdimg_filter(struct dirent **namelist, int count,
204         const char *basedir)
205 {
206         const char *ext, *p;
207         char buf[256], buf2[256];
208         int i, d, ret, good_cue;
209         struct stat64 statf;
210         FILE *f;
211
212         for (i = 1; i < count; i++) {
213                 if (namelist[i] == NULL || namelist[i]->d_type == DT_DIR)
214                         continue;
215
216                 ext = strrchr(namelist[i]->d_name, '.');
217                 if (ext == NULL) {
218                         // should not happen but whatever
219                         free(namelist[i]);
220                         namelist[i] = NULL;
221                         continue;
222                 }
223                 ext++;
224
225                 // first find .cue files and remove files they reference
226                 if (strcasecmp(ext, "cue") == 0)
227                 {
228                         snprintf(buf, sizeof(buf), "%s/%s", basedir,
229                                 namelist[i]->d_name);
230
231                         f = fopen(buf, "r");
232                         if (f == NULL) {
233                                 free(namelist[i]);
234                                 namelist[i] = NULL;
235                                 continue;
236                         }
237
238                         good_cue = 0;
239                         while (fgets(buf, sizeof(buf), f)) {
240                                 ret = sscanf(buf, " FILE \"%256[^\"]\"", buf2);
241                                 if (ret != 1)
242                                         ret = sscanf(buf, " FILE %256s", buf2);
243                                 if (ret != 1)
244                                         continue;
245
246                                 p = strrchr(buf2, '/');
247                                 if (p == NULL)
248                                         p = strrchr(buf2, '\\');
249                                 if (p != NULL)
250                                         p++;
251                                 else
252                                         p = buf2;
253
254                                 snprintf(buf, sizeof(buf), "%s/%s", basedir, p);
255                                 ret = stat64(buf, &statf);
256                                 if (ret == 0) {
257                                         rm_namelist_entry(namelist, count, p);
258                                         good_cue = 1;
259                                 }
260                         }
261                         fclose(f);
262
263                         if (!good_cue) {
264                                 free(namelist[i]);
265                                 namelist[i] = NULL;
266                         }
267                         continue;
268                 }
269
270                 p = strcasestr(namelist[i]->d_name, "track");
271                 if (p != NULL) {
272                         ret = strtoul(p + 5, NULL, 10);
273                         if (ret > 1) {
274                                 free(namelist[i]);
275                                 namelist[i] = NULL;
276                                 continue;
277                         }
278                 }
279         }
280
281         // compact namelist
282         for (i = d = 1; i < count; i++)
283                 if (namelist[i] != NULL)
284                         namelist[d++] = namelist[i];
285
286         return d;
287 }
288
289 // propagate menu settings to the emu vars
290 static void menu_sync_config(void)
291 {
292         static int allow_abs_only_old;
293
294         Config.PsxAuto = 1;
295         if (region > 0) {
296                 Config.PsxAuto = 0;
297                 Config.PsxType = region - 1;
298         }
299         cycle_multiplier = 10000 / psx_clock;
300
301         switch (in_type_sel1) {
302         case 1:  in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
303         case 2:  in_type1 = PSE_PAD_TYPE_GUNCON;    break;
304         default: in_type1 = PSE_PAD_TYPE_STANDARD;
305         }
306         switch (in_type_sel2) {
307         case 1:  in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
308         case 2:  in_type2 = PSE_PAD_TYPE_GUNCON;    break;
309         default: in_type2 = PSE_PAD_TYPE_STANDARD;
310         }
311         if (in_evdev_allow_abs_only != allow_abs_only_old) {
312                 in_probe();
313                 allow_abs_only_old = in_evdev_allow_abs_only;
314         }
315
316         spu_config.iVolume = 768 + 128 * volume_boost;
317         pl_rearmed_cbs.frameskip = frameskip - 1;
318         pl_timing_prepare(Config.PsxType);
319 }
320
321 static void menu_set_defconfig(void)
322 {
323         emu_set_default_config();
324
325         g_opts = 0;
326         g_scaler = SCALE_4_3;
327         g_gamma = 100;
328         volume_boost = 0;
329         frameskip = 0;
330         analog_deadzone = 50;
331         soft_scaling = 1;
332         soft_filter = 0;
333         plat_target.vout_fullscreen = 0;
334         psx_clock = DEFAULT_PSX_CLOCK;
335
336         region = 0;
337         in_type_sel1 = in_type_sel2 = 0;
338         in_evdev_allow_abs_only = 0;
339
340         menu_sync_config();
341 }
342
343 #define CE_CONFIG_STR(val) \
344         { #val, 0, Config.val }
345
346 #define CE_CONFIG_VAL(val) \
347         { #val, sizeof(Config.val), &Config.val }
348
349 #define CE_STR(val) \
350         { #val, 0, val }
351
352 #define CE_INTVAL(val) \
353         { #val, sizeof(val), &val }
354
355 #define CE_INTVAL_N(name, val) \
356         { name, sizeof(val), &val }
357
358 #define CE_INTVAL_P(val) \
359         { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
360
361 // 'versioned' var, used when defaults change
362 #define CE_CONFIG_STR_V(val, ver) \
363         { #val #ver, 0, Config.val }
364
365 #define CE_INTVAL_V(val, ver) \
366         { #val #ver, sizeof(val), &val }
367
368 #define CE_INTVAL_PV(val, ver) \
369         { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
370
371 static const struct {
372         const char *name;
373         size_t len;
374         void *val;
375 } config_data[] = {
376         CE_CONFIG_STR(Bios),
377         CE_CONFIG_STR_V(Gpu, 3),
378         CE_CONFIG_STR(Spu),
379 //      CE_CONFIG_STR(Cdr),
380         CE_CONFIG_VAL(Xa),
381 //      CE_CONFIG_VAL(Sio),
382         CE_CONFIG_VAL(Mdec),
383         CE_CONFIG_VAL(Cdda),
384         CE_CONFIG_VAL(Debug),
385         CE_CONFIG_VAL(PsxOut),
386         CE_CONFIG_VAL(SpuIrq),
387         CE_CONFIG_VAL(RCntFix),
388         CE_CONFIG_VAL(VSyncWA),
389         CE_CONFIG_VAL(Cpu),
390         CE_INTVAL(region),
391         CE_INTVAL_V(g_scaler, 2),
392         CE_INTVAL(g_gamma),
393         CE_INTVAL(g_layer_x),
394         CE_INTVAL(g_layer_y),
395         CE_INTVAL(g_layer_w),
396         CE_INTVAL(g_layer_h),
397         CE_INTVAL(soft_filter),
398         CE_INTVAL(plat_target.vout_method),
399         CE_INTVAL(plat_target.hwfilter),
400         CE_INTVAL(plat_target.vout_fullscreen),
401         CE_INTVAL(state_slot),
402         CE_INTVAL(cpu_clock),
403         CE_INTVAL(g_opts),
404         CE_INTVAL(in_type_sel1),
405         CE_INTVAL(in_type_sel2),
406         CE_INTVAL(analog_deadzone),
407         CE_INTVAL_N("adev0_is_nublike", in_adev_is_nublike[0]),
408         CE_INTVAL_N("adev1_is_nublike", in_adev_is_nublike[1]),
409         CE_INTVAL_V(frameskip, 3),
410         CE_INTVAL_P(gpu_peops.iUseDither),
411         CE_INTVAL_P(gpu_peops.dwActFixes),
412         CE_INTVAL_P(gpu_unai.lineskip),
413         CE_INTVAL_P(gpu_unai.abe_hack),
414         CE_INTVAL_P(gpu_unai.no_light),
415         CE_INTVAL_P(gpu_unai.no_blend),
416         CE_INTVAL_P(gpu_neon.allow_interlace),
417         CE_INTVAL_P(gpu_neon.enhancement_enable),
418         CE_INTVAL_P(gpu_neon.enhancement_no_main),
419         CE_INTVAL_P(gpu_peopsgl.bDrawDither),
420         CE_INTVAL_P(gpu_peopsgl.iFilterType),
421         CE_INTVAL_P(gpu_peopsgl.iFrameTexType),
422         CE_INTVAL_P(gpu_peopsgl.iUseMask),
423         CE_INTVAL_P(gpu_peopsgl.bOpaquePass),
424         CE_INTVAL_P(gpu_peopsgl.bAdvancedBlend),
425         CE_INTVAL_P(gpu_peopsgl.bUseFastMdec),
426         CE_INTVAL_P(gpu_peopsgl.iVRamSize),
427         CE_INTVAL_P(gpu_peopsgl.iTexGarbageCollection),
428         CE_INTVAL_P(gpu_peopsgl.dwActFixes),
429         CE_INTVAL(spu_config.iUseReverb),
430         CE_INTVAL(spu_config.iXAPitch),
431         CE_INTVAL(spu_config.iUseInterpolation),
432         CE_INTVAL(spu_config.iTempo),
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 static const char h_spu_tempo[]     = "Slows down audio if emu is too slow\n"
1400                                       "This is inaccurate and breaks games";
1401
1402 static menu_entry e_menu_plugin_spu[] =
1403 {
1404         mee_range_h   ("Volume boost",              0, volume_boost, -5, 30, h_spu_volboost),
1405         mee_onoff     ("Reverb",                    0, spu_config.iUseReverb, 1),
1406         mee_enum      ("Interpolation",             0, spu_config.iUseInterpolation, men_spu_interp),
1407         mee_onoff     ("Adjust XA pitch",           0, spu_config.iXAPitch, 1),
1408         mee_onoff_h   ("Adjust tempo",              0, spu_config.iTempo, 1, h_spu_tempo),
1409         mee_end,
1410 };
1411
1412 static int menu_loop_plugin_spu(int id, int keys)
1413 {
1414         static int sel = 0;
1415         me_loop(e_menu_plugin_spu, &sel);
1416         return 0;
1417 }
1418
1419 static const char h_bios[]       = "HLE is simulated BIOS. BIOS selection is saved in\n"
1420                                    "savestates and can't be changed there. Must save\n"
1421                                    "config and reload the game for change to take effect";
1422 static const char h_plugin_gpu[] = 
1423 #ifdef __ARM_NEON__
1424                                    "builtin_gpu is the NEON GPU, very fast and accurate\n"
1425 #endif
1426                                    "gpu_peops is Pete's soft GPU, slow but accurate\n"
1427                                    "gpu_unai is GPU from PCSX4ALL, fast but glitchy\n"
1428                                    "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1429                                    "must save config and reload the game if changed";
1430 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1431                                    "must save config and reload the game if changed";
1432 static const char h_gpu_peops[]  = "Configure P.E.Op.S. SoftGL Driver V1.17";
1433 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1434 static const char h_gpu_unai[]   = "Configure Unai/PCSX4ALL Team GPU plugin";
1435 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1436
1437 static menu_entry e_menu_plugin_options[] =
1438 {
1439         mee_enum_h    ("BIOS",                          0, bios_sel, bioses, h_bios),
1440         mee_enum_h    ("GPU plugin",                    0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1441         mee_enum_h    ("SPU plugin",                    0, spu_plugsel, spu_plugins, h_plugin_spu),
1442 #ifdef __ARM_NEON__
1443         mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1444 #endif
1445         mee_handler_h ("Configure gpu_peops plugin",    menu_loop_plugin_gpu_peops, h_gpu_peops),
1446         mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1447         mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1448         mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1449         mee_end,
1450 };
1451
1452 static menu_entry e_menu_main2[];
1453
1454 static int menu_loop_plugin_options(int id, int keys)
1455 {
1456         static int sel = 0;
1457         me_loop(e_menu_plugin_options, &sel);
1458
1459         // sync BIOS/plugins
1460         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1461         snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1462         snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1463         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1464
1465         return 0;
1466 }
1467
1468 // ------------ adv options menu ------------
1469
1470 static const char h_cfg_psxclk[]  = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1471                                     "(lower value - less work for the emu, may be faster)";
1472 static const char h_cfg_nosmc[]   = "Will cause crashes when loading, break memcards";
1473 static const char h_cfg_gteunn[]  = "May cause graphical glitches";
1474 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1475
1476 static menu_entry e_menu_speed_hacks[] =
1477 {
1478         mee_range_h   ("PSX CPU clock, %%",        0, psx_clock, 1, 500, h_cfg_psxclk),
1479         mee_onoff_h   ("Disable SMC checks",       0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1480         mee_onoff_h   ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1481         mee_onoff_h   ("Disable GTE flags",        0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1482         mee_end,
1483 };
1484
1485 static int menu_loop_speed_hacks(int id, int keys)
1486 {
1487         static int sel = 0;
1488         me_loop(e_menu_speed_hacks, &sel);
1489         return 0;
1490 }
1491
1492 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
1493 static const char h_cfg_spu[]    = "Shows active SPU channels\n"
1494                                    "(green: normal, red: fmod, blue: noise)";
1495 static const char h_cfg_fl[]     = "Frame Limiter keeps the game from running too fast";
1496 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
1497 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
1498                                    "(proper .cue/.bin dump is needed otherwise)";
1499 static const char h_cfg_sio[]    = "You should not need this, breaks games";
1500 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1501 static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1502                                    "(timing hack, breaks other games)";
1503 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix\n"
1504                                    "(timing hack, breaks other games)";
1505 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
1506                                    "Might be useful to overcome some dynarec bugs";
1507 static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
1508                                    "must reload game for any change to take effect";
1509
1510 static menu_entry e_menu_adv_options[] =
1511 {
1512         mee_onoff_h   ("Show CPU load",          0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1513         mee_onoff_h   ("Show SPU channels",      0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1514         mee_onoff_h   ("Disable Frame Limiter",  0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1515         mee_onoff_h   ("Disable XA Decoding",    0, Config.Xa, 1, h_cfg_xa),
1516         mee_onoff_h   ("Disable CD Audio",       0, Config.Cdda, 1, h_cfg_cdda),
1517         //mee_onoff_h   ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1518         mee_onoff_h   ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1519         //mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
1520         mee_onoff_h   ("Rootcounter hack 2",     0, Config.VSyncWA, 1, h_cfg_rcnt2),
1521         mee_onoff_h   ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1522         mee_handler_h ("[Speed hacks]",             menu_loop_speed_hacks, h_cfg_shacks),
1523         mee_end,
1524 };
1525
1526 static int menu_loop_adv_options(int id, int keys)
1527 {
1528         static int sel = 0;
1529         me_loop(e_menu_adv_options, &sel);
1530         return 0;
1531 }
1532
1533 // ------------ options menu ------------
1534
1535 static int mh_restore_defaults(int id, int keys)
1536 {
1537         menu_set_defconfig();
1538         menu_update_msg("defaults restored");
1539         return 1;
1540 }
1541
1542 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
1543 static const char *men_frameskip[]    = { "Auto", "Off", "1", "2", "3", NULL };
1544 /*
1545 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1546 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
1547                                         "loading state or both";
1548 */
1549 static const char h_restore_def[]     = "Switches back to default / recommended\n"
1550                                         "configuration";
1551 static const char h_frameskip[]       = "Warning: frameskip sometimes causes glitches\n";
1552
1553 static menu_entry e_menu_options[] =
1554 {
1555 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1556 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1557         mee_enum_h    ("Frameskip",                0, frameskip, men_frameskip, h_frameskip),
1558         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1559         mee_enum      ("Region",                   0, region, men_region),
1560         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1561         mee_handler_id("[Display]",                MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1562         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1563         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1564         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1565         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1566         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1567         mee_end,
1568 };
1569
1570 static int menu_loop_options(int id, int keys)
1571 {
1572         static int sel = 0;
1573         int i;
1574
1575         i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1576         e_menu_options[i].enabled = cpu_clock_st > 0 ? 1 : 0;
1577         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1578
1579         me_loop(e_menu_options, &sel);
1580
1581         return 0;
1582 }
1583
1584 // ------------ debug menu ------------
1585
1586 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1587 {
1588         int w = min(g_menuscreen_w, 1024);
1589         int h = min(g_menuscreen_h, 512);
1590         u16 *d = g_menuscreen_ptr;
1591         u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1592         char buff[64];
1593         int ty = 1;
1594
1595         gpuf->ulFreezeVersion = 1;
1596         if (GPU_freeze != NULL)
1597                 GPU_freeze(1, gpuf);
1598
1599         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1600                 bgr555_to_rgb565(d, s, w * 2);
1601
1602         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1603         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1604         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1605         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1606         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1607 }
1608
1609 static void debug_menu_loop(void)
1610 {
1611         int inp, df_x = 0, df_y = 0;
1612         GPUFreeze_t *gpuf;
1613
1614         gpuf = malloc(sizeof(*gpuf));
1615         if (gpuf == NULL)
1616                 return;
1617
1618         while (1)
1619         {
1620                 menu_draw_begin(0, 1);
1621                 draw_frame_debug(gpuf, df_x, df_y);
1622                 menu_draw_end();
1623
1624                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1625                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1626                 if      (inp & PBTN_MBACK) break;
1627                 else if (inp & PBTN_UP)    { if (df_y > 0) df_y--; }
1628                 else if (inp & PBTN_DOWN)  { if (df_y < 512 - g_menuscreen_h) df_y++; }
1629                 else if (inp & PBTN_LEFT)  { if (df_x > 0) df_x -= 2; }
1630                 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1631         }
1632
1633         free(gpuf);
1634 }
1635
1636 // --------- memcard manager ---------
1637
1638 static void draw_mc_icon(int dx, int dy, const u16 *s)
1639 {
1640         u16 *d;
1641         int x, y, l, p;
1642         
1643         d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1644
1645         for (y = 0; y < 16; y++, s += 16) {
1646                 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1647                         for (x = 0; x < 16; x++) {
1648                                 p = s[x];
1649                                 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1650                                         | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1651                         }
1652                 }
1653         }
1654 }
1655
1656 static void draw_mc_bg(void)
1657 {
1658         McdBlock *blocks1, *blocks2;
1659         int maxicons = 15;
1660         int i, y, row2;
1661
1662         blocks1 = malloc(15 * sizeof(blocks1[0]));
1663         blocks2 = malloc(15 * sizeof(blocks1[0]));
1664         if (blocks1 == NULL || blocks2 == NULL)
1665                 goto out;
1666
1667         for (i = 0; i < 15; i++) {
1668                 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1669                 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1670         }
1671
1672         menu_draw_begin(1, 1);
1673
1674         memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1675
1676         y = g_menuscreen_h / 2 - 15 * 32 / 2;
1677         if (y < 0) {
1678                 // doesn't fit..
1679                 y = 0;
1680                 maxicons = g_menuscreen_h / 32;
1681         }
1682
1683         row2 = g_menuscreen_w / 2;
1684         for (i = 0; i < maxicons; i++) {
1685                 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1686                 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1687
1688                 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1689                 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1690         }
1691
1692         menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1693
1694         menu_draw_end();
1695 out:
1696         free(blocks1);
1697         free(blocks2);
1698 }
1699
1700 static void handle_memcard_sel(void)
1701 {
1702         strcpy(Config.Mcd1, "none");
1703         if (memcard1_sel != 0)
1704                 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1705         strcpy(Config.Mcd2, "none");
1706         if (memcard2_sel != 0)
1707                 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1708         LoadMcds(Config.Mcd1, Config.Mcd2);
1709         draw_mc_bg();
1710 }
1711
1712 static menu_entry e_memcard_options[] =
1713 {
1714         mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1715         mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1716         mee_end,
1717 };
1718
1719 static int menu_loop_memcards(int id, int keys)
1720 {
1721         static int sel = 0;
1722         char *p;
1723         int i;
1724
1725         memcard1_sel = memcard2_sel = 0;
1726         p = strrchr(Config.Mcd1, '/');
1727         if (p != NULL)
1728                 for (i = 0; memcards[i] != NULL; i++)
1729                         if (strcmp(p + 1, memcards[i]) == 0)
1730                                 { memcard1_sel = i; break; }
1731         p = strrchr(Config.Mcd2, '/');
1732         if (p != NULL)
1733                 for (i = 0; memcards[i] != NULL; i++)
1734                         if (strcmp(p + 1, memcards[i]) == 0)
1735                                 { memcard2_sel = i; break; }
1736
1737         me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1738
1739         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1740
1741         return 0;
1742 }
1743
1744 // ------------ cheats menu ------------
1745
1746 static void draw_cheatlist(int sel)
1747 {
1748         int max_cnt, start, i, pos, active;
1749
1750         max_cnt = g_menuscreen_h / me_sfont_h;
1751         start = max_cnt / 2 - sel;
1752
1753         menu_draw_begin(1, 1);
1754
1755         for (i = 0; i < NumCheats; i++) {
1756                 pos = start + i;
1757                 if (pos < 0) continue;
1758                 if (pos >= max_cnt) break;
1759                 active = Cheats[i].Enabled;
1760                 smalltext_out16(14,                pos * me_sfont_h,
1761                         active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1762                 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1763                         Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1764         }
1765         pos = start + i;
1766         if (pos < max_cnt)
1767                 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1768
1769         text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1770         menu_draw_end();
1771 }
1772
1773 static void menu_loop_cheats(void)
1774 {
1775         static int menu_sel = 0;
1776         int inp;
1777
1778         for (;;)
1779         {
1780                 draw_cheatlist(menu_sel);
1781                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1782                                 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1783                 if (inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1784                 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1785                 if (inp &(PBTN_LEFT|PBTN_L))  { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1786                 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1787                 if (inp & PBTN_MOK) { // action
1788                         if (menu_sel < NumCheats)
1789                                 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1790                         else    break;
1791                 }
1792                 if (inp & PBTN_MBACK)
1793                         break;
1794         }
1795 }
1796
1797 // --------- main menu help ----------
1798
1799 static void menu_bios_warn(void)
1800 {
1801         int inp;
1802         static const char msg[] =
1803                 "You don't seem to have copied any BIOS\n"
1804                 "files to\n"
1805                 MENU_BIOS_PATH "\n\n"
1806
1807                 "While many games work fine with fake\n"
1808                 "(HLE) BIOS, others (like MGS and FF8)\n"
1809                 "require BIOS to work.\n"
1810                 "After copying the file, you'll also need\n"
1811                 "to select it in the emu's menu:\n"
1812                 "options->[BIOS/Plugins]\n\n"
1813                 "The file is usually named SCPH1001.BIN,\n"
1814                 "but other not compressed files can be\n"
1815                 "used too.\n\n"
1816                 "Press %s or %s to continue";
1817         char tmp_msg[sizeof(msg) + 64];
1818
1819         snprintf(tmp_msg, sizeof(tmp_msg), msg,
1820                 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1821         while (1)
1822         {
1823                 draw_menu_message(tmp_msg, NULL);
1824
1825                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
1826                 if (inp & (PBTN_MBACK|PBTN_MOK))
1827                         return;
1828         }
1829 }
1830
1831 // ------------ main menu ------------
1832
1833 static menu_entry e_menu_main[];
1834
1835 static void draw_frame_main(void)
1836 {
1837         struct tm *tmp;
1838         time_t ltime;
1839         int capacity;
1840         char ltime_s[16];
1841         char buff[64];
1842         char *out;
1843
1844         if (CdromId[0] != 0) {
1845                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1846                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1847                          Config.HLE ? "HLE" : "BIOS");
1848                 smalltext_out16(4, 1, buff, 0x105f);
1849         }
1850
1851         if (ready_to_go) {
1852                 capacity = plat_target_bat_capacity_get();
1853                 ltime = time(NULL);
1854                 tmp = localtime(&ltime);
1855                 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1856                 if (capacity >= 0) {
1857                         snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1858                         out = buff;
1859                 }
1860                 else
1861                         out = ltime_s;
1862                 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1863         }
1864 }
1865
1866 static void draw_frame_credits(void)
1867 {
1868         smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1869 }
1870
1871 static const char credits_text[] = 
1872         "PCSX-ReARMed\n\n"
1873         "(C) 1999-2003 PCSX Team\n"
1874         "(C) 2005-2009 PCSX-df Team\n"
1875         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1876         "ARM recompiler (C) 2009-2011 Ari64\n"
1877 #ifdef __ARM_NEON__
1878         "ARM NEON GPU (c) 2011-2012 Exophase\n"
1879 #endif
1880         "PEOpS GPU and SPU by Pete Bernert\n"
1881         "  and the P.E.Op.S. team\n"
1882         "PCSX4ALL plugin by PCSX4ALL team\n"
1883         "  Chui, Franxis, Unai\n\n"
1884         "integration, optimization and\n"
1885         "  frontend (C) 2010-2012 notaz\n";
1886
1887 static int reset_game(void)
1888 {
1889         // sanity check
1890         if (bios_sel == 0 && !Config.HLE)
1891                 return -1;
1892
1893         ClosePlugins();
1894         OpenPlugins();
1895         SysReset();
1896         if (CheckCdrom() != -1) {
1897                 LoadCdrom();
1898         }
1899         return 0;
1900 }
1901
1902 static int reload_plugins(const char *cdimg)
1903 {
1904         pl_vout_buf = NULL;
1905
1906         ClosePlugins();
1907
1908         set_cd_image(cdimg);
1909         LoadPlugins();
1910         pcnt_hook_plugins();
1911         NetOpened = 0;
1912         if (OpenPlugins() == -1) {
1913                 menu_update_msg("failed to open plugins");
1914                 return -1;
1915         }
1916         plugin_call_rearmed_cbs();
1917
1918         cdrIsoMultidiskCount = 1;
1919         CdromId[0] = '\0';
1920         CdromLabel[0] = '\0';
1921
1922         return 0;
1923 }
1924
1925 static int run_bios(void)
1926 {
1927         if (bios_sel == 0)
1928                 return -1;
1929
1930         ready_to_go = 0;
1931         if (reload_plugins(NULL) != 0)
1932                 return -1;
1933         SysReset();
1934
1935         ready_to_go = 1;
1936         return 0;
1937 }
1938
1939 static int run_exe(void)
1940 {
1941         const char *exts[] = { "exe", NULL };
1942         const char *fname;
1943
1944         fname = menu_loop_romsel(last_selected_fname,
1945                 sizeof(last_selected_fname), exts, NULL);
1946         if (fname == NULL)
1947                 return -1;
1948
1949         ready_to_go = 0;
1950         if (reload_plugins(NULL) != 0)
1951                 return -1;
1952
1953         SysReset();
1954         if (Load(fname) != 0) {
1955                 menu_update_msg("exe load failed, bad file?");
1956                 printf("meh\n");
1957                 return -1;
1958         }
1959
1960         ready_to_go = 1;
1961         return 0;
1962 }
1963
1964 static int run_cd_image(const char *fname)
1965 {
1966         ready_to_go = 0;
1967         reload_plugins(fname);
1968
1969         // always autodetect, menu_sync_config will override as needed
1970         Config.PsxAuto = 1;
1971
1972         if (CheckCdrom() == -1) {
1973                 // Only check the CD if we are starting the console with a CD
1974                 ClosePlugins();
1975                 menu_update_msg("unsupported/invalid CD image");
1976                 return -1;
1977         }
1978
1979         SysReset();
1980
1981         // Read main executable directly from CDRom and start it
1982         if (LoadCdrom() == -1) {
1983                 ClosePlugins();
1984                 menu_update_msg("failed to load CD image");
1985                 return -1;
1986         }
1987
1988         emu_on_new_cd(1);
1989         ready_to_go = 1;
1990
1991         return 0;
1992 }
1993
1994 static int romsel_run(void)
1995 {
1996         int prev_gpu, prev_spu;
1997         const char *fname;
1998
1999         fname = menu_loop_romsel(last_selected_fname,
2000                         sizeof(last_selected_fname), filter_exts,
2001                         optional_cdimg_filter);
2002         if (fname == NULL)
2003                 return -1;
2004
2005         printf("selected file: %s\n", fname);
2006
2007         new_dynarec_clear_full();
2008
2009         if (run_cd_image(fname) != 0)
2010                 return -1;
2011
2012         prev_gpu = gpu_plugsel;
2013         prev_spu = spu_plugsel;
2014         if (menu_load_config(1) != 0)
2015                 menu_load_config(0);
2016
2017         // check for plugin changes, have to repeat
2018         // loading if game config changed plugins to reload them
2019         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2020                 printf("plugin change detected, reloading plugins..\n");
2021                 if (run_cd_image(fname) != 0)
2022                         return -1;
2023         }
2024
2025         strcpy(last_selected_fname, fname);
2026         menu_do_last_cd_img(0);
2027         return 0;
2028 }
2029
2030 static int swap_cd_image(void)
2031 {
2032         const char *fname;
2033
2034         fname = menu_loop_romsel(last_selected_fname,
2035                         sizeof(last_selected_fname), filter_exts,
2036                         optional_cdimg_filter);
2037         if (fname == NULL)
2038                 return -1;
2039
2040         printf("selected file: %s\n", fname);
2041
2042         CdromId[0] = '\0';
2043         CdromLabel[0] = '\0';
2044
2045         set_cd_image(fname);
2046         if (ReloadCdromPlugin() < 0) {
2047                 menu_update_msg("failed to load cdr plugin");
2048                 return -1;
2049         }
2050         if (CDR_open() < 0) {
2051                 menu_update_msg("failed to open cdr plugin");
2052                 return -1;
2053         }
2054
2055         SetCdOpenCaseTime(time(NULL) + 2);
2056         LidInterrupt();
2057
2058         strcpy(last_selected_fname, fname);
2059         return 0;
2060 }
2061
2062 static int swap_cd_multidisk(void)
2063 {
2064         cdrIsoMultidiskSelect++;
2065         CdromId[0] = '\0';
2066         CdromLabel[0] = '\0';
2067
2068         CDR_close();
2069         if (CDR_open() < 0) {
2070                 menu_update_msg("failed to open cdr plugin");
2071                 return -1;
2072         }
2073
2074         SetCdOpenCaseTime(time(NULL) + 2);
2075         LidInterrupt();
2076
2077         return 0;
2078 }
2079
2080 static void load_pcsx_cht(void)
2081 {
2082         static const char *exts[] = { "cht", NULL };
2083         const char *fname;
2084         char msg[64];
2085
2086         fname = menu_loop_romsel(last_selected_fname,
2087                         sizeof(last_selected_fname), exts, NULL);
2088         if (fname == NULL)
2089                 return;
2090
2091         printf("selected cheat file: %s\n", fname);
2092         LoadCheats(fname);
2093
2094         if (NumCheats == 0 && NumCodes == 0)
2095                 menu_update_msg("failed to load cheats");
2096         else {
2097                 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2098                 menu_update_msg(msg);
2099         }
2100         me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2101 }
2102
2103 static int main_menu_handler(int id, int keys)
2104 {
2105         switch (id)
2106         {
2107         case MA_MAIN_RESUME_GAME:
2108                 if (ready_to_go)
2109                         return 1;
2110                 break;
2111         case MA_MAIN_SAVE_STATE:
2112                 if (ready_to_go)
2113                         return menu_loop_savestate(0);
2114                 break;
2115         case MA_MAIN_LOAD_STATE:
2116                 if (ready_to_go)
2117                         return menu_loop_savestate(1);
2118                 break;
2119         case MA_MAIN_RESET_GAME:
2120                 if (ready_to_go && reset_game() == 0)
2121                         return 1;
2122                 break;
2123         case MA_MAIN_LOAD_ROM:
2124                 if (romsel_run() == 0)
2125                         return 1;
2126                 break;
2127         case MA_MAIN_SWAP_CD:
2128                 if (swap_cd_image() == 0)
2129                         return 1;
2130                 break;
2131         case MA_MAIN_SWAP_CD_MULTI:
2132                 if (swap_cd_multidisk() == 0)
2133                         return 1;
2134                 break;
2135         case MA_MAIN_RUN_BIOS:
2136                 if (run_bios() == 0)
2137                         return 1;
2138                 break;
2139         case MA_MAIN_RUN_EXE:
2140                 if (run_exe() == 0)
2141                         return 1;
2142                 break;
2143         case MA_MAIN_CHEATS:
2144                 menu_loop_cheats();
2145                 break;
2146         case MA_MAIN_LOAD_CHEATS:
2147                 load_pcsx_cht();
2148                 break;
2149         case MA_MAIN_CREDITS:
2150                 draw_menu_message(credits_text, draw_frame_credits);
2151                 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2152                 break;
2153         case MA_MAIN_EXIT:
2154                 emu_core_ask_exit();
2155                 return 1;
2156         default:
2157                 lprintf("%s: something unknown selected\n", __FUNCTION__);
2158                 break;
2159         }
2160
2161         return 0;
2162 }
2163
2164 static menu_entry e_menu_main2[] =
2165 {
2166         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
2167         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2168         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
2169         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
2170         mee_handler   ("Memcard manager",    menu_loop_memcards),
2171         mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS,   main_menu_handler),
2172         mee_end,
2173 };
2174
2175 static int main_menu2_handler(int id, int keys)
2176 {
2177         static int sel = 0;
2178
2179         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
2180         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2181         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2182         me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2183
2184         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2185 }
2186
2187 static const char h_extra[] = "Change CD, manage memcards..\n";
2188
2189 static menu_entry e_menu_main[] =
2190 {
2191         mee_label     (""),
2192         mee_label     (""),
2193         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
2194         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
2195         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
2196         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
2197         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
2198         mee_handler   ("Options",            menu_loop_options),
2199         mee_handler   ("Controls",           menu_loop_keyconfig),
2200         mee_handler_id("Cheats",             MA_MAIN_CHEATS,      main_menu_handler),
2201         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
2202         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
2203         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
2204         mee_end,
2205 };
2206
2207 // ----------------------------
2208
2209 static void menu_leave_emu(void);
2210
2211 void menu_loop(void)
2212 {
2213         static int warned_about_bios = 0;
2214         static int sel = 0;
2215
2216         menu_leave_emu();
2217
2218         if (config_save_counter == 0) {
2219                 // assume first run
2220                 if (bioses[1] != NULL) {
2221                         // autoselect BIOS to make user's life easier
2222                         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2223                         bios_sel = 1;
2224                 }
2225                 else if (!warned_about_bios) {
2226                         menu_bios_warn();
2227                         warned_about_bios = 1;
2228                 }
2229         }
2230
2231         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2232         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
2233         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
2234         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
2235         me_enable(e_menu_main, MA_MAIN_CHEATS,      ready_to_go && NumCheats);
2236
2237         in_set_config_int(0, IN_CFG_BLOCKING, 1);
2238
2239         do {
2240                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2241         } while (!ready_to_go && !g_emu_want_quit);
2242
2243         /* wait until menu, ok, back is released */
2244         while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2245                 ;
2246
2247         in_set_config_int(0, IN_CFG_BLOCKING, 0);
2248
2249         menu_prepare_emu();
2250 }
2251
2252 static int qsort_strcmp(const void *p1, const void *p2)
2253 {
2254         char * const *s1 = (char * const *)p1;
2255         char * const *s2 = (char * const *)p2;
2256         return strcasecmp(*s1, *s2);
2257 }
2258
2259 static void scan_bios_plugins(void)
2260 {
2261         char fname[MAXPATHLEN];
2262         struct dirent *ent;
2263         int bios_i, gpu_i, spu_i, mc_i;
2264         char *p;
2265         DIR *dir;
2266
2267         bioses[0] = "HLE";
2268         gpu_plugins[0] = "builtin_gpu";
2269         spu_plugins[0] = "builtin_spu";
2270         memcards[0] = "(none)";
2271         bios_i = gpu_i = spu_i = mc_i = 1;
2272
2273         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2274         dir = opendir(fname);
2275         if (dir == NULL) {
2276                 perror("scan_bios_plugins bios opendir");
2277                 goto do_plugins;
2278         }
2279
2280         while (1) {
2281                 struct stat st;
2282
2283                 errno = 0;
2284                 ent = readdir(dir);
2285                 if (ent == NULL) {
2286                         if (errno != 0)
2287                                 perror("readdir");
2288                         break;
2289                 }
2290
2291                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2292                         continue;
2293
2294                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2295                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2296                         printf("bad BIOS file: %s\n", ent->d_name);
2297                         continue;
2298                 }
2299
2300                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2301                         bioses[bios_i++] = strdup(ent->d_name);
2302                         continue;
2303                 }
2304
2305                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2306         }
2307
2308         closedir(dir);
2309
2310 do_plugins:
2311         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2312         dir = opendir(fname);
2313         if (dir == NULL) {
2314                 perror("scan_bios_plugins plugins opendir");
2315                 goto do_memcards;
2316         }
2317
2318         while (1) {
2319                 void *h, *tmp;
2320
2321                 errno = 0;
2322                 ent = readdir(dir);
2323                 if (ent == NULL) {
2324                         if (errno != 0)
2325                                 perror("readdir");
2326                         break;
2327                 }
2328                 p = strstr(ent->d_name, ".so");
2329                 if (p == NULL)
2330                         continue;
2331
2332                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2333                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2334                 if (h == NULL) {
2335                         fprintf(stderr, "%s\n", dlerror());
2336                         continue;
2337                 }
2338
2339                 // now what do we have here?
2340                 tmp = dlsym(h, "GPUinit");
2341                 if (tmp) {
2342                         dlclose(h);
2343                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2344                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2345                         continue;
2346                 }
2347
2348                 tmp = dlsym(h, "SPUinit");
2349                 if (tmp) {
2350                         dlclose(h);
2351                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2352                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2353                         continue;
2354                 }
2355
2356                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2357                 dlclose(h);
2358         }
2359
2360         closedir(dir);
2361
2362 do_memcards:
2363         dir = opendir("." MEMCARD_DIR);
2364         if (dir == NULL) {
2365                 perror("scan_bios_plugins memcards opendir");
2366                 return;
2367         }
2368
2369         while (1) {
2370                 struct stat st;
2371
2372                 errno = 0;
2373                 ent = readdir(dir);
2374                 if (ent == NULL) {
2375                         if (errno != 0)
2376                                 perror("readdir");
2377                         break;
2378                 }
2379
2380                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2381                         continue;
2382
2383                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2384                 if (stat(fname, &st) != 0) {
2385                         printf("bad memcard file: %s\n", ent->d_name);
2386                         continue;
2387                 }
2388
2389                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2390                         memcards[mc_i++] = strdup(ent->d_name);
2391                         continue;
2392                 }
2393
2394                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2395         }
2396
2397         if (mc_i > 2)
2398                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2399
2400         closedir(dir);
2401 }
2402
2403 void menu_init(void)
2404 {
2405         char buff[MAXPATHLEN];
2406         int i;
2407
2408         cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2409
2410         scan_bios_plugins();
2411         menu_init_base();
2412
2413         menu_set_defconfig();
2414         menu_load_config(0);
2415         menu_do_last_cd_img(1);
2416         last_vout_w = 320;
2417         last_vout_h = 240;
2418         last_vout_bpp = 16;
2419
2420         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2421         g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2422         if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2423                 fprintf(stderr, "OOM\n");
2424                 exit(1);
2425         }
2426
2427         emu_make_path(buff, "skin/background.png", sizeof(buff));
2428         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2429
2430         i = plat_target.cpu_clock_set != NULL
2431                 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2432         me_enable(e_menu_gfx_options, MA_OPT_CPU_CLOCKS, i);
2433
2434         i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2435         e_menu_gfx_options[i].data = plat_target.vout_methods;
2436         me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2437                 plat_target.vout_methods != NULL);
2438
2439         i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2440         e_menu_gfx_options[i].data = plat_target.hwfilters;
2441         me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2442                 plat_target.hwfilters != NULL);
2443
2444         me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2445                 plat_target.gamma_set != NULL);
2446
2447 #ifndef __ARM_ARCH_7A__
2448         me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2449 #endif
2450         me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2451         me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2452         me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2453         me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2454         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2455         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2456         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2457 }
2458
2459 void menu_notify_mode_change(int w, int h, int bpp)
2460 {
2461         last_vout_w = w;
2462         last_vout_h = h;
2463         last_vout_bpp = bpp;
2464 }
2465
2466 static void menu_leave_emu(void)
2467 {
2468         if (GPU_close != NULL) {
2469                 int ret = GPU_close();
2470                 if (ret)
2471                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2472         }
2473
2474         plat_video_menu_enter(ready_to_go);
2475
2476         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2477         if (pl_vout_buf != NULL && ready_to_go) {
2478                 int x = max(0, g_menuscreen_w - last_vout_w);
2479                 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2480                 int w = min(g_menuscreen_w, last_vout_w);
2481                 int h = min(g_menuscreen_h, last_vout_h);
2482                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2483                 char *s = pl_vout_buf;
2484
2485                 if (last_vout_bpp == 16) {
2486                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2487                                 menu_darken_bg(d, s, w, 0);
2488                 }
2489                 else {
2490                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2491                                 rgb888_to_rgb565(d, s, w * 3);
2492                                 menu_darken_bg(d, d, w, 0);
2493                         }
2494                 }
2495         }
2496
2497         if (ready_to_go)
2498                 cpu_clock = plat_target_cpu_clock_get();
2499 }
2500
2501 void menu_prepare_emu(void)
2502 {
2503         R3000Acpu *prev_cpu = psxCpu;
2504
2505         plat_video_menu_leave();
2506
2507         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2508         if (psxCpu != prev_cpu) {
2509                 prev_cpu->Shutdown();
2510                 psxCpu->Init();
2511                 // note that this does not really reset, just clears drc caches
2512                 psxCpu->Reset();
2513         }
2514
2515         // core doesn't care about Config.Cdda changes,
2516         // so handle them manually here
2517         if (Config.Cdda)
2518                 CDR_stop();
2519
2520         menu_sync_config();
2521         if (cpu_clock > 0)
2522                 plat_target_cpu_clock_set(cpu_clock);
2523
2524         // push config to GPU plugin
2525         plugin_call_rearmed_cbs();
2526
2527         if (GPU_open != NULL) {
2528                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2529                 if (ret)
2530                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2531         }
2532
2533         dfinput_activate();
2534 }
2535
2536 void menu_update_msg(const char *msg)
2537 {
2538         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2539         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2540
2541         menu_error_time = plat_get_ticks_ms();
2542         lprintf("msg: %s\n", menu_error_msg);
2543 }
2544
2545 void menu_finish(void)
2546 {
2547         if (cpu_clock_st > 0)
2548                 plat_target_cpu_clock_set(cpu_clock_st);
2549 }