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