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