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