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