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