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