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