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