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