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