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