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