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