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