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