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