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