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