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