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