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