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