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