standalone: allow scaler to cut off the letterbox
[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         void *saved_layer = NULL;
1289         size_t saved_layer_size = 0;
1290         int was_layer_clipped = 0;
1291         unsigned int inp;
1292
1293         if (!pl_vout_buf)
1294                 return -1;
1295
1296         g_scaler = SCALE_CUSTOM;
1297         saved_layer_size = last_vout_w * last_vout_h * last_vout_bpp / 8;
1298         saved_layer = malloc(saved_layer_size);
1299         if (saved_layer)
1300                 memcpy(saved_layer, pl_vout_buf, saved_layer_size);
1301
1302         plat_gvideo_open(Config.PsxType);
1303
1304         menu_draw_begin(0, 1);
1305         memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1306         menu_draw_end();
1307
1308         for (;;)
1309         {
1310                 if (saved_layer && last_vout_bpp == 16) {
1311                         int top_x = max(0, -g_layer_x * last_vout_h / 800) + 1;
1312                         int top_y = max(0, -g_layer_y * last_vout_h / 480) + 1;
1313                         char text[128];
1314                         memcpy(pl_vout_buf, saved_layer, saved_layer_size);
1315                         snprintf(text, sizeof(text), "%d,%d %dx%d",
1316                                 g_layer_x, g_layer_y, g_layer_w, g_layer_h);
1317                         basic_text_out16_nf(pl_vout_buf, last_vout_w,
1318                                 top_x, top_y, text);
1319                         basic_text_out16_nf(pl_vout_buf, last_vout_w, 2,
1320                                 last_vout_h - 20, "d-pad: resize, R+d-pad: move");
1321                         pl_vout_buf = plat_gvideo_flip();
1322                 }
1323
1324                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT
1325                                 |PBTN_R|PBTN_MOK|PBTN_MBACK, NULL, 40);
1326                 if (inp & PBTN_UP)    g_layer_y--;
1327                 if (inp & PBTN_DOWN)  g_layer_y++;
1328                 if (inp & PBTN_LEFT)  g_layer_x--;
1329                 if (inp & PBTN_RIGHT) g_layer_x++;
1330                 if (!(inp & PBTN_R)) {
1331                         if (inp & PBTN_UP)    g_layer_h += 2;
1332                         if (inp & PBTN_DOWN)  g_layer_h -= 2;
1333                         if (inp & PBTN_LEFT)  g_layer_w += 2;
1334                         if (inp & PBTN_RIGHT) g_layer_w -= 2;
1335                 }
1336                 if (inp & (PBTN_MOK|PBTN_MBACK))
1337                         break;
1338
1339                 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1340                         int layer_clipped = 0;
1341                         g_layer_x = max(-320, min(g_layer_x, 640));
1342                         g_layer_y = max(-240, min(g_layer_y, 400));
1343                         g_layer_w = max(160, g_layer_w);
1344                         g_layer_h = max( 60, g_layer_h);
1345                         if (g_layer_x < 0 || g_layer_x + g_layer_w > 800)
1346                                 layer_clipped = 1;
1347                         if (g_layer_w > 800+400)
1348                                 g_layer_w = 800+400;
1349                         if (g_layer_y < 0 || g_layer_y + g_layer_h > 480)
1350                                 layer_clipped = 1;
1351                         if (g_layer_h > 480+360)
1352                                 g_layer_h = 480+360;
1353                         // resize the layer
1354                         plat_gvideo_open(Config.PsxType);
1355                         if (layer_clipped || was_layer_clipped)
1356                                 pl_vout_buf = plat_gvideo_set_mode(&last_vout_w,
1357                                         &last_vout_h, &last_vout_bpp);
1358                         was_layer_clipped = layer_clipped;
1359                 }
1360         }
1361
1362         plat_gvideo_close();
1363         free(saved_layer);
1364
1365         return 0;
1366 }
1367
1368 static menu_entry e_menu_gfx_options[] =
1369 {
1370         mee_enum      ("Screen centering",         MA_OPT_CENTERING, pl_rearmed_cbs.screen_centering_type, men_centering),
1371         mee_enum_h    ("Scaler",                   MA_OPT_VARSCALER, g_scaler, men_scaler, h_scaler),
1372         mee_enum      ("Video output mode",        MA_OPT_VOUT_MODE, plat_target.vout_method, men_dummy),
1373         mee_onoff     ("Software Scaling",         MA_OPT_SCALER2, soft_scaling, 1),
1374         mee_enum      ("Hardware Filter",          MA_OPT_HWFILTER, plat_target.hwfilter, men_dummy),
1375         mee_enum_h    ("Software Filter",          MA_OPT_SWFILTER, soft_filter, men_soft_filter, h_soft_filter),
1376 #ifdef __ARM_NEON__
1377         mee_enum      ("Scanlines",                MA_OPT_SCANLINES, scanlines, men_scanlines),
1378         mee_range_h   ("Scanline brightness",      MA_OPT_SCANLINE_LEVEL, scanline_level, 0, 100, h_scanline_l),
1379 #endif
1380         mee_range_h   ("Gamma adjustment",         MA_OPT_GAMMA, g_gamma, 1, 200, h_gamma),
1381 //      mee_onoff     ("Vsync",                    0, vsync, 1),
1382         mee_cust_h    ("Setup custom scaler",      MA_OPT_VARSCALER_C, menu_loop_cscaler, NULL, h_cscaler),
1383         mee_end,
1384 };
1385
1386 static int menu_loop_gfx_options(int id, int keys)
1387 {
1388         static int sel = 0;
1389
1390         me_loop(e_menu_gfx_options, &sel);
1391
1392         return 0;
1393 }
1394
1395 // ------------ bios/plugins ------------
1396
1397 #ifdef BUILTIN_GPU_NEON
1398
1399 static const char h_gpu_neon[] =
1400         "Configure built-in NEON GPU plugin";
1401 static const char h_gpu_neon_enhanced[] =
1402         "Renders in double resolution at the cost of lower performance\n"
1403         "(not available for high resolution games)";
1404 static const char h_gpu_neon_enhanced_hack[] =
1405         "Speed hack for above option (glitches some games)";
1406 static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1407
1408 static menu_entry e_menu_plugin_gpu_neon[] =
1409 {
1410         mee_enum      ("Enable interlace mode",      0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1411         mee_onoff_h   ("Enhanced resolution",        0, pl_rearmed_cbs.gpu_neon.enhancement_enable, 1, h_gpu_neon_enhanced),
1412         mee_onoff_h   ("Enhanced res. speed hack",   0, pl_rearmed_cbs.gpu_neon.enhancement_no_main, 1, h_gpu_neon_enhanced_hack),
1413         mee_end,
1414 };
1415
1416 static int menu_loop_plugin_gpu_neon(int id, int keys)
1417 {
1418         static int sel = 0;
1419         me_loop(e_menu_plugin_gpu_neon, &sel);
1420         return 0;
1421 }
1422
1423 #endif
1424
1425 static menu_entry e_menu_plugin_gpu_unai_old[] =
1426 {
1427         mee_onoff     ("Skip every 2nd line",        0, pl_rearmed_cbs.gpu_unai_old.lineskip, 1),
1428         mee_onoff     ("Abe's Odyssey hack",         0, pl_rearmed_cbs.gpu_unai_old.abe_hack, 1),
1429         mee_onoff     ("Disable lighting",           0, pl_rearmed_cbs.gpu_unai_old.no_light, 1),
1430         mee_onoff     ("Disable blending",           0, pl_rearmed_cbs.gpu_unai_old.no_blend, 1),
1431         mee_end,
1432 };
1433
1434 static int menu_loop_plugin_gpu_unai_old(int id, int keys)
1435 {
1436         int sel = 0;
1437         me_loop(e_menu_plugin_gpu_unai_old, &sel);
1438         return 0;
1439 }
1440
1441 static menu_entry e_menu_plugin_gpu_unai[] =
1442 {
1443         mee_onoff     ("Interlace",                  0, pl_rearmed_cbs.gpu_unai.ilace_force, 1),
1444         mee_onoff     ("Dithering",                  0, pl_rearmed_cbs.gpu_unai.dithering, 1),
1445         mee_onoff     ("Lighting",                   0, pl_rearmed_cbs.gpu_unai.lighting, 1),
1446         mee_onoff     ("Fast lighting",              0, pl_rearmed_cbs.gpu_unai.fast_lighting, 1),
1447         mee_onoff     ("Blending",                   0, pl_rearmed_cbs.gpu_unai.blending, 1),
1448         mee_onoff     ("Pixel skip",                 0, pl_rearmed_cbs.gpu_unai.pixel_skip, 1),
1449         mee_end,
1450 };
1451
1452 static int menu_loop_plugin_gpu_unai(int id, int keys)
1453 {
1454         int sel = 0;
1455         me_loop(e_menu_plugin_gpu_unai, &sel);
1456         return 0;
1457 }
1458
1459
1460 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1461 //static const char h_gpu_0[]            = "Needed for Chrono Cross";
1462 static const char h_gpu_1[]            = "Capcom fighting games";
1463 static const char h_gpu_2[]            = "Black screens in Lunar";
1464 static const char h_gpu_3[]            = "Compatibility mode";
1465 static const char h_gpu_6[]            = "Pandemonium 2";
1466 //static const char h_gpu_7[]            = "Skip every second frame";
1467 static const char h_gpu_8[]            = "Needed by Dark Forces";
1468 static const char h_gpu_9[]            = "better g-colors, worse textures";
1469 static const char h_gpu_10[]           = "Toggle busy flags after drawing";
1470
1471 static menu_entry e_menu_plugin_gpu_peops[] =
1472 {
1473         mee_enum      ("Dithering",                  0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1474 //      mee_onoff_h   ("Odd/even bit hack",          0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1475         mee_onoff_h   ("Expand screen width",        0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1476         mee_onoff_h   ("Ignore brightness color",    0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1477         mee_onoff_h   ("Disable coordinate check",   0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1478         mee_onoff_h   ("Lazy screen update",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1479 //      mee_onoff_h   ("Old frame skipping",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1480         mee_onoff_h   ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1481         mee_onoff_h   ("Draw quads with triangles",  0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1482         mee_onoff_h   ("Fake 'gpu busy' states",     0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1483         mee_end,
1484 };
1485
1486 static int menu_loop_plugin_gpu_peops(int id, int keys)
1487 {
1488         static int sel = 0;
1489         me_loop(e_menu_plugin_gpu_peops, &sel);
1490         return 0;
1491 }
1492
1493 static const char *men_peopsgl_texfilter[] = { "None", "Standard", "Extended",
1494         "Standard-sprites", "Extended-sprites", "Standard+sprites", "Extended+sprites", NULL };
1495 static const char *men_peopsgl_fbtex[] = { "Emulated VRam", "Black", "Card", "Card+soft" };
1496
1497 static menu_entry e_menu_plugin_gpu_peopsgl[] =
1498 {
1499         mee_onoff     ("Dithering",                  0, pl_rearmed_cbs.gpu_peopsgl.bDrawDither, 1),
1500         mee_enum      ("Texture Filtering",          0, pl_rearmed_cbs.gpu_peopsgl.iFilterType, men_peopsgl_texfilter),
1501         mee_enum      ("Framebuffer Textures",       0, pl_rearmed_cbs.gpu_peopsgl.iFrameTexType, men_peopsgl_fbtex),
1502         mee_onoff     ("Mask Detect",                0, pl_rearmed_cbs.gpu_peopsgl.iUseMask, 1),
1503         mee_onoff     ("Opaque Pass",                0, pl_rearmed_cbs.gpu_peopsgl.bOpaquePass, 1),
1504         mee_onoff     ("Advanced Blend",             0, pl_rearmed_cbs.gpu_peopsgl.bAdvancedBlend, 1),
1505         mee_onoff     ("Use Fast Mdec",              0, pl_rearmed_cbs.gpu_peopsgl.bUseFastMdec, 1),
1506         mee_range     ("Texture RAM size (MB)",      0, pl_rearmed_cbs.gpu_peopsgl.iVRamSize, 4, 128),
1507         mee_onoff     ("Texture garbage collection", 0, pl_rearmed_cbs.gpu_peopsgl.iTexGarbageCollection, 1),
1508         mee_label     ("Fixes/hacks:"),
1509         mee_onoff     ("FF7 cursor",                 0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<0),
1510         mee_onoff     ("Direct FB updates",          0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<1),
1511         mee_onoff     ("Black brightness",           0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<2),
1512         mee_onoff     ("Swap front detection",       0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<3),
1513         mee_onoff     ("Disable coord check",        0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<4),
1514         mee_onoff     ("No blue glitches (LoD)",     0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<5),
1515         mee_onoff     ("Soft FB access",             0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<6),
1516         mee_onoff     ("FF9 rect",                   0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<9),
1517         mee_onoff     ("No subtr. blending",         0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<10),
1518         mee_onoff     ("Lazy upload (DW7)",          0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<11),
1519         mee_onoff     ("Additional uploads",         0, pl_rearmed_cbs.gpu_peopsgl.dwActFixes, 1<<15),
1520         mee_end,
1521 };
1522
1523 static int menu_loop_plugin_gpu_peopsgl(int id, int keys)
1524 {
1525         static int sel = 0;
1526         me_loop(e_menu_plugin_gpu_peopsgl, &sel);
1527         return 0;
1528 }
1529
1530 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1531 static const char h_spu_volboost[]  = "Large values cause distortion";
1532 static const char h_spu_tempo[]     = "Slows down audio if emu is too slow\n"
1533                                       "This is inaccurate and breaks games";
1534
1535 static menu_entry e_menu_plugin_spu[] =
1536 {
1537         mee_range_h   ("Volume boost",              0, volume_boost, -5, 30, h_spu_volboost),
1538         mee_onoff     ("Reverb",                    0, spu_config.iUseReverb, 1),
1539         mee_enum      ("Interpolation",             0, spu_config.iUseInterpolation, men_spu_interp),
1540         //mee_onoff     ("Adjust XA pitch",           0, spu_config.iXAPitch, 1),
1541         mee_onoff_h   ("Adjust tempo",              0, spu_config.iTempo, 1, h_spu_tempo),
1542         mee_end,
1543 };
1544
1545 static int menu_loop_plugin_spu(int id, int keys)
1546 {
1547         static int sel = 0;
1548         me_loop(e_menu_plugin_spu, &sel);
1549         return 0;
1550 }
1551
1552 static const char h_bios[]       = "HLE is simulated BIOS. BIOS selection is saved in\n"
1553                                    "savestates and can't be changed there. Must save\n"
1554                                    "config and reload the game for change to take effect";
1555 static const char h_plugin_gpu[] = 
1556 #ifdef BUILTIN_GPU_NEON
1557                                    "builtin_gpu is the NEON GPU, very fast and accurate\n"
1558 #endif
1559                                    "gpu_peops is Pete's soft GPU, slow but accurate\n"
1560                                    "gpu_unai_old is from old PCSX4ALL, fast but glitchy\n"
1561                                    "gpu_unai is newer, more accurate but slower\n"
1562                                    "gpu_gles Pete's hw GPU, uses 3D chip but is glitchy\n"
1563                                    "must save config and reload the game if changed";
1564 static const char h_plugin_spu[] = "spunull effectively disables sound\n"
1565                                    "must save config and reload the game if changed";
1566 static const char h_gpu_peops[]  = "Configure P.E.Op.S. SoftGL Driver V1.17";
1567 static const char h_gpu_peopsgl[]= "Configure P.E.Op.S. MesaGL Driver V1.78";
1568 static const char h_gpu_unai_old[] = "Configure Unai/PCSX4ALL Team GPU plugin (old)";
1569 static const char h_gpu_unai[]   = "Configure Unai/PCSX4ALL Team plugin (new)";
1570 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1571
1572 static menu_entry e_menu_plugin_options[] =
1573 {
1574         mee_enum_h    ("BIOS",                          0, bios_sel, bioses, h_bios),
1575         mee_enum_h    ("GPU plugin",                    0, gpu_plugsel, gpu_plugins, h_plugin_gpu),
1576         mee_enum_h    ("SPU plugin",                    0, spu_plugsel, spu_plugins, h_plugin_spu),
1577 #ifdef BUILTIN_GPU_NEON
1578         mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1579 #endif
1580         mee_handler_h ("Configure gpu_peops plugin",    menu_loop_plugin_gpu_peops, h_gpu_peops),
1581         mee_handler_h ("Configure gpu_unai_old GPU plugin", menu_loop_plugin_gpu_unai_old, h_gpu_unai_old),
1582         mee_handler_h ("Configure gpu_unai GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1583         mee_handler_h ("Configure gpu_gles GPU plugin", menu_loop_plugin_gpu_peopsgl, h_gpu_peopsgl),
1584         mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1585         mee_end,
1586 };
1587
1588 static menu_entry e_menu_main2[];
1589
1590 static int menu_loop_plugin_options(int id, int keys)
1591 {
1592         static int sel = 0;
1593         me_loop(e_menu_plugin_options, &sel);
1594
1595         // sync BIOS/plugins
1596         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1597         snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1598         snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1599         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1600
1601         return 0;
1602 }
1603
1604 // ------------ adv options menu ------------
1605
1606 #ifndef DRC_DISABLE
1607 static const char h_cfg_noch[]    = "Disables game-specific compatibility hacks";
1608 static const char h_cfg_nosmc[]   = "Will cause crashes when loading, break memcards";
1609 static const char h_cfg_gteunn[]  = "May cause graphical glitches";
1610 static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1611 #endif
1612 static const char h_cfg_stalls[]  = "Will cause some games to run too fast";
1613
1614 static menu_entry e_menu_speed_hacks[] =
1615 {
1616 #ifndef DRC_DISABLE
1617         mee_onoff_h   ("Disable compat hacks",     0, new_dynarec_hacks, NDHACK_NO_COMPAT_HACKS, h_cfg_noch),
1618         mee_onoff_h   ("Disable SMC checks",       0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1619         mee_onoff_h   ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1620         mee_onoff_h   ("Disable GTE flags",        0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1621 #endif
1622         mee_onoff_h   ("Disable CPU/GTE stalls",   0, menu_iopts[0], 1, h_cfg_stalls),
1623         mee_end,
1624 };
1625
1626 static int menu_loop_speed_hacks(int id, int keys)
1627 {
1628         static int sel = 0;
1629         menu_iopts[0] = Config.DisableStalls;
1630         me_loop(e_menu_speed_hacks, &sel);
1631         Config.DisableStalls = menu_iopts[0];
1632         return 0;
1633 }
1634
1635 static const char *men_gpul[]    = { "Auto", "Off", "On", NULL };
1636
1637 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
1638 static const char h_cfg_spu[]    = "Shows active SPU channels\n"
1639                                    "(green: normal, red: fmod, blue: noise)";
1640 static const char h_cfg_fl[]     = "Frame Limiter keeps the game from running too fast";
1641 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
1642 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
1643                                    "(proper .cue/.bin dump is needed otherwise)";
1644 #ifndef DRC_DISABLE
1645 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
1646                                    "Might be useful to overcome some dynarec bugs";
1647 #endif
1648 static const char h_cfg_shacks[] = "Breaks games but may give better performance";
1649 static const char h_cfg_icache[] = "Support F1 games (only when dynarec is off)";
1650 static const char h_cfg_exc[]    = "Emulate some PSX's debug hw like breakpoints\n"
1651                                    "and exceptions (slow, interpreter only, keep off)";
1652 static const char h_cfg_gpul[]   = "Try enabling this if the game misses some graphics\n"
1653                                    "causes a performance hit";
1654 static const char h_cfg_psxclk[]  = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1655                                     "(adjust this if the game is too slow/too fast/hangs)";
1656
1657 enum { AMO_XA, AMO_CDDA, AMO_IC, AMO_BP, AMO_CPU, AMO_GPUL };
1658
1659 static menu_entry e_menu_adv_options[] =
1660 {
1661         mee_onoff_h   ("Show CPU load",          0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1662         mee_onoff_h   ("Show SPU channels",      0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1663         mee_onoff_h   ("Disable Frame Limiter",  0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1664         mee_onoff_h   ("Disable XA Decoding",    0, menu_iopts[AMO_XA],   1, h_cfg_xa),
1665         mee_onoff_h   ("Disable CD Audio",       0, menu_iopts[AMO_CDDA], 1, h_cfg_cdda),
1666         mee_onoff_h   ("ICache emulation",       0, menu_iopts[AMO_IC],   1, h_cfg_icache),
1667         mee_onoff_h   ("BP exception emulation", 0, menu_iopts[AMO_BP],   1, h_cfg_exc),
1668         mee_enum_h    ("GPU l-list slow walking",0, menu_iopts[AMO_GPUL], men_gpul, h_cfg_gpul),
1669 #if !defined(DRC_DISABLE) || defined(LIGHTREC)
1670         mee_onoff_h   ("Disable dynarec (slow!)",0, menu_iopts[AMO_CPU],  1, h_cfg_nodrc),
1671 #endif
1672         mee_range_h   ("PSX CPU clock, %",       0, psx_clock, 1, 500, h_cfg_psxclk),
1673         mee_handler_h ("[Speed hacks]",             menu_loop_speed_hacks, h_cfg_shacks),
1674         mee_end,
1675 };
1676
1677 static int menu_loop_adv_options(int id, int keys)
1678 {
1679         static int sel = 0;
1680         static struct {
1681                 boolean *opt;
1682                 int *mopt;
1683         } opts[] = {
1684                 { &Config.Xa,      &menu_iopts[AMO_XA] },
1685                 { &Config.Cdda,    &menu_iopts[AMO_CDDA] },
1686                 { &Config.icache_emulation, &menu_iopts[AMO_IC] },
1687                 { &Config.PreciseExceptions, &menu_iopts[AMO_BP] },
1688                 { &Config.Cpu,     &menu_iopts[AMO_CPU] },
1689         };
1690         int i;
1691         for (i = 0; i < ARRAY_SIZE(opts); i++)
1692                 *opts[i].mopt = *opts[i].opt;
1693         menu_iopts[AMO_GPUL] = Config.GpuListWalking + 1;
1694
1695         me_loop(e_menu_adv_options, &sel);
1696
1697         for (i = 0; i < ARRAY_SIZE(opts); i++)
1698                 *opts[i].opt = *opts[i].mopt;
1699         Config.GpuListWalking = menu_iopts[AMO_GPUL] - 1;
1700
1701         return 0;
1702 }
1703
1704 // ------------ options menu ------------
1705
1706 static int mh_restore_defaults(int id, int keys)
1707 {
1708         menu_set_defconfig();
1709         menu_update_msg("defaults restored");
1710         return 1;
1711 }
1712
1713 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
1714 static const char *men_frameskip[]    = { "Auto", "Off", "1", "2", "3", NULL };
1715 /*
1716 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1717 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
1718                                         "loading state or both";
1719 */
1720 static const char h_restore_def[]     = "Switches back to default / recommended\n"
1721                                         "configuration";
1722 static const char h_frameskip[]       = "Warning: frameskip sometimes causes glitches\n";
1723
1724 static menu_entry e_menu_options[] =
1725 {
1726 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1727 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1728         mee_enum_h    ("Frameskip",                0, frameskip, men_frameskip, h_frameskip),
1729         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1730         mee_enum      ("Region",                   0, region, men_region),
1731         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1732 #ifdef C64X_DSP
1733         mee_onoff     ("Use C64x DSP for sound",   MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1734 #else
1735         mee_onoff     ("Threaded SPU",             MA_OPT_SPU_THREAD, spu_config.iUseThread, 1),
1736 #endif
1737         mee_handler_id("[Display]",                MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1738         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1739         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1740         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1741         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1742         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1743         mee_end,
1744 };
1745
1746 static int menu_loop_options(int id, int keys)
1747 {
1748         static int sel = 0;
1749
1750         me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, cpu_clock_st > 0);
1751         me_enable(e_menu_options, MA_OPT_SPU_THREAD, spu_config.iThreadAvail);
1752         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1753
1754         me_loop(e_menu_options, &sel);
1755
1756         return 0;
1757 }
1758
1759 // ------------ debug menu ------------
1760
1761 static void draw_frame_debug(GPUFreeze_t *gpuf, int x, int y)
1762 {
1763         int w = min(g_menuscreen_w, 1024);
1764         int h = min(g_menuscreen_h, 512);
1765         u16 *d = g_menuscreen_ptr;
1766         u16 *s = (u16 *)gpuf->psxVRam + y * 1024 + x;
1767         char buff[64];
1768         int ty = 1;
1769
1770         gpuf->ulFreezeVersion = 1;
1771         if (GPU_freeze != NULL)
1772                 GPU_freeze(1, gpuf);
1773
1774         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1775                 bgr555_to_rgb565(d, s, w * 2);
1776
1777         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1778         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1779         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1780         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1781         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1782 }
1783
1784 static void debug_menu_loop(void)
1785 {
1786         int inp, df_x = 0, df_y = 0;
1787         GPUFreeze_t *gpuf;
1788
1789         gpuf = malloc(sizeof(*gpuf));
1790         if (gpuf == NULL)
1791                 return;
1792
1793         while (1)
1794         {
1795                 menu_draw_begin(0, 1);
1796                 draw_frame_debug(gpuf, df_x, df_y);
1797                 menu_draw_end();
1798
1799                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1800                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, NULL, 10);
1801                 if      (inp & PBTN_MBACK) break;
1802                 else if (inp & PBTN_UP)    { if (df_y > 0) df_y--; }
1803                 else if (inp & PBTN_DOWN)  { if (df_y < 512 - g_menuscreen_h) df_y++; }
1804                 else if (inp & PBTN_LEFT)  { if (df_x > 0) df_x -= 2; }
1805                 else if (inp & PBTN_RIGHT) { if (df_x < 1024 - g_menuscreen_w) df_x += 2; }
1806         }
1807
1808         free(gpuf);
1809 }
1810
1811 // --------- memcard manager ---------
1812
1813 static void draw_mc_icon(int dx, int dy, const u16 *s)
1814 {
1815         u16 *d;
1816         int x, y, l, p;
1817         
1818         d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1819
1820         for (y = 0; y < 16; y++, s += 16) {
1821                 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1822                         for (x = 0; x < 16; x++) {
1823                                 p = s[x];
1824                                 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1825                                         | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1826                         }
1827                 }
1828         }
1829 }
1830
1831 static void draw_mc_bg(void)
1832 {
1833         McdBlock *blocks1, *blocks2;
1834         int maxicons = 15;
1835         int i, y, row2;
1836
1837         blocks1 = malloc(15 * sizeof(blocks1[0]));
1838         blocks2 = malloc(15 * sizeof(blocks1[0]));
1839         if (blocks1 == NULL || blocks2 == NULL)
1840                 goto out;
1841
1842         for (i = 0; i < 15; i++) {
1843                 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1844                 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1845         }
1846
1847         menu_draw_begin(1, 1);
1848
1849         memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1850
1851         y = g_menuscreen_h / 2 - 15 * 32 / 2;
1852         if (y < 0) {
1853                 // doesn't fit..
1854                 y = 0;
1855                 maxicons = g_menuscreen_h / 32;
1856         }
1857
1858         row2 = g_menuscreen_w / 2;
1859         for (i = 0; i < maxicons; i++) {
1860                 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1861                 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1862
1863                 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1864                 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1865         }
1866
1867         menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1868
1869         menu_draw_end();
1870 out:
1871         free(blocks1);
1872         free(blocks2);
1873 }
1874
1875 static void handle_memcard_sel(void)
1876 {
1877         strcpy(Config.Mcd1, "none");
1878         if (memcard1_sel != 0)
1879                 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1880         strcpy(Config.Mcd2, "none");
1881         if (memcard2_sel != 0)
1882                 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1883         LoadMcds(Config.Mcd1, Config.Mcd2);
1884         draw_mc_bg();
1885 }
1886
1887 static menu_entry e_memcard_options[] =
1888 {
1889         mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1890         mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1891         mee_end,
1892 };
1893
1894 static int menu_loop_memcards(int id, int keys)
1895 {
1896         static int sel = 0;
1897         char *p;
1898         int i;
1899
1900         memcard1_sel = memcard2_sel = 0;
1901         p = strrchr(Config.Mcd1, '/');
1902         if (p != NULL)
1903                 for (i = 0; memcards[i] != NULL; i++)
1904                         if (strcmp(p + 1, memcards[i]) == 0)
1905                                 { memcard1_sel = i; break; }
1906         p = strrchr(Config.Mcd2, '/');
1907         if (p != NULL)
1908                 for (i = 0; memcards[i] != NULL; i++)
1909                         if (strcmp(p + 1, memcards[i]) == 0)
1910                                 { memcard2_sel = i; break; }
1911
1912         me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1913
1914         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1915
1916         return 0;
1917 }
1918
1919 // ------------ cheats menu ------------
1920
1921 static void draw_cheatlist(int sel)
1922 {
1923         int max_cnt, start, i, pos, active;
1924
1925         max_cnt = g_menuscreen_h / me_sfont_h;
1926         start = max_cnt / 2 - sel;
1927
1928         menu_draw_begin(1, 1);
1929
1930         for (i = 0; i < NumCheats; i++) {
1931                 pos = start + i;
1932                 if (pos < 0) continue;
1933                 if (pos >= max_cnt) break;
1934                 active = Cheats[i].Enabled;
1935                 smalltext_out16(14,                pos * me_sfont_h,
1936                         active ? "ON " : "OFF", active ? 0xfff6 : 0xffff);
1937                 smalltext_out16(14 + me_sfont_w*4, pos * me_sfont_h,
1938                         Cheats[i].Descr, active ? 0xfff6 : 0xffff);
1939         }
1940         pos = start + i;
1941         if (pos < max_cnt)
1942                 smalltext_out16(14, pos * me_sfont_h, "done", 0xffff);
1943
1944         text_out16(5, max_cnt / 2 * me_sfont_h, ">");
1945         menu_draw_end();
1946 }
1947
1948 static void menu_loop_cheats(void)
1949 {
1950         static int menu_sel = 0;
1951         int inp;
1952
1953         for (;;)
1954         {
1955                 draw_cheatlist(menu_sel);
1956                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_L|PBTN_R
1957                                 |PBTN_MOK|PBTN_MBACK, NULL, 33);
1958                 if (inp & PBTN_UP  ) { menu_sel--; if (menu_sel < 0) menu_sel = NumCheats; }
1959                 if (inp & PBTN_DOWN) { menu_sel++; if (menu_sel > NumCheats) menu_sel = 0; }
1960                 if (inp &(PBTN_LEFT|PBTN_L))  { menu_sel-=10; if (menu_sel < 0) menu_sel = 0; }
1961                 if (inp &(PBTN_RIGHT|PBTN_R)) { menu_sel+=10; if (menu_sel > NumCheats) menu_sel = NumCheats; }
1962                 if (inp & PBTN_MOK) { // action
1963                         if (menu_sel < NumCheats)
1964                                 Cheats[menu_sel].Enabled = !Cheats[menu_sel].Enabled;
1965                         else    break;
1966                 }
1967                 if (inp & PBTN_MBACK)
1968                         break;
1969         }
1970 }
1971
1972 // --------- main menu help ----------
1973
1974 static void menu_bios_warn(void)
1975 {
1976         int inp;
1977         static const char msg[] =
1978                 "You don't seem to have copied any BIOS\n"
1979                 "files to\n"
1980                 MENU_BIOS_PATH "\n\n"
1981
1982                 "While many games work fine with fake\n"
1983                 "(HLE) BIOS, others (like MGS and FF8)\n"
1984                 "require BIOS to work.\n"
1985                 "After copying the file, you'll also need\n"
1986                 "to select it in the emu's menu:\n"
1987                 "options->[BIOS/Plugins]\n\n"
1988                 "The file is usually named SCPH1001.BIN,\n"
1989                 "but other not compressed files can be\n"
1990                 "used too.\n\n"
1991                 "Press %s or %s to continue";
1992         char tmp_msg[sizeof(msg) + 64];
1993
1994         snprintf(tmp_msg, sizeof(tmp_msg), msg,
1995                 in_get_key_name(-1, -PBTN_MOK), in_get_key_name(-1, -PBTN_MBACK));
1996         while (1)
1997         {
1998                 draw_menu_message(tmp_msg, NULL);
1999
2000                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2001                 if (inp & (PBTN_MBACK|PBTN_MOK))
2002                         return;
2003         }
2004 }
2005
2006 // ------------ main menu ------------
2007
2008 static menu_entry e_menu_main[];
2009
2010 static void draw_frame_main(void)
2011 {
2012         struct tm *tmp;
2013         time_t ltime;
2014         int capacity;
2015         char ltime_s[16];
2016         char buff[64];
2017         char *out;
2018
2019         if (CdromId[0] != 0) {
2020                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
2021                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
2022                          Config.HLE ? "HLE" : "BIOS");
2023                 smalltext_out16(4, 1, buff, 0x105f);
2024         }
2025
2026         if (ready_to_go) {
2027                 capacity = plat_target_bat_capacity_get();
2028                 ltime = time(NULL);
2029                 tmp = localtime(&ltime);
2030                 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
2031                 if (capacity >= 0) {
2032                         snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, capacity);
2033                         out = buff;
2034                 }
2035                 else
2036                         out = ltime_s;
2037                 smalltext_out16(4, 1 + me_sfont_h, out, 0x105f);
2038         }
2039 }
2040
2041 static void draw_frame_credits(void)
2042 {
2043         smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
2044 }
2045
2046 static const char credits_text[] = 
2047         "PCSX-ReARMed\n\n"
2048         "(C) 1999-2003 PCSX Team\n"
2049         "(C) 2005-2009 PCSX-df Team\n"
2050         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
2051         "ARM recompiler (C) 2009-2011 Ari64\n"
2052 #ifdef BUILTIN_GPU_NEON
2053         "ARM NEON GPU (c) 2011-2012 Exophase\n"
2054 #endif
2055         "PEOpS GPU and SPU by Pete Bernert\n"
2056         "  and the P.E.Op.S. team\n"
2057         "PCSX4ALL plugin by PCSX4ALL team\n"
2058         "  Chui, Franxis, Unai\n\n"
2059         "integration, optimization and\n"
2060         "  frontend (C) 2010-2015 notaz\n";
2061
2062 static int reset_game(void)
2063 {
2064         ClosePlugins();
2065         OpenPlugins();
2066         SysReset();
2067         if (Config.HLE) {
2068                 if (LoadCdrom() == -1)
2069                         return -1;
2070         }
2071         return 0;
2072 }
2073
2074 static int reload_plugins(const char *cdimg)
2075 {
2076         pl_vout_buf = NULL;
2077
2078         ClosePlugins();
2079
2080         set_cd_image(cdimg);
2081         LoadPlugins();
2082         pcnt_hook_plugins();
2083         NetOpened = 0;
2084         if (OpenPlugins() == -1) {
2085                 menu_update_msg("failed to open plugins");
2086                 return -1;
2087         }
2088         plugin_call_rearmed_cbs();
2089
2090         cdrIsoMultidiskCount = 1;
2091         CdromId[0] = '\0';
2092         CdromLabel[0] = '\0';
2093
2094         return 0;
2095 }
2096
2097 static int run_bios(void)
2098 {
2099         boolean origSlowBoot = Config.SlowBoot;
2100
2101         if (bios_sel == 0)
2102                 return -1;
2103
2104         ready_to_go = 0;
2105         if (reload_plugins(NULL) != 0)
2106                 return -1;
2107         Config.SlowBoot = 1;
2108         SysReset();
2109         Config.SlowBoot = origSlowBoot;
2110
2111         ready_to_go = 1;
2112         return 0;
2113 }
2114
2115 static int run_exe(void)
2116 {
2117         const char *exts[] = { "exe", NULL };
2118         const char *fname;
2119
2120         fname = menu_loop_romsel(last_selected_fname,
2121                 sizeof(last_selected_fname), exts, NULL);
2122         if (fname == NULL)
2123                 return -1;
2124
2125         ready_to_go = 0;
2126         if (reload_plugins(NULL) != 0)
2127                 return -1;
2128
2129         SysReset();
2130         if (Load(fname) != 0) {
2131                 menu_update_msg("exe load failed, bad file?");
2132                 printf("meh\n");
2133                 return -1;
2134         }
2135
2136         ready_to_go = 1;
2137         return 0;
2138 }
2139
2140 static int run_cd_image(const char *fname)
2141 {
2142         int autoload_state = g_autostateld_opt;
2143
2144         ready_to_go = 0;
2145         reload_plugins(fname);
2146
2147         // always autodetect, menu_sync_config will override as needed
2148         Config.PsxAuto = 1;
2149
2150         if (CheckCdrom() == -1) {
2151                 // Only check the CD if we are starting the console with a CD
2152                 ClosePlugins();
2153                 menu_update_msg("unsupported/invalid CD image");
2154                 return -1;
2155         }
2156
2157         SysReset();
2158
2159         // Read main executable directly from CDRom and start it
2160         if (LoadCdrom() == -1) {
2161                 ClosePlugins();
2162                 menu_update_msg("failed to load CD image");
2163                 return -1;
2164         }
2165
2166         emu_on_new_cd(1);
2167         ready_to_go = 1;
2168
2169         if (autoload_state) {
2170                 unsigned int newest = 0;
2171                 int time, slot, newest_slot = -1;
2172
2173                 for (slot = 0; slot < 10; slot++) {
2174                         if (emu_check_save_file(slot, &time)) {
2175                                 if ((unsigned int)time > newest) {
2176                                         newest = time;
2177                                         newest_slot = slot;
2178                                 }
2179                         }
2180                 }
2181
2182                 if (newest_slot >= 0) {
2183                         lprintf("autoload slot %d\n", newest_slot);
2184                         emu_load_state(newest_slot);
2185                 }
2186                 else {
2187                         lprintf("no save to autoload.\n");
2188                 }
2189         }
2190
2191         return 0;
2192 }
2193
2194 static int romsel_run(void)
2195 {
2196         int prev_gpu, prev_spu;
2197         const char *fname;
2198
2199         fname = menu_loop_romsel(last_selected_fname,
2200                         sizeof(last_selected_fname), filter_exts,
2201                         optional_cdimg_filter);
2202         if (fname == NULL)
2203                 return -1;
2204
2205         printf("selected file: %s\n", fname);
2206
2207         new_dynarec_clear_full();
2208
2209         if (run_cd_image(fname) != 0)
2210                 return -1;
2211
2212         prev_gpu = gpu_plugsel;
2213         prev_spu = spu_plugsel;
2214         if (menu_load_config(1) != 0)
2215                 menu_load_config(0);
2216
2217         // check for plugin changes, have to repeat
2218         // loading if game config changed plugins to reload them
2219         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
2220                 printf("plugin change detected, reloading plugins..\n");
2221                 if (run_cd_image(fname) != 0)
2222                         return -1;
2223         }
2224
2225         strcpy(last_selected_fname, fname);
2226         menu_do_last_cd_img(0);
2227         return 0;
2228 }
2229
2230 static int swap_cd_image(void)
2231 {
2232         const char *fname;
2233
2234         fname = menu_loop_romsel(last_selected_fname,
2235                         sizeof(last_selected_fname), filter_exts,
2236                         optional_cdimg_filter);
2237         if (fname == NULL)
2238                 return -1;
2239
2240         printf("selected file: %s\n", fname);
2241
2242         CdromId[0] = '\0';
2243         CdromLabel[0] = '\0';
2244
2245         set_cd_image(fname);
2246         if (ReloadCdromPlugin() < 0) {
2247                 menu_update_msg("failed to load cdr plugin");
2248                 return -1;
2249         }
2250         if (CDR_open() < 0) {
2251                 menu_update_msg("failed to open cdr plugin");
2252                 return -1;
2253         }
2254
2255         SetCdOpenCaseTime(time(NULL) + 2);
2256         LidInterrupt();
2257
2258         strcpy(last_selected_fname, fname);
2259         return 0;
2260 }
2261
2262 static int swap_cd_multidisk(void)
2263 {
2264         cdrIsoMultidiskSelect++;
2265         CdromId[0] = '\0';
2266         CdromLabel[0] = '\0';
2267
2268         CDR_close();
2269         if (CDR_open() < 0) {
2270                 menu_update_msg("failed to open cdr plugin");
2271                 return -1;
2272         }
2273
2274         SetCdOpenCaseTime(time(NULL) + 2);
2275         LidInterrupt();
2276
2277         return 0;
2278 }
2279
2280 static void load_pcsx_cht(void)
2281 {
2282         static const char *exts[] = { "cht", NULL };
2283         const char *fname;
2284         char msg[64];
2285
2286         fname = menu_loop_romsel(last_selected_fname,
2287                         sizeof(last_selected_fname), exts, NULL);
2288         if (fname == NULL)
2289                 return;
2290
2291         printf("selected cheat file: %s\n", fname);
2292         LoadCheats(fname);
2293
2294         if (NumCheats == 0 && NumCodes == 0)
2295                 menu_update_msg("failed to load cheats");
2296         else {
2297                 snprintf(msg, sizeof(msg), "%d cheat(s) loaded", NumCheats + NumCodes);
2298                 menu_update_msg(msg);
2299         }
2300         me_enable(e_menu_main, MA_MAIN_CHEATS, ready_to_go && NumCheats);
2301 }
2302
2303 static int main_menu_handler(int id, int keys)
2304 {
2305         switch (id)
2306         {
2307         case MA_MAIN_RESUME_GAME:
2308                 if (ready_to_go)
2309                         return 1;
2310                 break;
2311         case MA_MAIN_SAVE_STATE:
2312                 if (ready_to_go)
2313                         return menu_loop_savestate(0);
2314                 break;
2315         case MA_MAIN_LOAD_STATE:
2316                 if (ready_to_go)
2317                         return menu_loop_savestate(1);
2318                 break;
2319         case MA_MAIN_RESET_GAME:
2320                 if (ready_to_go && reset_game() == 0)
2321                         return 1;
2322                 break;
2323         case MA_MAIN_LOAD_ROM:
2324                 if (romsel_run() == 0)
2325                         return 1;
2326                 break;
2327         case MA_MAIN_SWAP_CD:
2328                 if (swap_cd_image() == 0)
2329                         return 1;
2330                 break;
2331         case MA_MAIN_SWAP_CD_MULTI:
2332                 if (swap_cd_multidisk() == 0)
2333                         return 1;
2334                 break;
2335         case MA_MAIN_RUN_BIOS:
2336                 if (run_bios() == 0)
2337                         return 1;
2338                 break;
2339         case MA_MAIN_RUN_EXE:
2340                 if (run_exe() == 0)
2341                         return 1;
2342                 break;
2343         case MA_MAIN_CHEATS:
2344                 menu_loop_cheats();
2345                 break;
2346         case MA_MAIN_LOAD_CHEATS:
2347                 load_pcsx_cht();
2348                 break;
2349         case MA_MAIN_CREDITS:
2350                 draw_menu_message(credits_text, draw_frame_credits);
2351                 in_menu_wait(PBTN_MOK|PBTN_MBACK, NULL, 70);
2352                 break;
2353         case MA_MAIN_EXIT:
2354                 emu_core_ask_exit();
2355                 return 1;
2356         default:
2357                 lprintf("%s: something unknown selected\n", __FUNCTION__);
2358                 break;
2359         }
2360
2361         return 0;
2362 }
2363
2364 static menu_entry e_menu_main2[] =
2365 {
2366         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,       main_menu_handler),
2367         mee_handler_id("Next multidisk CD",  MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
2368         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,      main_menu_handler),
2369         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,       main_menu_handler),
2370         mee_handler   ("Memcard manager",    menu_loop_memcards),
2371         mee_handler_id("Load PCSX cheats..", MA_MAIN_LOAD_CHEATS,   main_menu_handler),
2372         mee_end,
2373 };
2374
2375 static int main_menu2_handler(int id, int keys)
2376 {
2377         static int sel = 0;
2378
2379         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
2380         me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
2381         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
2382         me_enable(e_menu_main2, MA_MAIN_LOAD_CHEATS, ready_to_go);
2383
2384         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
2385 }
2386
2387 static const char h_extra[] = "Change CD, manage memcards..\n";
2388
2389 static menu_entry e_menu_main[] =
2390 {
2391         mee_label     (""),
2392         mee_label     (""),
2393         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
2394         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
2395         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
2396         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
2397         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
2398         mee_handler   ("Options",            menu_loop_options),
2399         mee_handler   ("Controls",           menu_loop_keyconfig),
2400         mee_handler_id("Cheats",             MA_MAIN_CHEATS,      main_menu_handler),
2401         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
2402         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
2403         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
2404         mee_end,
2405 };
2406
2407 // ----------------------------
2408
2409 static void menu_leave_emu(void);
2410
2411 void menu_loop(void)
2412 {
2413         static int warned_about_bios = 0;
2414         static int sel = 0;
2415
2416         menu_leave_emu();
2417
2418         if (config_save_counter == 0) {
2419                 // assume first run
2420                 if (bioses[1] != NULL) {
2421                         // autoselect BIOS to make user's life easier
2422                         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[1]);
2423                         bios_sel = 1;
2424                 }
2425                 else if (!warned_about_bios) {
2426                         menu_bios_warn();
2427                         warned_about_bios = 1;
2428                 }
2429         }
2430
2431         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
2432         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
2433         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
2434         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
2435         me_enable(e_menu_main, MA_MAIN_CHEATS,      ready_to_go && NumCheats);
2436
2437         in_set_config_int(0, IN_CFG_BLOCKING, 1);
2438
2439         do {
2440                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
2441         } while (!ready_to_go && !g_emu_want_quit);
2442
2443         /* wait until menu, ok, back is released */
2444         while (in_menu_wait_any(NULL, 50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2445                 ;
2446
2447         in_set_config_int(0, IN_CFG_BLOCKING, 0);
2448
2449         menu_prepare_emu();
2450 }
2451
2452 static int qsort_strcmp(const void *p1, const void *p2)
2453 {
2454         char * const *s1 = (char * const *)p1;
2455         char * const *s2 = (char * const *)p2;
2456         return strcasecmp(*s1, *s2);
2457 }
2458
2459 static void scan_bios_plugins(void)
2460 {
2461         char fname[MAXPATHLEN];
2462         struct dirent *ent;
2463         int bios_i, gpu_i, spu_i, mc_i;
2464         char *p;
2465         DIR *dir;
2466
2467         bioses[0] = "HLE";
2468         gpu_plugins[0] = "builtin_gpu";
2469         spu_plugins[0] = "builtin_spu";
2470         memcards[0] = "(none)";
2471         bios_i = gpu_i = spu_i = mc_i = 1;
2472
2473         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2474         dir = opendir(fname);
2475         if (dir == NULL) {
2476                 perror("scan_bios_plugins bios opendir");
2477                 goto do_plugins;
2478         }
2479
2480         while (1) {
2481                 struct stat st;
2482
2483                 errno = 0;
2484                 ent = readdir(dir);
2485                 if (ent == NULL) {
2486                         if (errno != 0)
2487                                 perror("readdir");
2488                         break;
2489                 }
2490
2491                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2492                         continue;
2493
2494                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2495                 if (stat(fname, &st) != 0
2496                     || (st.st_size != 512*1024 && st.st_size != 4*1024*1024)) {
2497                         printf("bad BIOS file: %s\n", ent->d_name);
2498                         continue;
2499                 }
2500
2501                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2502                         bioses[bios_i++] = strdup(ent->d_name);
2503                         continue;
2504                 }
2505
2506                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2507         }
2508
2509         closedir(dir);
2510
2511 do_plugins:
2512         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2513         dir = opendir(fname);
2514         if (dir == NULL) {
2515                 perror("scan_bios_plugins plugins opendir");
2516                 goto do_memcards;
2517         }
2518
2519         while (1) {
2520                 void *h, *tmp;
2521
2522                 errno = 0;
2523                 ent = readdir(dir);
2524                 if (ent == NULL) {
2525                         if (errno != 0)
2526                                 perror("readdir");
2527                         break;
2528                 }
2529                 p = strstr(ent->d_name, ".so");
2530                 if (p == NULL)
2531                         continue;
2532
2533                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2534                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2535                 if (h == NULL) {
2536                         fprintf(stderr, "%s\n", dlerror());
2537                         continue;
2538                 }
2539
2540                 // now what do we have here?
2541                 tmp = dlsym(h, "GPUinit");
2542                 if (tmp) {
2543                         dlclose(h);
2544                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2545                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2546                         continue;
2547                 }
2548
2549                 tmp = dlsym(h, "SPUinit");
2550                 if (tmp) {
2551                         dlclose(h);
2552                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2553                                 spu_plugins[spu_i++] = strdup(ent->d_name);
2554                         continue;
2555                 }
2556
2557                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2558                 dlclose(h);
2559         }
2560
2561         closedir(dir);
2562
2563 do_memcards:
2564         dir = opendir("." MEMCARD_DIR);
2565         if (dir == NULL) {
2566                 perror("scan_bios_plugins memcards opendir");
2567                 return;
2568         }
2569
2570         while (1) {
2571                 struct stat st;
2572
2573                 errno = 0;
2574                 ent = readdir(dir);
2575                 if (ent == NULL) {
2576                         if (errno != 0)
2577                                 perror("readdir");
2578                         break;
2579                 }
2580
2581                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2582                         continue;
2583
2584                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2585                 if (stat(fname, &st) != 0) {
2586                         printf("bad memcard file: %s\n", ent->d_name);
2587                         continue;
2588                 }
2589
2590                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2591                         memcards[mc_i++] = strdup(ent->d_name);
2592                         continue;
2593                 }
2594
2595                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2596         }
2597
2598         if (mc_i > 2)
2599                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2600
2601         closedir(dir);
2602 }
2603
2604 void menu_init(void)
2605 {
2606         char buff[MAXPATHLEN];
2607         int i;
2608
2609         cpu_clock_st = cpu_clock = plat_target_cpu_clock_get();
2610
2611         scan_bios_plugins();
2612         menu_init_base();
2613
2614         menu_set_defconfig();
2615         menu_load_config(0);
2616         menu_do_last_cd_img(1);
2617         last_vout_w = 320;
2618         last_vout_h = 240;
2619         last_vout_bpp = 16;
2620
2621         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2622         g_menubg_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2623         if (g_menubg_src_ptr == NULL || g_menubg_ptr == NULL) {
2624                 fprintf(stderr, "OOM\n");
2625                 exit(1);
2626         }
2627
2628         emu_make_path(buff, "skin/background.png", sizeof(buff));
2629         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
2630
2631         i = plat_target.cpu_clock_set != NULL
2632                 && plat_target.cpu_clock_get != NULL && cpu_clock_st > 0;
2633         me_enable(e_menu_options, MA_OPT_CPU_CLOCKS, i);
2634
2635         i = me_id2offset(e_menu_gfx_options, MA_OPT_VOUT_MODE);
2636         e_menu_gfx_options[i].data = plat_target.vout_methods;
2637         me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE,
2638                 plat_target.vout_methods != NULL);
2639
2640         i = me_id2offset(e_menu_gfx_options, MA_OPT_HWFILTER);
2641         e_menu_gfx_options[i].data = plat_target.hwfilters;
2642         me_enable(e_menu_gfx_options, MA_OPT_HWFILTER,
2643                 plat_target.hwfilters != NULL);
2644
2645         me_enable(e_menu_gfx_options, MA_OPT_GAMMA,
2646                 plat_target.gamma_set != NULL);
2647
2648 #ifdef HAVE_PRE_ARMV7
2649         me_enable(e_menu_gfx_options, MA_OPT_SWFILTER, 0);
2650 #endif
2651         me_enable(e_menu_gfx_options, MA_OPT_VARSCALER, MENU_SHOW_VARSCALER);
2652         me_enable(e_menu_gfx_options, MA_OPT_VOUT_MODE, MENU_SHOW_VOUTMODE);
2653         me_enable(e_menu_gfx_options, MA_OPT_VARSCALER_C, MENU_SHOW_VARSCALER);
2654         me_enable(e_menu_gfx_options, MA_OPT_SCALER2, MENU_SHOW_SCALER2);
2655         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, MENU_SHOW_NUBS_BTNS);
2656         me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, MENU_SHOW_VIBRATION);
2657         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, MENU_SHOW_DEADZONE);
2658 }
2659
2660 void menu_notify_mode_change(int w, int h, int bpp)
2661 {
2662         last_vout_w = w;
2663         last_vout_h = h;
2664         last_vout_bpp = bpp;
2665 }
2666
2667 static void menu_leave_emu(void)
2668 {
2669         if (GPU_close != NULL) {
2670                 int ret = GPU_close();
2671                 if (ret)
2672                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2673         }
2674
2675         plat_video_menu_enter(ready_to_go);
2676
2677         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2678         if (pl_vout_buf != NULL && ready_to_go) {
2679                 int x = max(0, g_menuscreen_w - last_vout_w);
2680                 int y = max(0, g_menuscreen_h / 2 - last_vout_h / 2);
2681                 int w = min(g_menuscreen_w, last_vout_w);
2682                 int h = min(g_menuscreen_h, last_vout_h);
2683                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2684                 char *s = pl_vout_buf;
2685
2686                 if (last_vout_bpp == 16) {
2687                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 2)
2688                                 menu_darken_bg(d, s, w, 0);
2689                 }
2690                 else {
2691                         for (; h > 0; h--, d += g_menuscreen_w, s += last_vout_w * 3) {
2692                                 rgb888_to_rgb565(d, s, w * 3);
2693                                 menu_darken_bg(d, d, w, 0);
2694                         }
2695                 }
2696         }
2697
2698         if (ready_to_go)
2699                 cpu_clock = plat_target_cpu_clock_get();
2700 }
2701
2702 void menu_prepare_emu(void)
2703 {
2704         R3000Acpu *prev_cpu = psxCpu;
2705
2706         plat_video_menu_leave();
2707
2708         #if !defined(DRC_DISABLE) || defined(LIGHTREC)
2709         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2710         #else
2711         psxCpu = &psxInt;
2712         #endif
2713         if (psxCpu != prev_cpu) {
2714                 prev_cpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
2715                 prev_cpu->Shutdown();
2716                 psxCpu->Init();
2717                 psxCpu->Reset();
2718                 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
2719         }
2720
2721         menu_sync_config();
2722         psxCpu->ApplyConfig();
2723
2724         // core doesn't care about Config.Cdda changes,
2725         // so handle them manually here
2726         if (Config.Cdda)
2727                 CDR_stop();
2728
2729         if (cpu_clock > 0)
2730                 plat_target_cpu_clock_set(cpu_clock);
2731
2732         // push config to GPU plugin
2733         plugin_call_rearmed_cbs();
2734
2735         if (GPU_open != NULL) {
2736                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2737                 if (ret)
2738                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2739         }
2740 }
2741
2742 void menu_update_msg(const char *msg)
2743 {
2744         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2745         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2746
2747         menu_error_time = plat_get_ticks_ms();
2748         lprintf("msg: %s\n", menu_error_msg);
2749 }
2750
2751 void menu_finish(void)
2752 {
2753         if (cpu_clock_st > 0)
2754                 plat_target_cpu_clock_set(cpu_clock_st);
2755 }