2823a230d3e07a14976cc2dae9a33b0134ec1c31
[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, 2),
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[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
1198 static const char *men_soft_filter[] = { "None",
1199 #ifdef __ARM_NEON__
1200         "scale2x", "eagle2x",
1201 #endif
1202         NULL };
1203 static const char *men_dummy[] = { NULL };
1204 static const char h_cscaler[]   = "Displays the scaler layer, you can resize it\n"
1205                                   "using d-pad or move it using R+d-pad";
1206 static const char h_overlay[]   = "Overlay provides hardware accelerated scaling";
1207 static const char h_soft_filter[] = "Works only if game uses low resolution modes";
1208 static const char h_gamma[]     = "Gamma/brightness adjustment (default 100)";
1209
1210 static int menu_loop_cscaler(int id, int keys)
1211 {
1212         unsigned int inp;
1213
1214         g_scaler = SCALE_CUSTOM;
1215
1216         plat_gvideo_open(Config.PsxType);
1217
1218         for (;;)
1219         {
1220                 menu_draw_begin(0, 1);
1221                 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1222                 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1223                 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1224                 menu_draw_end();
1225
1226                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1227                                 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1228                 if (inp & PBTN_UP)    g_layer_y--;
1229                 if (inp & PBTN_DOWN)  g_layer_y++;
1230                 if (inp & PBTN_LEFT)  g_layer_x--;
1231                 if (inp & PBTN_RIGHT) g_layer_x++;
1232                 if (!(inp & PBTN_R)) {
1233                         if (inp & PBTN_UP)    g_layer_h += 2;
1234                         if (inp & PBTN_DOWN)  g_layer_h -= 2;
1235                         if (inp & PBTN_LEFT)  g_layer_w += 2;
1236                         if (inp & PBTN_RIGHT) g_layer_w -= 2;
1237                 }
1238                 if (inp & (PBTN_MOK|PBTN_MBACK))
1239                         break;
1240
1241                 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1242                         if (g_layer_x < 0)   g_layer_x = 0;
1243                         if (g_layer_x > 640) g_layer_x = 640;
1244                         if (g_layer_y < 0)   g_layer_y = 0;
1245                         if (g_layer_y > 420) g_layer_y = 420;
1246                         if (g_layer_w < 160) g_layer_w = 160;
1247                         if (g_layer_h < 60)  g_layer_h = 60;
1248                         if (g_layer_x + g_layer_w > 800)
1249                                 g_layer_w = 800 - g_layer_x;
1250                         if (g_layer_y + g_layer_h > 480)
1251                                 g_layer_h = 480 - g_layer_y;
1252                         // resize the layer
1253                         plat_gvideo_open(Config.PsxType);
1254                 }
1255         }
1256
1257         plat_gvideo_close();
1258
1259         return 0;
1260 }
1261
1262 static menu_entry e_menu_gfx_options[] =
1263 {
1264         mee_enum      ("Scaler",                   MA_OPT_VARSCALER, g_scaler, men_scaler),
1265         mee_enum      ("Video output mode",        MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1266         mee_onoff     ("Software Scaling",         MA_OPT_SCALER2, soft_scaling, 1),
1267         mee_enum      ("Hardware Filter",          MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1268         mee_enum_h    ("Software Filter",          MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1269         mee_range_h   ("Gamma adjustment",         MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1270 //      mee_onoff     ("Vsync",                    0, vsync, 1),
1271         mee_cust_h    ("Setup custom scaler",      MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1272         mee_end,
1273 };
1274
1275 static int menu_loop_gfx_options(int id, int keys)
1276 {
1277         static int sel = 0;
1278
1279         me_loop(e_menu_gfx_options, &sel);
1280
1281         return 0;
1282 }
1283
1284 // ------------ bios/plugins ------------
1285
1286 #ifdef __ARM_NEON__
1287
1288 static const char h_gpu_neon[] =
1289         "Configure built-in NEON GPU plugin";
1290 static const char h_gpu_neon_enhanced[] =
1291         "Renders in double resolution at the cost of lower performance\n"
1292         "(not available for high resolution games)";
1293 static const char h_gpu_neon_enhanced_hack[] =
1294         "Speed hack for above option (glitches some games)";
1295 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1296
1297 static menu_entry e_menu_plugin_gpu_neon[] =
1298 {
1299         mee_enum      ("Enable interlace mode",      0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1300         mee_onoff_h   ("Enhanced resolution (slow)", 0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1301         mee_onoff_h   ("Enhanced res. speed hack",   0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1302         mee_end,
1303 };
1304
1305 static int menu_loop_plugin_gpu_neon(int id, int keys)
1306 {
1307         static int sel = 0;
1308         me_loop(e_menu_plugin_gpu_neon, &sel);
1309         return 0;
1310 }
1311
1312 #endif
1313
1314 static menu_entry e_menu_plugin_gpu_unai[] =
1315 {
1316         mee_onoff     ("Skip every 2nd line",        0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
1317         mee_onoff     ("Abe's Odyssey hack",         0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1318         mee_onoff     ("Disable lighting",           0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1319         mee_onoff     ("Disable blending",           0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1320         mee_end,
1321 };
1322
1323 static int menu_loop_plugin_gpu_unai(int id, int keys)
1324 {
1325         int sel = 0;
1326         me_loop(e_menu_plugin_gpu_unai, &sel);
1327         return 0;
1328 }
1329
1330 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1331 //static const char h_gpu_0[]            = "Needed for Chrono Cross";
1332 static const char h_gpu_1[]            = "Capcom fighting games";
1333 static const char h_gpu_2[]            = "Black screens in Lunar";
1334 static const char h_gpu_3[]            = "Compatibility mode";
1335 static const char h_gpu_6[]            = "Pandemonium 2";
1336 //static const char h_gpu_7[]            = "Skip every second frame";
1337 static const char h_gpu_8[]            = "Needed by Dark Forces";
1338 static const char h_gpu_9[]            = "better g-colors, worse textures";
1339 static const char h_gpu_10[]           = "Toggle busy flags after drawing";
1340
1341 static menu_entry e_menu_plugin_gpu_peops[] =
1342 {
1343         mee_enum      ("Dithering",                  0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1344 //      mee_onoff_h   ("Odd/even bit hack",          0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1345         mee_onoff_h   ("Expand screen width",        0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1346         mee_onoff_h   ("Ignore brightness color",    0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1347         mee_onoff_h   ("Disable coordinate check",   0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1348         mee_onoff_h   ("Lazy screen update",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1349 //      mee_onoff_h   ("Old frame skipping",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1350         mee_onoff_h   ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1351         mee_onoff_h   ("Draw quads with triangles",  0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1352         mee_onoff_h   ("Fake 'gpu busy' states",     0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1353         mee_end,
1354 };
1355
1356 static int menu_loop_plugin_gpu_peops(int id, int keys)
1357 {
1358         static int sel = 0;
1359         me_loop(e_menu_plugin_gpu_peops, &sel);
1360         return 0;
1361 }
1362
1363 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1364         "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1365 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1366
1367 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1368 {
1369         mee_onoff     ("Dithering",                  0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1370         mee_enum      ("Texture Filtering",          0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1371         mee_enum      ("Framebuffer Textures",       0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1372         mee_onoff     ("Mask Detect",                0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1373         mee_onoff     ("Opaque Pass",                0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1374         mee_onoff     ("Advanced Blend",             0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1375         mee_onoff     ("Use Fast Mdec",              0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1376         mee_range     ("Texture RAM size (MB)",      0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1377         mee_onoff     ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1378         mee_label     ("Fixes/hacks:"),
1379         mee_onoff     ("FF7 cursor",                 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1380         mee_onoff     ("Direct FB updates",          0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1381         mee_onoff     ("Black brightness",           0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1382         mee_onoff     ("Swap front detection",       0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1383         mee_onoff     ("Disable coord check",        0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1384         mee_onoff     ("No blue glitches (LoD)",     0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1385         mee_onoff     ("Soft FB access",             0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1386         mee_onoff     ("FF9 rect",                   0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1387         mee_onoff     ("No subtr. blending",         0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1388         mee_onoff     ("Lazy upload (DW7)",          0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1389         mee_onoff     ("Additional uploads",         0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1390         mee_end,
1391 };
1392
1393 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1394 {
1395         static int sel = 0;
1396         me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1397         return 0;
1398 }
1399
1400 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1401 static const char h_spu_volboost[]  = "Large values cause distortion";
1402 static const char h_spu_tempo[]     = "Slows down audio if emu is too slow\n"
1403                                       "This is inaccurate and breaks games";
1404
1405 static menu_entry e_menu_plugin_spu[] =
1406 {
1407         mee_range_h   ("Volume boost",              0, volume_boost, -5, 30, h_spu_volboost),
1408         mee_onoff     ("Reverb",                    0, spu_config.iUseReverb, 1),
1409         mee_enum      ("Interpolation",             0, spu_config.iUseInterpolation, men_spu_interp),
1410         mee_onoff     ("Adjust XA pitch",           0, spu_config.iXAPitch, 1),
1411         mee_onoff_h   ("Adjust tempo",              0, spu_config.iTempo, 1, h_spu_tempo),
1412         mee_end,
1413 };
1414
1415 static int menu_loop_plugin_spu(int id, int keys)
1416 {
1417         static int sel = 0;
1418         me_loop(e_menu_plugin_spu, &sel);
1419         return 0;
1420 }
1421
1422 static const char h_bios[]       = "HLE is simulated BIOS. BIOS selection is saved in\n"
1423                                    "savestates and can't be changed there. Must save\n"
1424                                    "config and reload the game for change to take effect";
1425 static const char h_plugin_gpu[] = 
1426 #ifdef __ARM_NEON__
1427                                    "builtin_gpu is the NEON GPU, very fast and accurate\n"
1428 #endif
1429                                    "gpu_peops is Pete's soft GPU, slow but accurate\n"
1430                                    "gpu_unai is GPU from PCSX4ALL, fast but glitchy\n"
1431                                    "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1432                                    "must save config and reload the game if changed";
1433 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1434                                    "must save config and reload the game if changed";
1435 static const char h_gpu_peops[]  = "Configure P.E.Op.S. SoftGL Driver V1.17";
1436 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1437 static const char h_gpu_unai[]   = "Configure Unai/PCSX4ALL Team GPU plugin";
1438 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1439
1440 static menu_entry e_menu_plugin_options[] =
1441 {
1442         mee_enum_h    ("BIOS",                          0, bios_sel, bioses, h_bios),
1443         mee_enum_h    ("GPU plugin",                    0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1444         mee_enum_h    ("SPU plugin",                    0, spu_plugsel, spu_plugins, h_plugin_spu),
1445 #ifdef __ARM_NEON__
1446         mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1447 #endif
1448         mee_handler_h ("Configure gpu_peops plugin",    menu_loop_plugin_gpu_peops, h_gpu_peops),
1449         mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1450         mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1451         mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1452         mee_end,
1453 };
1454
1455 static menu_entry e_menu_main2[];
1456
1457 static int menu_loop_plugin_options(int id, int keys)
1458 {
1459         static int sel = 0;
1460         me_loop(e_menu_plugin_options, &sel);
1461
1462         // sync BIOS/plugins
1463         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1464         snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1465         snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1466         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1467
1468         return 0;
1469 }
1470
1471 // ------------ adv options menu ------------
1472
1473 static const char h_cfg_psxclk[]  = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1474                                     "(lower value - less work for the emu, may be faster)";
1475 static const char h_cfg_nosmc[]   = "Will cause crashes when loading, break memcards";
1476 static const char h_cfg_gteunn[]  = "May cause graphical glitches";
1477 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1478
1479 static menu_entry e_menu_speed_hacks[] =
1480 {
1481         mee_range_h   ("PSX CPU clock, %%",        0, psx_clock, 1, 500, h_cfg_psxclk),
1482         mee_onoff_h   ("Disable SMC checks",       0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1483         mee_onoff_h   ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1484         mee_onoff_h   ("Disable GTE flags",        0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1485         mee_end,
1486 };
1487
1488 static int menu_loop_speed_hacks(int id, int keys)
1489 {
1490         static int sel = 0;
1491         me_loop(e_menu_speed_hacks, &sel);
1492         return 0;
1493 }
1494
1495 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
1496 static const char h_cfg_spu[]    = "Shows active SPU channels\n"
1497                                    "(green: normal, red: fmod, blue: noise)";
1498 static const char h_cfg_fl[]     = "Frame Limiter keeps the game from running too fast";
1499 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
1500 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
1501                                    "(proper .cue/.bin dump is needed otherwise)";
1502 static const char h_cfg_sio[]    = "You should not need this, breaks games";
1503 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1504 static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1505                                    "(timing hack, breaks other games)";
1506 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix\n"
1507                                    "(timing hack, breaks other games)";
1508 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
1509                                    "Might be useful to overcome some dynarec bugs";
1510 static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
1511                                    "must reload game for any change to take effect";
1512
1513 static menu_entry e_menu_adv_options[] =
1514 {
1515         mee_onoff_h   ("Show CPU load",          0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1516         mee_onoff_h   ("Show SPU channels",      0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1517         mee_onoff_h   ("Disable Frame Limiter",  0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1518         mee_onoff_h   ("Disable XA Decoding",    0, Config.Xa, 1, h_cfg_xa),
1519         mee_onoff_h   ("Disable CD Audio",       0, Config.Cdda, 1, h_cfg_cdda),
1520         //mee_onoff_h   ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1521         mee_onoff_h   ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1522         //mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
1523         mee_onoff_h   ("Rootcounter hack 2",     0, Config.VSyncWA, 1, h_cfg_rcnt2),
1524         mee_onoff_h   ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1525         mee_handler_h ("[Speed hacks]",             menu_loop_speed_hacks, h_cfg_shacks),
1526         mee_end,
1527 };
1528
1529 static int menu_loop_adv_options(int id, int keys)
1530 {
1531         static int sel = 0;
1532         me_loop(e_menu_adv_options, &sel);
1533         return 0;
1534 }
1535
1536 // ------------ options menu ------------
1537
1538 static int mh_restore_defaults(int id, int keys)
1539 {
1540         menu_set_defconfig();
1541         menu_update_msg("defaults restored");
1542         return 1;
1543 }
1544
1545 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
1546 static const char *men_frameskip[]    = { "Auto", "Off", "1", "2", "3", NULL };
1547 /*
1548 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1549 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
1550                                         "loading state or both";
1551 */
1552 static const char h_restore_def[]     = "Switches back to default / recommended\n"
1553                                         "configuration";
1554 static const char h_frameskip[]       = "Warning: frameskip sometimes causes glitches\n";
1555
1556 static menu_entry e_menu_options[] =
1557 {
1558 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1559 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1560         mee_enum_h    ("Frameskip",                0, frameskip, men_frameskip, h_frameskip),
1561         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1562         mee_enum      ("Region",                   0, region, men_region),
1563         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1564         mee_handler_id("[Display]",                MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1565         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1566         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1567         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1568         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1569         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1570         mee_end,
1571 };
1572
1573 static int menu_loop_options(int id, int keys)
1574 {
1575         static int sel = 0;
1576         int i;
1577
1578         i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1579         e_menu_options[i].enabled = cpu_clock_st > 0 ? 1 : 0;
1580         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1581
1582         me_loop(e_menu_options, &sel);
1583
1584         return 0;
1585 }
1586
1587 // ------------ debug menu ------------
1588
1589 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1590 {
1591         int w = min(g_menuscreen_w, 1024);
1592         int h = min(g_menuscreen_h, 512);
1593         u16 *d = g_menuscreen_ptr;
1594         u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1595         char buff[64];
1596         int ty = 1;
1597
1598         gpuf->ulFreezeVersion = 1;
1599         if (GPU_freeze != NULL)
1600                 GPU_freeze(1, gpuf);
1601
1602         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1603                 bgr555_to_rgb565(d, s, w * 2);
1604
1605         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1606         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1607         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1608         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1609         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1610 }
1611
1612 static void debug_menu_loop(void)
1613 {
1614         int inp, df_x = 0, df_y = 0;
1615         GPUFreeze_t *gpuf;
1616
1617         gpuf = malloc(sizeof(*gpuf));
1618         if (gpuf == NULL)
1619                 return;
1620
1621         while (1)
1622         {
1623                 menu_draw_begin(0, 1);
1624                 draw_frame_debug(gpuf, df_x, df_y);
1625                 menu_draw_end();
1626
1627                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1628                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1629                 if      (inp & PBTN_MBACK) break;
1630                 else if (inp & PBTN_UP)    { if (df_y > 0) df_y--; }
1631                 else if (inp & PBTN_DOWN)  { if (df_y < 512 - g_menuscreen_h) df_y++; }
1632                 else if (inp & PBTN_LEFT)  { if (df_x > 0) df_x -= 2; }
1633                 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1634         }
1635
1636         free(gpuf);
1637 }
1638
1639 // --------- memcard manager ---------
1640
1641 static void draw_mc_icon(int dx, int dy, const u16 *s)
1642 {
1643         u16 *d;
1644         int x, y, l, p;
1645         
1646         d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1647
1648         for (y = 0; y < 16; y++, s += 16) {
1649                 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1650                         for (x = 0; x < 16; x++) {
1651                                 p = s[x];
1652                                 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1653                                         | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1654                         }
1655                 }
1656         }
1657 }
1658
1659 static void draw_mc_bg(void)
1660 {
1661         McdBlock *blocks1, *blocks2;
1662         int maxicons = 15;
1663         int i, y, row2;
1664
1665         blocks1 = malloc(15 * sizeof(blocks1[0]));
1666         blocks2 = malloc(15 * sizeof(blocks1[0]));
1667         if (blocks1 == NULL || blocks2 == NULL)
1668                 goto out;
1669
1670         for (i = 0; i < 15; i++) {
1671                 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1672                 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1673         }
1674
1675         menu_draw_begin(1, 1);
1676
1677         memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1678
1679         y = g_menuscreen_h / 2 - 15 * 32 / 2;
1680         if (y < 0) {
1681                 // doesn't fit..
1682                 y = 0;
1683                 maxicons = g_menuscreen_h / 32;
1684         }
1685
1686         row2 = g_menuscreen_w / 2;
1687         for (i = 0; i < maxicons; i++) {
1688                 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1689                 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1690
1691                 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1692                 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1693         }
1694
1695         menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1696
1697         menu_draw_end();
1698 out:
1699         free(blocks1);
1700         free(blocks2);
1701 }
1702
1703 static void handle_memcard_sel(void)
1704 {
1705         strcpy(Config.Mcd1, "none");
1706         if (memcard1_sel != 0)
1707                 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1708         strcpy(Config.Mcd2, "none");
1709         if (memcard2_sel != 0)
1710                 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1711         LoadMcds(Config.Mcd1, Config.Mcd2);
1712         draw_mc_bg();
1713 }
1714
1715 static menu_entry e_memcard_options[] =
1716 {
1717         mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1718         mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1719         mee_end,
1720 };
1721
1722 static int menu_loop_memcards(int id, int keys)
1723 {
1724         static int sel = 0;
1725         char *p;
1726         int i;
1727
1728         memcard1_sel = memcard2_sel = 0;
1729         p = strrchr(Config.Mcd1, '/');
1730         if (p != NULL)
1731                 for (i = 0; memcards[i] != NULL; i++)
1732                         if (strcmp(p + 1, memcards[i]) == 0)
1733                                 { memcard1_sel = i; break; }
1734         p = strrchr(Config.Mcd2, '/');
1735         if (p != NULL)
1736                 for (i = 0; memcards[i] != NULL; i++)
1737                         if (strcmp(p + 1, memcards[i]) == 0)
1738                                 { memcard2_sel = i; break; }
1739
1740         me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1741
1742         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1743
1744         return 0;
1745 }
1746
1747 // ------------ cheats menu ------------
1748
1749 static void draw_cheatlist(int sel)
1750 {
1751         int max_cnt, start, i, pos, active;
1752
1753         max_cnt = g_menuscreen_h / me_sfont_h;
1754         start = max_cnt / 2 - sel;
1755
1756         menu_draw_begin(1, 1);
1757
1758         for (i = 0; i < NumCheats; i++) {
1759                 pos = start + i;
1760                 if (pos < 0) continue;
1761                 if (pos >= max_cnt) break;
1762                 active = Cheats[i].Enabled;
1763                 smalltext_out16(14,                pos * me_sfont_h,
1764                         active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1765                 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1766                         Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1767         }
1768         pos = start + i;
1769         if (pos < max_cnt)
1770                 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1771
1772         text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1773         menu_draw_end();
1774 }
1775
1776 static void menu_loop_cheats(void)
1777 {
1778         static int menu_sel = 0;
1779         int inp;
1780
1781         for (;;)
1782         {
1783                 draw_cheatlist(menu_sel);
1784                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1785                                 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1786                 if (inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1787                 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1788                 if (inp &(PBTN_LEFT|PBTN_L))  { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1789                 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1790                 if (inp & PBTN_MOK) { // action
1791                         if (menu_sel < NumCheats)
1792                                 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1793                         else    break;
1794                 }
1795                 if (inp & PBTN_MBACK)
1796                         break;
1797         }
1798 }
1799
1800 // --------- main menu help ----------
1801
1802 static void menu_bios_warn(void)
1803 {
1804         int inp;
1805         static const char msg[] =
1806                 "You don't seem to have copied any BIOS\n"
1807                 "files to\n"
1808                 MENU_BIOS_PATH "\n\n"
1809
1810                 "While many games work fine with fake\n"
1811                 "(HLE) BIOS, others (like MGS and FF8)\n"
1812                 "require BIOS to work.\n"
1813                 "After copying the file, you'll also need\n"
1814                 "to select it in the emu's menu:\n"
1815                 "options->[BIOS/Plugins]\n\n"
1816                 "The file is usually named SCPH1001.BIN,\n"
1817                 "but other not compressed files can be\n"
1818                 "used too.\n\n"
1819                 "Press %s or %s to continue";
1820         char tmp_msg[sizeof(msg) + 64];
1821
1822         snprintf(tmp_msg, sizeof(tmp_msg), msg,
1823                 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1824         while (1)
1825         {
1826                 draw_menu_message(tmp_msg, NULL);
1827
1828                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
1829                 if (inp & (PBTN_MBACK|PBTN_MOK))
1830                         return;
1831         }
1832 }
1833
1834 // ------------ main menu ------------
1835
1836 static menu_entry e_menu_main[];
1837
1838 static void draw_frame_main(void)
1839 {
1840         struct tm *tmp;
1841         time_t ltime;
1842         int capacity;
1843         char ltime_s[16];
1844         char buff[64];
1845         char *out;
1846
1847         if (CdromId[0] != 0) {
1848                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1849                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1850                          Config.HLE ? "HLE" : "BIOS");
1851                 smalltext_out16(4, 1, buff, 0x105f);
1852         }
1853
1854         if (ready_to_go) {
1855                 capacity = plat_target_bat_capacity_get();
1856                 ltime = time(NULL);
1857                 tmp = localtime(&ltime);
1858                 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1859                 if (capacity >= 0) {
1860                         snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
1861                         out = buff;
1862                 }
1863                 else
1864                         out = ltime_s;
1865                 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
1866         }
1867 }
1868
1869 static void draw_frame_credits(void)
1870 {
1871         smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1872 }
1873
1874 static const char credits_text[] = 
1875         "PCSX-ReARMed\n\n"
1876         "(C) 1999-2003 PCSX Team\n"
1877         "(C) 2005-2009 PCSX-df Team\n"
1878         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1879         "ARM recompiler (C) 2009-2011 Ari64\n"
1880 #ifdef __ARM_NEON__
1881         "ARM NEON GPU (c) 2011-2012 Exophase\n"
1882 #endif
1883         "PEOpS GPU and SPU by Pete Bernert\n"
1884         "  and the P.E.Op.S. team\n"
1885         "PCSX4ALL plugin by PCSX4ALL team\n"
1886         "  Chui, Franxis, Unai\n\n"
1887         "integration, optimization and\n"
1888         "  frontend (C) 2010-2012 notaz\n";
1889
1890 static int reset_game(void)
1891 {
1892         // sanity check
1893         if (bios_sel == 0 && !Config.HLE)
1894                 return -1;
1895
1896         ClosePlugins();
1897         OpenPlugins();
1898         SysReset();
1899         if (CheckCdrom() != -1) {
1900                 LoadCdrom();
1901         }
1902         return 0;
1903 }
1904
1905 static int reload_plugins(const char *cdimg)
1906 {
1907         pl_vout_buf = NULL;
1908
1909         ClosePlugins();
1910
1911         set_cd_image(cdimg);
1912         LoadPlugins();
1913         pcnt_hook_plugins();
1914         NetOpened = 0;
1915         if (OpenPlugins() == -1) {
1916                 menu_update_msg("failed to open plugins");
1917                 return -1;
1918         }
1919         plugin_call_rearmed_cbs();
1920
1921         cdrIsoMultidiskCount = 1;
1922         CdromId[0] = '\0';
1923         CdromLabel[0] = '\0';
1924
1925         return 0;
1926 }
1927
1928 static int run_bios(void)
1929 {
1930         if (bios_sel == 0)
1931                 return -1;
1932
1933         ready_to_go = 0;
1934         if (reload_plugins(NULL) != 0)
1935                 return -1;
1936         SysReset();
1937
1938         ready_to_go = 1;
1939         return 0;
1940 }
1941
1942 static int run_exe(void)
1943 {
1944         const char *exts[] = { "exe", NULL };
1945         const char *fname;
1946
1947         fname = menu_loop_romsel(last_selected_fname,
1948                 sizeof(last_selected_fname), exts, NULL);
1949         if (fname == NULL)
1950                 return -1;
1951
1952         ready_to_go = 0;
1953         if (reload_plugins(NULL) != 0)
1954                 return -1;
1955
1956         SysReset();
1957         if (Load(fname) != 0) {
1958                 menu_update_msg("exe load failed, bad file?");
1959                 printf("meh\n");
1960                 return -1;
1961         }
1962
1963         ready_to_go = 1;
1964         return 0;
1965 }
1966
1967 static int run_cd_image(const char *fname)
1968 {
1969         ready_to_go = 0;
1970         reload_plugins(fname);
1971
1972         // always autodetect, menu_sync_config will override as needed
1973         Config.PsxAuto = 1;
1974
1975         if (CheckCdrom() == -1) {
1976                 // Only check the CD if we are starting the console with a CD
1977                 ClosePlugins();
1978                 menu_update_msg("unsupported/invalid CD image");
1979                 return -1;
1980         }
1981
1982         SysReset();
1983
1984         // Read main executable directly from CDRom and start it
1985         if (LoadCdrom() == -1) {
1986                 ClosePlugins();
1987                 menu_update_msg("failed to load CD image");
1988                 return -1;
1989         }
1990
1991         emu_on_new_cd(1);
1992         ready_to_go = 1;
1993
1994         return 0;
1995 }
1996
1997 static int romsel_run(void)
1998 {
1999         int prev_gpu, prev_spu;
2000         const char *fname;
2001
2002         fname = menu_loop_romsel(last_selected_fname,
2003                         sizeof(last_selected_fname), filter_exts,
2004                         optional_cdimg_filter);
2005         if (fname == NULL)
2006                 return -1;
2007
2008         printf("selected file: %s\n", fname);
2009
2010         new_dynarec_clear_full();
2011
2012         if (run_cd_image(fname) != 0)
2013                 return -1;
2014
2015         prev_gpu = gpu_plugsel;
2016         prev_spu = spu_plugsel;
2017         if (menu_load_config(1) != 0)
2018                 menu_load_config(0);
2019
2020         // check for plugin changes, have to repeat
2021         // loading if game config changed plugins to reload them
2022         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2023                 printf("plugin change detected, reloading plugins..\n");
2024                 if (run_cd_image(fname) != 0)
2025                         return -1;
2026         }
2027
2028         strcpy(last_selected_fname, fname);
2029         menu_do_last_cd_img(0);
2030         return 0;
2031 }
2032
2033 static int swap_cd_image(void)
2034 {
2035         const char *fname;
2036
2037         fname = menu_loop_romsel(last_selected_fname,
2038                         sizeof(last_selected_fname), filter_exts,
2039                         optional_cdimg_filter);
2040         if (fname == NULL)
2041                 return -1;
2042
2043         printf("selected file: %s\n", fname);
2044
2045         CdromId[0] = '\0';
2046         CdromLabel[0] = '\0';
2047
2048         set_cd_image(fname);
2049         if (ReloadCdromPlugin() < 0) {
2050                 menu_update_msg("failed to load cdr plugin");
2051                 return -1;
2052         }
2053         if (CDR_open() < 0) {
2054                 menu_update_msg("failed to open cdr plugin");
2055                 return -1;
2056         }
2057
2058         SetCdOpenCaseTime(time(NULL) + 2);
2059         LidInterrupt();
2060
2061         strcpy(last_selected_fname, fname);
2062         return 0;
2063 }
2064
2065 static int swap_cd_multidisk(void)
2066 {
2067         cdrIsoMultidiskSelect++;
2068         CdromId[0] = '\0';
2069         CdromLabel[0] = '\0';
2070
2071         CDR_close();
2072         if (CDR_open() < 0) {
2073                 menu_update_msg("failed to open cdr plugin");
2074                 return -1;
2075         }
2076
2077         SetCdOpenCaseTime(time(NULL) + 2);
2078         LidInterrupt();
2079
2080         return 0;
2081 }
2082
2083 static void load_pcsx_cht(void)
2084 {
2085         static const char *exts[] = { "cht", NULL };
2086         const char *fname;
2087         char msg[64];
2088
2089         fname = menu_loop_romsel(last_selected_fname,
2090                         sizeof(last_selected_fname), exts, NULL);
2091         if (fname == NULL)
2092                 return;
2093
2094         printf("selected cheat file: %s\n", fname);
2095         LoadCheats(fname);
2096
2097         if (NumCheats == 0 && NumCodes == 0)
2098                 menu_update_msg("failed to load cheats");
2099         else {
2100                 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2101                 menu_update_msg(msg);
2102         }
2103         me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2104 }
2105
2106 static int main_menu_handler(int id, int keys)
2107 {
2108         switch (id)
2109         {
2110         case MA_MAIN_RESUME_GAME:
2111                 if (ready_to_go)
2112                         return 1;
2113                 break;
2114         case MA_MAIN_SAVE_STATE:
2115                 if (ready_to_go)
2116                         return menu_loop_savestate(0);
2117                 break;
2118         case MA_MAIN_LOAD_STATE:
2119                 if (ready_to_go)
2120                         return menu_loop_savestate(1);
2121                 break;
2122         case MA_MAIN_RESET_GAME:
2123                 if (ready_to_go && reset_game() == 0)
2124                         return 1;
2125                 break;
2126         case MA_MAIN_LOAD_ROM:
2127                 if (romsel_run() == 0)
2128                         return 1;
2129                 break;
2130         case MA_MAIN_SWAP_CD:
2131                 if (swap_cd_image() == 0)
2132                         return 1;
2133                 break;
2134         case MA_MAIN_SWAP_CD_MULTI:
2135                 if (swap_cd_multidisk() == 0)
2136                         return 1;
2137                 break;
2138         case MA_MAIN_RUN_BIOS:
2139                 if (run_bios() == 0)
2140                         return 1;
2141                 break;
2142         case MA_MAIN_RUN_EXE:
2143                 if (run_exe() == 0)
2144                         return 1;
2145                 break;
2146         case MA_MAIN_CHEATS:
2147                 menu_loop_cheats();
2148                 break;
2149         case MA_MAIN_LOAD_CHEATS:
2150                 load_pcsx_cht();
2151                 break;
2152         case MA_MAIN_CREDITS:
2153                 draw_menu_message(credits_text, draw_frame_credits);
2154                 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2155                 break;
2156         case MA_MAIN_EXIT:
2157                 emu_core_ask_exit();
2158                 return 1;
2159         default:
2160                 lprintf("%s: something unknown selected\n", __FUNCTION__);
2161                 break;
2162         }
2163
2164         return 0;
2165 }
2166
2167 static menu_entry e_menu_main2[] =
2168 {
2169         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
2170         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2171         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
2172         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
2173         mee_handler   ("Memcard manager",    menu_loop_memcards),
2174         mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS,   main_menu_handler),
2175         mee_end,
2176 };
2177
2178 static int main_menu2_handler(int id, int keys)
2179 {
2180         static int sel = 0;
2181
2182         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
2183         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2184         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2185         me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2186
2187         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2188 }
2189
2190 static const char h_extra[] = "Change CD, manage memcards..\n";
2191
2192 static menu_entry e_menu_main[] =
2193 {
2194         mee_label     (""),
2195         mee_label     (""),
2196         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
2197         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
2198         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
2199         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
2200         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
2201         mee_handler   ("Options",            menu_loop_options),
2202         mee_handler   ("Controls",           menu_loop_keyconfig),
2203         mee_handler_id("Cheats",             MA_MAIN_CHEATS,      main_menu_handler),
2204         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
2205         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
2206         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
2207         mee_end,
2208 };
2209
2210 // ----------------------------
2211
2212 static void menu_leave_emu(void);
2213
2214 void menu_loop(void)
2215 {
2216         static int warned_about_bios = 0;
2217         static int sel = 0;
2218
2219         menu_leave_emu();
2220
2221         if (config_save_counter == 0) {
2222                 // assume first run
2223                 if (bioses[1] != NULL) {
2224                         // autoselect BIOS to make user's life easier
2225                         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2226                         bios_sel = 1;
2227                 }
2228                 else if (!warned_about_bios) {
2229                         menu_bios_warn();
2230                         warned_about_bios = 1;
2231                 }
2232         }
2233
2234         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2235         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
2236         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
2237         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
2238         me_enable(e_menu_main, MA_MAIN_CHEATS,      ready_to_go && NumCheats);
2239
2240         in_set_config_int(0, IN_CFG_BLOCKING, 1);
2241
2242         do {
2243                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2244         } while (!ready_to_go && !g_emu_want_quit);
2245
2246         /* wait until menu, ok, back is released */
2247         while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2248                 ;
2249
2250         in_set_config_int(0, IN_CFG_BLOCKING, 0);
2251
2252         menu_prepare_emu();
2253 }
2254
2255 static int qsort_strcmp(const void *p1, const void *p2)
2256 {
2257         char * const *s1 = (char * const *)p1;
2258         char * const *s2 = (char * const *)p2;
2259         return strcasecmp(*s1, *s2);
2260 }
2261
2262 static void scan_bios_plugins(void)
2263 {
2264         char fname[MAXPATHLEN];
2265         struct dirent *ent;
2266         int bios_i, gpu_i, spu_i, mc_i;
2267         char *p;
2268         DIR *dir;
2269
2270         bioses[0] = "HLE";
2271         gpu_plugins[0] = "builtin_gpu";
2272         spu_plugins[0] = "builtin_spu";
2273         memcards[0] = "(none)";
2274         bios_i = gpu_i = spu_i = mc_i = 1;
2275
2276         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2277         dir = opendir(fname);
2278         if (dir == NULL) {
2279                 perror("scan_bios_plugins bios opendir");
2280                 goto do_plugins;
2281         }
2282
2283         while (1) {
2284                 struct stat st;
2285
2286                 errno = 0;
2287                 ent = readdir(dir);
2288                 if (ent == NULL) {
2289                         if (errno != 0)
2290                                 perror("readdir");
2291                         break;
2292                 }
2293
2294                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2295                         continue;
2296
2297                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2298                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2299                         printf("bad BIOS file: %s\n", ent->d_name);
2300                         continue;
2301                 }
2302
2303                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2304                         bioses[bios_i++] = strdup(ent->d_name);
2305                         continue;
2306                 }
2307
2308                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2309         }
2310
2311         closedir(dir);
2312
2313 do_plugins:
2314         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2315         dir = opendir(fname);
2316         if (dir == NULL) {
2317                 perror("scan_bios_plugins plugins opendir");
2318                 goto do_memcards;
2319         }
2320
2321         while (1) {
2322                 void *h, *tmp;
2323
2324                 errno = 0;
2325                 ent = readdir(dir);
2326                 if (ent == NULL) {
2327                         if (errno != 0)
2328                                 perror("readdir");
2329                         break;
2330                 }
2331                 p = strstr(ent->d_name, ".so");
2332                 if (p == NULL)
2333                         continue;
2334
2335                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2336                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2337                 if (h == NULL) {
2338                         fprintf(stderr, "%s\n", dlerror());
2339                         continue;
2340                 }
2341
2342                 // now what do we have here?
2343                 tmp = dlsym(h, "GPUinit");
2344                 if (tmp) {
2345                         dlclose(h);
2346                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2347                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2348                         continue;
2349                 }
2350
2351                 tmp = dlsym(h, "SPUinit");
2352                 if (tmp) {
2353                         dlclose(h);
2354                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2355                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2356                         continue;
2357                 }
2358
2359                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2360                 dlclose(h);
2361         }
2362
2363         closedir(dir);
2364
2365 do_memcards:
2366         dir = opendir("." MEMCARD_DIR);
2367         if (dir == NULL) {
2368                 perror("scan_bios_plugins memcards opendir");
2369                 return;
2370         }
2371
2372         while (1) {
2373                 struct stat st;
2374
2375                 errno = 0;
2376                 ent = readdir(dir);
2377                 if (ent == NULL) {
2378                         if (errno != 0)
2379                                 perror("readdir");
2380                         break;
2381                 }
2382
2383                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2384                         continue;
2385
2386                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2387                 if (stat(fname, &st) != 0) {
2388                         printf("bad memcard file: %s\n", ent->d_name);
2389                         continue;
2390                 }
2391
2392                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2393                         memcards[mc_i++] = strdup(ent->d_name);
2394                         continue;
2395                 }
2396
2397                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2398         }
2399
2400         if (mc_i > 2)
2401                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2402
2403         closedir(dir);
2404 }
2405
2406 void menu_init(void)
2407 {
2408         char buff[MAXPATHLEN];
2409         int i;
2410
2411         cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2412
2413         scan_bios_plugins();
2414         menu_init_base();
2415
2416         menu_set_defconfig();
2417         menu_load_config(0);
2418         menu_do_last_cd_img(1);
2419         last_vout_w = 320;
2420         last_vout_h = 240;
2421         last_vout_bpp = 16;
2422
2423         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2424         g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2425         if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2426                 fprintf(stderr, "OOM\n");
2427                 exit(1);
2428         }
2429
2430         emu_make_path(buff, "skin/background.png", sizeof(buff));
2431         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2432
2433         i = plat_target.cpu_clock_set != NULL
2434                 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2435         me_enable(e_menu_gfx_options, MA_OPT_CPU_CLOCKS, i);
2436
2437         i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2438         e_menu_gfx_options[i].data = plat_target.vout_methods;
2439         me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2440                 plat_target.vout_methods != NULL);
2441
2442         i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2443         e_menu_gfx_options[i].data = plat_target.hwfilters;
2444         me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2445                 plat_target.hwfilters != NULL);
2446
2447         me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2448                 plat_target.gamma_set != NULL);
2449
2450 #ifndef __ARM_ARCH_7A__
2451         me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2452 #endif
2453         me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2454         me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2455         me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2456         me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2457         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2458         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2459         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2460 }
2461
2462 void menu_notify_mode_change(int w, int h, int bpp)
2463 {
2464         last_vout_w = w;
2465         last_vout_h = h;
2466         last_vout_bpp = bpp;
2467 }
2468
2469 static void menu_leave_emu(void)
2470 {
2471         if (GPU_close != NULL) {
2472                 int ret = GPU_close();
2473                 if (ret)
2474                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2475         }
2476
2477         plat_video_menu_enter(ready_to_go);
2478
2479         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2480         if (pl_vout_buf != NULL && ready_to_go) {
2481                 int x = max(0, g_menuscreen_w - last_vout_w);
2482                 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2483                 int w = min(g_menuscreen_w, last_vout_w);
2484                 int h = min(g_menuscreen_h, last_vout_h);
2485                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2486                 char *s = pl_vout_buf;
2487
2488                 if (last_vout_bpp == 16) {
2489                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2490                                 menu_darken_bg(d, s, w, 0);
2491                 }
2492                 else {
2493                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2494                                 rgb888_to_rgb565(d, s, w * 3);
2495                                 menu_darken_bg(d, d, w, 0);
2496                         }
2497                 }
2498         }
2499
2500         if (ready_to_go)
2501                 cpu_clock = plat_target_cpu_clock_get();
2502 }
2503
2504 void menu_prepare_emu(void)
2505 {
2506         R3000Acpu *prev_cpu = psxCpu;
2507
2508         plat_video_menu_leave();
2509
2510         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2511         if (psxCpu != prev_cpu) {
2512                 prev_cpu->Shutdown();
2513                 psxCpu->Init();
2514                 // note that this does not really reset, just clears drc caches
2515                 psxCpu->Reset();
2516         }
2517
2518         // core doesn't care about Config.Cdda changes,
2519         // so handle them manually here
2520         if (Config.Cdda)
2521                 CDR_stop();
2522
2523         menu_sync_config();
2524         if (cpu_clock > 0)
2525                 plat_target_cpu_clock_set(cpu_clock);
2526
2527         // push config to GPU plugin
2528         plugin_call_rearmed_cbs();
2529
2530         if (GPU_open != NULL) {
2531                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2532                 if (ret)
2533                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2534         }
2535
2536         dfinput_activate();
2537 }
2538
2539 void menu_update_msg(const char *msg)
2540 {
2541         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2542         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2543
2544         menu_error_time = plat_get_ticks_ms();
2545         lprintf("msg: %s\n", menu_error_msg);
2546 }
2547
2548 void menu_finish(void)
2549 {
2550         if (cpu_clock_st > 0)
2551                 plat_target_cpu_clock_set(cpu_clock_st);
2552 }