new ability to adjust psx cpu clock
[pcsx_rearmed.git] / frontend / menu.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2010-2011
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 #include <stdio.h>
12 #include <string.h>
13 #include <errno.h>
14 #include <dlfcn.h>
15 #include <zlib.h>
16
17 #include "main.h"
18 #include "menu.h"
19 #include "config.h"
20 #include "plugin.h"
21 #include "plugin_lib.h"
22 #include "omap.h"
23 #include "plat.h"
24 #include "pcnt.h"
25 #include "cspace.h"
26 #include "common/plat.h"
27 #include "common/input.h"
28 #include "linux/in_evdev.h"
29 #include "../libpcsxcore/misc.h"
30 #include "../libpcsxcore/cdrom.h"
31 #include "../libpcsxcore/psemu_plugin_defs.h"
32 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
33 #include "../plugins/dfinput/main.h"
34 #include "revision.h"
35
36 #define array_size(x) (sizeof(x) / sizeof(x[0]))
37
38 typedef enum
39 {
40         MA_NONE = 1,
41         MA_MAIN_RESUME_GAME,
42         MA_MAIN_SAVE_STATE,
43         MA_MAIN_LOAD_STATE,
44         MA_MAIN_RESET_GAME,
45         MA_MAIN_LOAD_ROM,
46         MA_MAIN_SWAP_CD,
47         MA_MAIN_RUN_BIOS,
48         MA_MAIN_RUN_EXE,
49         MA_MAIN_CONTROLS,
50         MA_MAIN_CREDITS,
51         MA_MAIN_EXIT,
52         MA_CTRL_PLAYER1,
53         MA_CTRL_PLAYER2,
54         MA_CTRL_EMU,
55         MA_CTRL_DEV_FIRST,
56         MA_CTRL_DEV_NEXT,
57         MA_CTRL_NUBS_BTNS,
58         MA_CTRL_DEADZONE,
59         MA_CTRL_DONE,
60         MA_OPT_SAVECFG,
61         MA_OPT_SAVECFG_GAME,
62         MA_OPT_CPU_CLOCKS,
63         MA_OPT_FILTERING,
64         MA_OPT_DISP_OPTS,
65 } menu_id;
66
67 enum {
68         SCALE_1_1,
69         SCALE_4_3,
70         SCALE_4_3v2,
71         SCALE_FULLSCREEN,
72         SCALE_CUSTOM,
73 };
74
75 static int last_psx_w, last_psx_h, last_psx_bpp;
76 static int scaling, filter, cpu_clock, cpu_clock_st, volume_boost, frameskip;
77 static char rom_fname_reload[MAXPATHLEN];
78 static char last_selected_fname[MAXPATHLEN];
79 static int warned_about_bios, region, in_type_sel1, in_type_sel2;
80 static int psx_clock;
81 static int memcard1_sel, memcard2_sel;
82 int g_opts, analog_deadzone;
83
84 #define DEFAULT_PSX_CLOCK 50
85 #define DEFAULT_PSX_CLOCK_S "50"
86
87 // sound plugin
88 extern int iUseReverb;
89 extern int iUseInterpolation;
90 extern int iXAPitch;
91 extern int iSPUIRQWait;
92 extern int iUseTimer;
93 extern int iVolume;
94
95 static const char *bioses[24];
96 static const char *gpu_plugins[16];
97 static const char *spu_plugins[16];
98 static const char *memcards[32];
99 static int bios_sel, gpu_plugsel, spu_plugsel;
100
101
102 static int min(int x, int y) { return x < y ? x : y; }
103 static int max(int x, int y) { return x > y ? x : y; }
104
105 void emu_make_path(char *buff, const char *end, int size)
106 {
107         int pos, end_len;
108
109         end_len = strlen(end);
110         pos = plat_get_root_dir(buff, size);
111         strncpy(buff + pos, end, size - pos);
112         buff[size - 1] = 0;
113         if (pos + end_len > size - 1)
114                 printf("Warning: path truncated: %s\n", buff);
115 }
116
117 static int emu_check_save_file(int slot)
118 {
119         int ret = emu_check_state(slot);
120         return ret == 0 ? 1 : 0;
121 }
122
123 static int emu_save_load_game(int load, int unused)
124 {
125         int ret;
126
127         if (load) {
128                 ret = emu_load_state(state_slot);
129
130                 // reflect hle/bios mode from savestate
131                 if (Config.HLE)
132                         bios_sel = 0;
133                 else if (bios_sel == 0 && bioses[1] != NULL)
134                         // XXX: maybe find the right bios instead
135                         bios_sel = 1;
136         }
137         else
138                 ret = emu_save_state(state_slot);
139
140         return ret;
141 }
142
143 // propagate menu settings to the emu vars
144 static void menu_sync_config(void)
145 {
146         static int allow_abs_only_old;
147
148         Config.PsxAuto = 1;
149         if (region > 0) {
150                 Config.PsxAuto = 0;
151                 Config.PsxType = region - 1;
152         }
153         cycle_multiplier = 10000 / psx_clock;
154
155         switch (in_type_sel1) {
156         case 1:  in_type1 = PSE_PAD_TYPE_ANALOGPAD; break;
157         case 2:  in_type1 = PSE_PAD_TYPE_GUNCON;    break;
158         default: in_type1 = PSE_PAD_TYPE_STANDARD;
159         }
160         switch (in_type_sel2) {
161         case 1:  in_type2 = PSE_PAD_TYPE_ANALOGPAD; break;
162         case 2:  in_type2 = PSE_PAD_TYPE_GUNCON;    break;
163         default: in_type2 = PSE_PAD_TYPE_STANDARD;
164         }
165         if (in_evdev_allow_abs_only != allow_abs_only_old) {
166                 plat_rescan_inputs();
167                 allow_abs_only_old = in_evdev_allow_abs_only;
168         }
169
170         iVolume = 768 + 128 * volume_boost;
171         pl_rearmed_cbs.frameskip = frameskip - 1;
172         pl_timing_prepare(Config.PsxType);
173 }
174
175 static void menu_set_defconfig(void)
176 {
177         g_opts = 0;
178         scaling = SCALE_4_3;
179         volume_boost = 0;
180         frameskip = 0;
181         analog_deadzone = 70;
182         psx_clock = DEFAULT_PSX_CLOCK;
183
184         region = 0;
185         in_type_sel1 = in_type_sel2 = 0;
186         in_evdev_allow_abs_only = 0;
187         Config.Xa = Config.Cdda = Config.Sio =
188         Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
189         Config.CdrReschedule = 0;
190
191         pl_rearmed_cbs.gpu_peops.iUseDither = 0;
192         pl_rearmed_cbs.gpu_peops.dwActFixes = 1<<7;
193         pl_rearmed_cbs.gpu_unai.abe_hack =
194         pl_rearmed_cbs.gpu_unai.no_light =
195         pl_rearmed_cbs.gpu_unai.no_blend = 0;
196
197         iUseReverb = 2;
198         iUseInterpolation = 1;
199         iXAPitch = 0;
200         iSPUIRQWait = 1;
201         iUseTimer = 2;
202 #ifndef __ARM_ARCH_7A__ /* XXX */
203         iUseReverb = 0;
204         iUseInterpolation = 0;
205 #endif
206
207         menu_sync_config();
208 }
209
210 #define CE_CONFIG_STR(val) \
211         { #val, 0, Config.val }
212
213 #define CE_CONFIG_VAL(val) \
214         { #val, sizeof(Config.val), &Config.val }
215
216 #define CE_STR(val) \
217         { #val, 0, val }
218
219 #define CE_INTVAL(val) \
220         { #val, sizeof(val), &val }
221
222 #define CE_INTVAL_P(val) \
223         { #val, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
224
225 // 'versioned' var, used when defaults change
226 #define CE_INTVAL_V(val, ver) \
227         { #val #ver, sizeof(val), &val }
228
229 #define CE_INTVAL_PV(val, ver) \
230         { #val #ver, sizeof(pl_rearmed_cbs.val), &pl_rearmed_cbs.val }
231
232 static const struct {
233         const char *name;
234         size_t len;
235         void *val;
236 } config_data[] = {
237         CE_CONFIG_STR(Bios),
238         CE_CONFIG_STR(Gpu),
239         CE_CONFIG_STR(Spu),
240 //      CE_CONFIG_STR(Cdr),
241         CE_CONFIG_VAL(Xa),
242         CE_CONFIG_VAL(Sio),
243         CE_CONFIG_VAL(Mdec),
244         CE_CONFIG_VAL(Cdda),
245         CE_CONFIG_VAL(Debug),
246         CE_CONFIG_VAL(PsxOut),
247         CE_CONFIG_VAL(SpuIrq),
248         CE_CONFIG_VAL(RCntFix),
249         CE_CONFIG_VAL(VSyncWA),
250         CE_CONFIG_VAL(Cpu),
251         CE_CONFIG_VAL(CdrReschedule),
252         CE_INTVAL(region),
253         CE_INTVAL_V(scaling, 2),
254         CE_INTVAL(g_layer_x),
255         CE_INTVAL(g_layer_y),
256         CE_INTVAL(g_layer_w),
257         CE_INTVAL(g_layer_h),
258         CE_INTVAL(filter),
259         CE_INTVAL(state_slot),
260         CE_INTVAL(cpu_clock),
261         CE_INTVAL(g_opts),
262         CE_INTVAL(in_type_sel1),
263         CE_INTVAL(in_type_sel2),
264         CE_INTVAL(analog_deadzone),
265         CE_INTVAL_V(frameskip, 2),
266         CE_INTVAL_P(gpu_peops.iUseDither),
267         CE_INTVAL_P(gpu_peops.dwActFixes),
268         CE_INTVAL_P(gpu_unai.abe_hack),
269         CE_INTVAL_P(gpu_unai.no_light),
270         CE_INTVAL_P(gpu_unai.no_blend),
271         CE_INTVAL_V(iUseReverb, 3),
272         CE_INTVAL_V(iXAPitch, 3),
273         CE_INTVAL_V(iUseInterpolation, 3),
274         CE_INTVAL_V(iSPUIRQWait, 3),
275         CE_INTVAL_V(iUseTimer, 3),
276         CE_INTVAL(warned_about_bios),
277         CE_INTVAL(in_evdev_allow_abs_only),
278         CE_INTVAL(volume_boost),
279         CE_INTVAL(psx_clock),
280 };
281
282 static char *get_cd_label(void)
283 {
284         static char trimlabel[33];
285         int j;
286
287         strncpy(trimlabel, CdromLabel, 32);
288         trimlabel[32] = 0;
289         for (j = 31; j >= 0; j--)
290                 if (trimlabel[j] == ' ')
291                         trimlabel[j] = 0;
292
293         return trimlabel;
294 }
295
296 static void make_cfg_fname(char *buf, size_t size, int is_game)
297 {
298         if (is_game)
299                 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
300         else
301                 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
302 }
303
304 static void keys_write_all(FILE *f);
305
306 static int menu_write_config(int is_game)
307 {
308         char cfgfile[MAXPATHLEN];
309         FILE *f;
310         int i;
311
312         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
313         f = fopen(cfgfile, "w");
314         if (f == NULL) {
315                 printf("menu_write_config: failed to open: %s\n", cfgfile);
316                 return -1;
317         }
318
319         for (i = 0; i < ARRAY_SIZE(config_data); i++) {
320                 fprintf(f, "%s = ", config_data[i].name);
321                 switch (config_data[i].len) {
322                 case 0:
323                         fprintf(f, "%s\n", (char *)config_data[i].val);
324                         break;
325                 case 1:
326                         fprintf(f, "%x\n", *(u8 *)config_data[i].val);
327                         break;
328                 case 2:
329                         fprintf(f, "%x\n", *(u16 *)config_data[i].val);
330                         break;
331                 case 4:
332                         fprintf(f, "%x\n", *(u32 *)config_data[i].val);
333                         break;
334                 default:
335                         printf("menu_write_config: unhandled len %d for %s\n",
336                                  config_data[i].len, config_data[i].name);
337                         break;
338                 }
339         }
340
341         if (!is_game)
342                 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
343
344         keys_write_all(f);
345         fclose(f);
346
347         return 0;
348 }
349
350 static void parse_str_val(char *cval, const char *src)
351 {
352         char *tmp;
353         strncpy(cval, src, MAXPATHLEN);
354         cval[MAXPATHLEN - 1] = 0;
355         tmp = strchr(cval, '\n');
356         if (tmp == NULL)
357                 tmp = strchr(cval, '\r');
358         if (tmp != NULL)
359                 *tmp = 0;
360 }
361
362 static void keys_load_all(const char *cfg);
363
364 static int menu_load_config(int is_game)
365 {
366         char cfgfile[MAXPATHLEN];
367         int i, ret = -1;
368         long size;
369         char *cfg;
370         FILE *f;
371
372         make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
373         f = fopen(cfgfile, "r");
374         if (f == NULL) {
375                 printf("menu_load_config: failed to open: %s\n", cfgfile);
376                 return -1;
377         }
378
379         fseek(f, 0, SEEK_END);
380         size = ftell(f);
381         if (size <= 0) {
382                 printf("bad size %ld: %s\n", size, cfgfile);
383                 goto fail;
384         }
385
386         cfg = malloc(size + 1);
387         if (cfg == NULL)
388                 goto fail;
389
390         fseek(f, 0, SEEK_SET);
391         if (fread(cfg, 1, size, f) != size) {
392                 printf("failed to read: %s\n", cfgfile);
393                 goto fail_read;
394         }
395         cfg[size] = 0;
396
397         for (i = 0; i < ARRAY_SIZE(config_data); i++) {
398                 char *tmp, *tmp2;
399                 u32 val;
400
401                 tmp = strstr(cfg, config_data[i].name);
402                 if (tmp == NULL)
403                         continue;
404                 tmp += strlen(config_data[i].name);
405                 if (strncmp(tmp, " = ", 3) != 0)
406                         continue;
407                 tmp += 3;
408
409                 if (config_data[i].len == 0) {
410                         parse_str_val(config_data[i].val, tmp);
411                         continue;
412                 }
413
414                 tmp2 = NULL;
415                 val = strtoul(tmp, &tmp2, 16);
416                 if (tmp2 == NULL || tmp == tmp2)
417                         continue; // parse failed
418
419                 switch (config_data[i].len) {
420                 case 1:
421                         *(u8 *)config_data[i].val = val;
422                         break;
423                 case 2:
424                         *(u16 *)config_data[i].val = val;
425                         break;
426                 case 4:
427                         *(u32 *)config_data[i].val = val;
428                         break;
429                 default:
430                         printf("menu_load_config: unhandled len %d for %s\n",
431                                  config_data[i].len, config_data[i].name);
432                         break;
433                 }
434         }
435
436         if (!is_game) {
437                 char *tmp = strstr(cfg, "lastcdimg = ");
438                 if (tmp != NULL) {
439                         tmp += 12;
440                         parse_str_val(last_selected_fname, tmp);
441                 }
442         }
443
444         menu_sync_config();
445
446         // sync plugins
447         for (i = bios_sel = 0; bioses[i] != NULL; i++)
448                 if (strcmp(Config.Bios, bioses[i]) == 0)
449                         { bios_sel = i; break; }
450
451         for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
452                 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
453                         { gpu_plugsel = i; break; }
454
455         for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
456                 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
457                         { spu_plugsel = i; break; }
458
459         keys_load_all(cfg);
460         ret = 0;
461 fail_read:
462         free(cfg);
463 fail:
464         fclose(f);
465         return ret;
466 }
467
468 // rrrr rggg gggb bbbb
469 static unsigned short fname2color(const char *fname)
470 {
471         static const char *cdimg_exts[] = { ".bin", ".img", ".mdf", ".iso", ".cue", ".z", ".bz", ".znx", ".pbp" };
472         static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub", ".table", ".index", ".sbi" };
473         const char *ext = strrchr(fname, '.');
474         int i;
475
476         if (ext == NULL)
477                 return 0xffff;
478         for (i = 0; i < array_size(cdimg_exts); i++)
479                 if (strcasecmp(ext, cdimg_exts[i]) == 0)
480                         return 0x7bff;
481         for (i = 0; i < array_size(other_exts); i++)
482                 if (strcasecmp(ext, other_exts[i]) == 0)
483                         return 0xa514;
484         return 0xffff;
485 }
486
487 static void draw_savestate_bg(int slot);
488
489 static const char *filter_exts[] = {
490         ".mp3", ".MP3", ".txt", ".htm", "html", ".jpg", ".pnd"
491 };
492
493 #define MENU_ALIGN_LEFT
494 #ifdef __ARM_ARCH_7A__ // assume hires device
495 #define MENU_X2 1
496 #else
497 #define MENU_X2 0
498 #endif
499
500 #define menu_init menu_init_common
501 #include "common/menu.c"
502 #undef menu_init
503
504 // a bit of black magic here
505 static void draw_savestate_bg(int slot)
506 {
507         static const int psx_widths[8]  = { 256, 368, 320, 384, 512, 512, 640, 640 };
508         int x, y, w, h;
509         char fname[MAXPATHLEN];
510         GPUFreeze_t *gpu;
511         u16 *s, *d;
512         gzFile f;
513         int ret;
514         u32 tmp;
515
516         ret = get_state_filename(fname, sizeof(fname), slot);
517         if (ret != 0)
518                 return;
519
520         f = gzopen(fname, "rb");
521         if (f == NULL)
522                 return;
523
524         if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
525                 fprintf(stderr, "gzseek failed\n");
526                 gzclose(f);
527                 return;
528         }
529
530         gpu = malloc(sizeof(*gpu));
531         if (gpu == NULL) {
532                 gzclose(f);
533                 return;
534         }
535
536         ret = gzread(f, gpu, sizeof(*gpu));
537         gzclose(f);
538         if (ret != sizeof(*gpu)) {
539                 fprintf(stderr, "gzread failed\n");
540                 goto out;
541         }
542
543         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
544
545         if (gpu->ulStatus & 0x800000)
546                 goto out; // disabled
547
548         x = gpu->ulControl[5] & 0x3ff;
549         y = (gpu->ulControl[5] >> 10) & 0x1ff;
550         s = (u16 *)gpu->psxVRam + y * 1024 + (x & ~1);
551         w = psx_widths[(gpu->ulStatus >> 16) & 7];
552         tmp = gpu->ulControl[7];
553         h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
554         if (gpu->ulStatus & 0x80000) // doubleheight
555                 h *= 2;
556
557         x = max(0, g_menuscreen_w - w) & ~3;
558         y = max(0, g_menuscreen_h / 2 - h / 2);
559         w = min(g_menuscreen_w, w);
560         h = min(g_menuscreen_h, h);
561         d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
562
563         for (; h > 0; h--, d += g_menuscreen_w, s += 1024) {
564                 if (gpu->ulStatus & 0x200000)
565                         bgr888_to_rgb565(d, s, w * 3);
566                 else
567                         bgr555_to_rgb565(d, s, w * 2);
568 #ifndef __ARM_ARCH_7A__
569                 // better darken this on small screens
570                 menu_darken_bg(d, d, w * 2, 0);
571 #endif
572         }
573
574 out:
575         free(gpu);
576 }
577
578 // ---------- XXX: pandora specific -----------
579
580 static const char pnd_script_base[] = "sudo -n /usr/pandora/scripts";
581 static char **pnd_filter_list;
582
583 static void apply_filter(int which)
584 {
585         static int old = -1;
586         char buf[128];
587         int i;
588
589         if (pnd_filter_list == NULL || which == old)
590                 return;
591
592         for (i = 0; i < which; i++)
593                 if (pnd_filter_list[i] == NULL)
594                         return;
595
596         if (pnd_filter_list[i] == NULL)
597                 return;
598
599         snprintf(buf, sizeof(buf), "%s/op_videofir.sh %s", pnd_script_base, pnd_filter_list[i]);
600         system(buf);
601         old = which;
602 }
603
604 static void apply_lcdrate(int pal)
605 {
606         static int old = -1;
607         char buf[128];
608
609         if (pal == old)
610                 return;
611
612         snprintf(buf, sizeof(buf), "%s/op_lcdrate.sh %d",
613                         pnd_script_base, pal ? 50 : 60);
614         system(buf);
615         old = pal;
616 }
617
618 static menu_entry e_menu_gfx_options[];
619
620 static void pnd_menu_init(void)
621 {
622         struct dirent *ent;
623         int i, count = 0;
624         char **mfilters;
625         char buff[64];
626         DIR *dir;
627
628         cpu_clock_st = cpu_clock = plat_cpu_clock_get();
629
630         dir = opendir("/etc/pandora/conf/dss_fir");
631         if (dir == NULL) {
632                 perror("filter opendir");
633                 return;
634         }
635
636         while (1) {
637                 errno = 0;
638                 ent = readdir(dir);
639                 if (ent == NULL) {
640                         if (errno != 0)
641                                 perror("readdir");
642                         break;
643                 }
644
645                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
646                         continue;
647
648                 count++;
649         }
650
651         if (count == 0)
652                 return;
653
654         mfilters = calloc(count + 1, sizeof(mfilters[0]));
655         if (mfilters == NULL)
656                 return;
657
658         rewinddir(dir);
659         for (i = 0; (ent = readdir(dir)); ) {
660                 size_t len;
661
662                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
663                         continue;
664
665                 len = strlen(ent->d_name);
666
667                 // skip pre-HF5 extra files
668                 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
669                         continue;
670                 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
671                         continue;
672
673                 // have to cut "_up_h" for pre-HF5
674                 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
675                         len -= 5;
676
677                 if (len > sizeof(buff) - 1)
678                         continue;
679
680                 strncpy(buff, ent->d_name, len);
681                 buff[len] = 0;
682                 mfilters[i] = strdup(buff);
683                 if (mfilters[i] != NULL)
684                         i++;
685         }
686         closedir(dir);
687
688         i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
689         e_menu_gfx_options[i].data = (void *)mfilters;
690         pnd_filter_list = mfilters;
691 }
692
693 void menu_finish(void)
694 {
695         plat_cpu_clock_apply(cpu_clock_st);
696 }
697
698 // -------------- key config --------------
699
700 me_bind_action me_ctrl_actions[] =
701 {
702         { "UP      ", 1 << DKEY_UP},
703         { "DOWN    ", 1 << DKEY_DOWN },
704         { "LEFT    ", 1 << DKEY_LEFT },
705         { "RIGHT   ", 1 << DKEY_RIGHT },
706         { "TRIANGLE", 1 << DKEY_TRIANGLE },
707         { "CIRCLE  ", 1 << DKEY_CIRCLE },
708         { "CROSS   ", 1 << DKEY_CROSS },
709         { "SQUARE  ", 1 << DKEY_SQUARE },
710         { "L1      ", 1 << DKEY_L1 },
711         { "R1      ", 1 << DKEY_R1 },
712         { "L2      ", 1 << DKEY_L2 },
713         { "R2      ", 1 << DKEY_R2 },
714         { "L3      ", 1 << DKEY_L3 },
715         { "R3      ", 1 << DKEY_R3 },
716         { "START   ", 1 << DKEY_START },
717         { "SELECT  ", 1 << DKEY_SELECT },
718         { NULL,       0 }
719 };
720
721 me_bind_action emuctrl_actions[] =
722 {
723         { "Save State       ", 1 << SACTION_SAVE_STATE },
724         { "Load State       ", 1 << SACTION_LOAD_STATE },
725         { "Prev Save Slot   ", 1 << SACTION_PREV_SSLOT },
726         { "Next Save Slot   ", 1 << SACTION_NEXT_SSLOT },
727         { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
728         { "Take Screenshot  ", 1 << SACTION_SCREENSHOT },
729         { "Enter Menu       ", 1 << SACTION_ENTER_MENU },
730         { "Gun Trigger      ", 1 << SACTION_GUN_TRIGGER },
731         { "Gun A button     ", 1 << SACTION_GUN_A },
732         { "Gun B button     ", 1 << SACTION_GUN_B },
733         { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
734         { NULL,                0 }
735 };
736
737 static char *mystrip(char *str)
738 {
739         int i, len;
740
741         len = strlen(str);
742         for (i = 0; i < len; i++)
743                 if (str[i] != ' ') break;
744         if (i > 0) memmove(str, str + i, len - i + 1);
745
746         len = strlen(str);
747         for (i = len - 1; i >= 0; i--)
748                 if (str[i] != ' ') break;
749         str[i+1] = 0;
750
751         return str;
752 }
753
754 static void get_line(char *d, size_t size, const char *s)
755 {
756         const char *pe;
757         size_t len;
758
759         for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
760                 ;
761         len = pe - s;
762         if (len > size - 1)
763                 len = size - 1;
764         strncpy(d, s, len);
765         d[len] = 0;
766
767         mystrip(d);
768 }
769
770 static void keys_write_all(FILE *f)
771 {
772         int d;
773
774         for (d = 0; d < IN_MAX_DEVS; d++)
775         {
776                 const int *binds = in_get_dev_binds(d);
777                 const char *name = in_get_dev_name(d, 0, 0);
778                 int k, count = 0;
779
780                 if (binds == NULL || name == NULL)
781                         continue;
782
783                 fprintf(f, "binddev = %s\n", name);
784                 in_get_config(d, IN_CFG_BIND_COUNT, &count);
785
786                 for (k = 0; k < count; k++)
787                 {
788                         int i, kbinds, mask;
789                         char act[32];
790
791                         act[0] = act[31] = 0;
792                         name = in_get_key_name(d, k);
793
794                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
795                         for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
796                                 mask = me_ctrl_actions[i].mask;
797                                 if (mask & kbinds) {
798                                         strncpy(act, me_ctrl_actions[i].name, 31);
799                                         fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
800                                         kbinds &= ~mask;
801                                 }
802                                 mask = me_ctrl_actions[i].mask << 16;
803                                 if (mask & kbinds) {
804                                         strncpy(act, me_ctrl_actions[i].name, 31);
805                                         fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
806                                         kbinds &= ~mask;
807                                 }
808                         }
809
810                         kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
811                         for (i = 0; kbinds && i < ARRAY_SIZE(emuctrl_actions) - 1; i++) {
812                                 mask = emuctrl_actions[i].mask;
813                                 if (mask & kbinds) {
814                                         strncpy(act, emuctrl_actions[i].name, 31);
815                                         fprintf(f, "bind %s = %s\n", name, mystrip(act));
816                                         kbinds &= ~mask;
817                                 }
818                         }
819                 }
820         }
821 }
822
823 static int parse_bind_val(const char *val, int *type)
824 {
825         int i;
826
827         *type = IN_BINDTYPE_NONE;
828         if (val[0] == 0)
829                 return 0;
830         
831         if (strncasecmp(val, "player", 6) == 0)
832         {
833                 int player, shift = 0;
834                 player = atoi(val + 6) - 1;
835
836                 if ((unsigned int)player > 1)
837                         return -1;
838                 if (player == 1)
839                         shift = 16;
840
841                 *type = IN_BINDTYPE_PLAYER12;
842                 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
843                         if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
844                                 return me_ctrl_actions[i].mask << shift;
845                 }
846         }
847         for (i = 0; emuctrl_actions[i].name != NULL; i++) {
848                 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
849                         *type = IN_BINDTYPE_EMU;
850                         return emuctrl_actions[i].mask;
851                 }
852         }
853
854         return -1;
855 }
856
857 static void keys_load_all(const char *cfg)
858 {
859         char dev[256], key[128], *act;
860         const char *p;
861         int bind, bindtype;
862         int dev_id;
863
864         p = cfg;
865         while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
866                 p += 10;
867
868                 get_line(dev, sizeof(dev), p);
869                 dev_id = in_config_parse_dev(dev);
870                 if (dev_id < 0) {
871                         printf("input: can't handle dev: %s\n", dev);
872                         continue;
873                 }
874
875                 in_unbind_all(dev_id, -1, -1);
876                 while ((p = strstr(p, "bind"))) {
877                         if (strncmp(p, "binddev = ", 10) == 0)
878                                 break;
879
880                         p += 4;
881                         if (*p != ' ') {
882                                 printf("input: parse error: %16s..\n", p);
883                                 continue;
884                         }
885
886                         get_line(key, sizeof(key), p);
887                         act = strchr(key, '=');
888                         if (act == NULL) {
889                                 printf("parse failed: %16s..\n", p);
890                                 continue;
891                         }
892                         *act = 0;
893                         act++;
894                         mystrip(key);
895                         mystrip(act);
896
897                         bind = parse_bind_val(act, &bindtype);
898                         if (bind != -1 && bind != 0) {
899                                 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
900                                 in_config_bind_key(dev_id, key, bind, bindtype);
901                         }
902                         else
903                                 lprintf("config: unhandled action \"%s\"\n", act);
904                 }
905         }
906         in_clean_binds();
907 }
908
909 static int key_config_loop_wrap(int id, int keys)
910 {
911         switch (id) {
912                 case MA_CTRL_PLAYER1:
913                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
914                         break;
915                 case MA_CTRL_PLAYER2:
916                         key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
917                         break;
918                 case MA_CTRL_EMU:
919                         key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
920                         break;
921                 default:
922                         break;
923         }
924         return 0;
925 }
926
927 static const char *mgn_dev_name(int id, int *offs)
928 {
929         const char *name = NULL;
930         static int it = 0;
931
932         if (id == MA_CTRL_DEV_FIRST)
933                 it = 0;
934
935         for (; it < IN_MAX_DEVS; it++) {
936                 name = in_get_dev_name(it, 1, 1);
937                 if (name != NULL)
938                         break;
939         }
940
941         it++;
942         return name;
943 }
944
945 static const char *mgn_saveloadcfg(int id, int *offs)
946 {
947         return "";
948 }
949
950 static int mh_savecfg(int id, int keys)
951 {
952         if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
953                 me_update_msg("config saved");
954         else
955                 me_update_msg("failed to write config");
956
957         return 1;
958 }
959
960 static int mh_input_rescan(int id, int keys)
961 {
962         //menu_sync_config();
963         plat_rescan_inputs();
964         me_update_msg("rescan complete.");
965
966         return 0;
967 }
968
969 static const char *men_in_type_sel[] = {
970         "Standard (SCPH-1080)",
971         "Analog (SCPH-1150)",
972         "GunCon",
973         NULL
974 };
975 static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
976 static const char h_notsgun[] =  "Don't trigger (shoot) when touching screen in gun games.";
977
978 static menu_entry e_menu_keyconfig[] =
979 {
980         mee_handler_id("Player 1",              MA_CTRL_PLAYER1,    key_config_loop_wrap),
981         mee_handler_id("Player 2",              MA_CTRL_PLAYER2,    key_config_loop_wrap),
982         mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU,        key_config_loop_wrap),
983         mee_label     (""),
984         mee_enum      ("Port 1 device",     0, in_type_sel1,    men_in_type_sel),
985         mee_enum      ("Port 2 device",     0, in_type_sel2,    men_in_type_sel),
986         mee_onoff_h   ("Nubs as buttons",   MA_CTRL_NUBS_BTNS,  in_evdev_allow_abs_only, 1, h_nub_btns),
987         mee_range     ("Analog deadzone",   MA_CTRL_DEADZONE,   analog_deadzone, 1, 99),
988         mee_onoff_h   ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
989         mee_cust_nosave("Save global config",       MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
990         mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
991         mee_handler   ("Rescan devices",  mh_input_rescan),
992         mee_label     (""),
993         mee_label     ("Input devices:"),
994         mee_label_mk  (MA_CTRL_DEV_FIRST, mgn_dev_name),
995         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
996         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
997         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
998         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
999         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1000         mee_label_mk  (MA_CTRL_DEV_NEXT,  mgn_dev_name),
1001         mee_end,
1002 };
1003
1004 static int menu_loop_keyconfig(int id, int keys)
1005 {
1006         static int sel = 0;
1007
1008 //      me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1009         me_loop(e_menu_keyconfig, &sel);
1010         return 0;
1011 }
1012
1013 // ------------ gfx options menu ------------
1014
1015 static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
1016 static const char h_cscaler[]   = "Displays the scaler layer, you can resize it\n"
1017                                   "using d-pad or move it using R+d-pad";
1018 static const char *men_dummy[] = { NULL };
1019
1020 static int menu_loop_cscaler(int id, int keys)
1021 {
1022         unsigned int inp;
1023
1024         scaling = SCALE_CUSTOM;
1025
1026         omap_enable_layer(1);
1027
1028         for (;;)
1029         {
1030                 menu_draw_begin(0);
1031                 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1032                 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1033                 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
1034                 menu_draw_end();
1035
1036                 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
1037                 if (inp & PBTN_UP)    g_layer_y--;
1038                 if (inp & PBTN_DOWN)  g_layer_y++;
1039                 if (inp & PBTN_LEFT)  g_layer_x--;
1040                 if (inp & PBTN_RIGHT) g_layer_x++;
1041                 if (!(inp & PBTN_R)) {
1042                         if (inp & PBTN_UP)    g_layer_h += 2;
1043                         if (inp & PBTN_DOWN)  g_layer_h -= 2;
1044                         if (inp & PBTN_LEFT)  g_layer_w += 2;
1045                         if (inp & PBTN_RIGHT) g_layer_w -= 2;
1046                 }
1047                 if (inp & (PBTN_MOK|PBTN_MBACK))
1048                         break;
1049
1050                 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1051                         if (g_layer_x < 0)   g_layer_x = 0;
1052                         if (g_layer_x > 640) g_layer_x = 640;
1053                         if (g_layer_y < 0)   g_layer_y = 0;
1054                         if (g_layer_y > 420) g_layer_y = 420;
1055                         if (g_layer_w < 160) g_layer_w = 160;
1056                         if (g_layer_h < 60)  g_layer_h = 60;
1057                         if (g_layer_x + g_layer_w > 800)
1058                                 g_layer_w = 800 - g_layer_x;
1059                         if (g_layer_y + g_layer_h > 480)
1060                                 g_layer_h = 480 - g_layer_y;
1061                         omap_enable_layer(1);
1062                 }
1063         }
1064
1065         omap_enable_layer(0);
1066
1067         return 0;
1068 }
1069
1070 static menu_entry e_menu_gfx_options[] =
1071 {
1072         mee_enum      ("Scaler",                   0, scaling, men_scaler),
1073         mee_enum      ("Filter",                   MA_OPT_FILTERING, filter, men_dummy),
1074 //      mee_onoff     ("Vsync",                    0, vsync, 1),
1075         mee_cust_h    ("Setup custom scaler",      0, menu_loop_cscaler, NULL, h_cscaler),
1076         mee_end,
1077 };
1078
1079 static int menu_loop_gfx_options(int id, int keys)
1080 {
1081         static int sel = 0;
1082
1083         me_loop(e_menu_gfx_options, &sel);
1084
1085         return 0;
1086 }
1087
1088 // ------------ bios/plugins ------------
1089
1090 static menu_entry e_menu_plugin_gpu_unai[] =
1091 {
1092         mee_onoff     ("Abe's Odyssey hack",         0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1093         mee_onoff     ("Disable lighting",           0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1094         mee_onoff     ("Disable blending",           0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1095         mee_end,
1096 };
1097
1098 static int menu_loop_plugin_gpu_unai(int id, int keys)
1099 {
1100         int sel = 0;
1101         me_loop(e_menu_plugin_gpu_unai, &sel);
1102         return 0;
1103 }
1104
1105 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1106 static const char h_gpu_0[]            = "Needed for Chrono Cross";
1107 static const char h_gpu_1[]            = "Capcom fighting games";
1108 static const char h_gpu_2[]            = "Black screens in Lunar";
1109 static const char h_gpu_3[]            = "Compatibility mode";
1110 static const char h_gpu_6[]            = "Pandemonium 2";
1111 static const char h_gpu_7[]            = "Skip every second frame";
1112 static const char h_gpu_8[]            = "Needed by Dark Forces";
1113 static const char h_gpu_9[]            = "better g-colors, worse textures";
1114 static const char h_gpu_10[]           = "Toggle busy flags after drawing";
1115
1116 static menu_entry e_menu_plugin_gpu_peops[] =
1117 {
1118         mee_enum      ("Dithering",                  0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
1119         mee_onoff_h   ("Odd/even bit hack",          0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
1120         mee_onoff_h   ("Expand screen width",        0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1121         mee_onoff_h   ("Ignore brightness color",    0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1122         mee_onoff_h   ("Disable coordinate check",   0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1123         mee_onoff_h   ("Lazy screen update",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
1124         mee_onoff_h   ("Old frame skipping",         0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
1125         mee_onoff_h   ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1126         mee_onoff_h   ("Draw quads with triangles",  0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1127         mee_onoff_h   ("Fake 'gpu busy' states",     0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
1128         mee_end,
1129 };
1130
1131 static int menu_loop_plugin_gpu_peops(int id, int keys)
1132 {
1133         static int sel = 0;
1134         me_loop(e_menu_plugin_gpu_peops, &sel);
1135         return 0;
1136 }
1137
1138 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1139 static const char h_spu_volboost[]  = "Large values cause distortion";
1140 static const char h_spu_irq_wait[]  = "Wait for CPU (recommended set to ON)";
1141 static const char h_spu_thread[]    = "Run sound emulation in main thread (recommended)";
1142
1143 static menu_entry e_menu_plugin_spu[] =
1144 {
1145         mee_range_h   ("Volume boost",              0, volume_boost, -5, 30, h_spu_volboost),
1146         mee_onoff     ("Reverb",                    0, iUseReverb, 2),
1147         mee_enum      ("Interpolation",             0, iUseInterpolation, men_spu_interp),
1148         mee_onoff     ("Adjust XA pitch",           0, iXAPitch, 1),
1149         mee_onoff_h   ("SPU IRQ Wait",              0, iSPUIRQWait, 1, h_spu_irq_wait),
1150         mee_onoff_h   ("Sound in main thread",      0, iUseTimer, 2, h_spu_thread),
1151         mee_end,
1152 };
1153
1154 static int menu_loop_plugin_spu(int id, int keys)
1155 {
1156         static int sel = 0;
1157         me_loop(e_menu_plugin_spu, &sel);
1158         return 0;
1159 }
1160
1161 static const char h_bios[]       = "HLE is simulated BIOS. BIOS selection is saved in\n"
1162                                    "savestates and can't be changed there. Must save\n"
1163                                    "config and reload the game for change to take effect";
1164 static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1165                                    "for plugin change to take effect";
1166 static const char h_gpu_peops[]  = "Configure P.E.Op.S. SoftGL Driver V1.17";
1167 static const char h_gpu_unai[]   = "Configure Unai/PCSX4ALL Team GPU plugin";
1168 static const char h_spu[]        = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1169
1170 static menu_entry e_menu_plugin_options[] =
1171 {
1172         mee_enum_h    ("BIOS",                          0, bios_sel, bioses, h_bios),
1173         mee_enum_h    ("GPU plugin",                    0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1174         mee_enum_h    ("SPU plugin",                    0, spu_plugsel, spu_plugins, h_plugin_xpu),
1175         mee_handler_h ("Configure gpu_peops plugin",    menu_loop_plugin_gpu_peops, h_gpu_peops),
1176         mee_handler_h ("Configure PCSX4ALL GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
1177         mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1178         mee_end,
1179 };
1180
1181 static menu_entry e_menu_main2[];
1182
1183 static int menu_loop_plugin_options(int id, int keys)
1184 {
1185         static int sel = 0;
1186         me_loop(e_menu_plugin_options, &sel);
1187
1188         // sync BIOS/plugins
1189         snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1190         snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1191         snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1192         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1193
1194         return 0;
1195 }
1196
1197 // ------------ adv options menu ------------
1198
1199 static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
1200 static const char h_cfg_cpul[]   = "Shows CPU usage in %";
1201 static const char h_cfg_spu[]    = "Shows active SPU channels\n"
1202                                    "(green: normal, red: fmod, blue: noise)";
1203 static const char h_cfg_fl[]     = "Frame Limiter keeps the game from running too fast";
1204 static const char h_cfg_xa[]     = "Disables XA sound, which can sometimes improve performance";
1205 static const char h_cfg_cdda[]   = "Disable CD Audio for a performance boost\n"
1206                                    "(proper .cue/.bin dump is needed otherwise)";
1207 static const char h_cfg_sio[]    = "You should not need this, breaks games";
1208 static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1209 static const char h_cfg_rcnt1[]  = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1210                                    "(timing hack, breaks other games)";
1211 static const char h_cfg_rcnt2[]  = "InuYasha Sengoku Battle Fix\n"
1212                                    "(timing hack, breaks other games)";
1213 static const char h_cfg_cdrr[]   = "Compatibility tweak (fixes Team Buddies, maybe more)\n"
1214                                    "(CD timing hack, breaks FMVs)";
1215 static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n"
1216                                    "(may break games, must reload game to take effect)";
1217 static const char h_cfg_nodrc[]  = "Disable dynamic recompiler and use interpreter\n"
1218                                    "Might be useful to overcome some dynarec bugs";
1219
1220 static menu_entry e_menu_adv_options[] =
1221 {
1222         mee_onoff_h   ("Show CPU load",          0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1223         mee_onoff_h   ("Show SPU channels",      0, g_opts, OPT_SHOWSPU, h_cfg_spu),
1224         mee_onoff_h   ("Disable Frame Limiter",  0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1225         mee_onoff_h   ("Disable XA Decoding",    0, Config.Xa, 1, h_cfg_xa),
1226         mee_onoff_h   ("Disable CD Audio",       0, Config.Cdda, 1, h_cfg_cdda),
1227         mee_onoff_h   ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1228         mee_onoff_h   ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1229         mee_onoff_h   ("Rootcounter hack",       0, Config.RCntFix, 1, h_cfg_rcnt1),
1230         mee_onoff_h   ("Rootcounter hack 2",     0, Config.VSyncWA, 1, h_cfg_rcnt2),
1231         mee_enum_h    ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
1232         mee_range_h   ("PSX CPU clock, %%",      0, psx_clock, 1, 500, h_cfg_psxclk),
1233         mee_onoff_h   ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1234         mee_end,
1235 };
1236
1237 static int menu_loop_adv_options(int id, int keys)
1238 {
1239         static int sel = 0;
1240         me_loop(e_menu_adv_options, &sel);
1241         return 0;
1242 }
1243
1244 // ------------ options menu ------------
1245
1246 static int mh_restore_defaults(int id, int keys)
1247 {
1248         menu_set_defconfig();
1249         me_update_msg("defaults restored");
1250         return 1;
1251 }
1252
1253 static const char *men_region[]       = { "Auto", "NTSC", "PAL", NULL };
1254 static const char *men_frameskip[]    = { "Auto", "Off", "1", NULL };
1255 /*
1256 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1257 static const char h_confirm_save[]    = "Ask for confirmation when overwriting save,\n"
1258                                         "loading state or both";
1259 */
1260 static const char h_restore_def[]     = "Switches back to default / recommended\n"
1261                                         "configuration";
1262 static const char h_frameskip[]       = "Warning: frameskip sometimes causes glitches\n";
1263
1264 static menu_entry e_menu_options[] =
1265 {
1266 //      mee_range     ("Save slot",                0, state_slot, 0, 9),
1267 //      mee_enum_h    ("Confirm savestate",        0, dummy, men_confirm_save, h_confirm_save),
1268         mee_enum_h    ("Frameskip",                0, frameskip, men_frameskip, h_frameskip),
1269         mee_onoff     ("Show FPS",                 0, g_opts, OPT_SHOWFPS),
1270         mee_enum      ("Region",                   0, region, men_region),
1271         mee_range     ("CPU clock",                MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1272         mee_handler_id("[Display]",                MA_OPT_DISP_OPTS, menu_loop_gfx_options),
1273         mee_handler   ("[BIOS/Plugins]",           menu_loop_plugin_options),
1274         mee_handler   ("[Advanced]",               menu_loop_adv_options),
1275         mee_cust_nosave("Save global config",      MA_OPT_SAVECFG,      mh_savecfg, mgn_saveloadcfg),
1276         mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1277         mee_handler_h ("Restore default config",   mh_restore_defaults, h_restore_def),
1278         mee_end,
1279 };
1280
1281 static int menu_loop_options(int id, int keys)
1282 {
1283         static int sel = 0;
1284         int i;
1285
1286         i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1287         e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1288         me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1289
1290         me_loop(e_menu_options, &sel);
1291
1292         return 0;
1293 }
1294
1295 // ------------ debug menu ------------
1296
1297 static void draw_frame_debug(GPUFreeze_t *gpuf)
1298 {
1299         int w = min(g_menuscreen_w, 1024);
1300         int h = min(g_menuscreen_h, 512);
1301         u16 *d = g_menuscreen_ptr;
1302         u16 *s = (u16 *)gpuf->psxVRam;
1303         char buff[64];
1304         int ty = 1;
1305
1306         gpuf->ulFreezeVersion = 1;
1307         if (GPU_freeze != NULL)
1308                 GPU_freeze(1, gpuf);
1309
1310         for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1311                 bgr555_to_rgb565(d, s, w * 2);
1312
1313         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1314         snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1315         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1316         snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1317         smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1318 }
1319
1320 static void debug_menu_loop(void)
1321 {
1322         GPUFreeze_t *gpuf;
1323         int inp;
1324
1325         gpuf = malloc(sizeof(*gpuf));
1326         if (gpuf == NULL)
1327                 return;
1328
1329         while (1)
1330         {
1331                 menu_draw_begin(0);
1332                 draw_frame_debug(gpuf);
1333                 menu_draw_end();
1334
1335                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1336                                         PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1337                 if (inp & PBTN_MBACK)
1338                         break;
1339         }
1340
1341         free(gpuf);
1342 }
1343
1344 // --------- memcard manager ---------
1345
1346 static void draw_mc_icon(int dx, int dy, const u16 *s)
1347 {
1348         u16 *d;
1349         int x, y, l, p;
1350         
1351         d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1352
1353         for (y = 0; y < 16; y++, s += 16) {
1354                 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1355                         for (x = 0; x < 16; x++) {
1356                                 p = s[x];
1357                                 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1358                                         | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1359                         }
1360                 }
1361         }
1362 }
1363
1364 static void draw_mc_bg(void)
1365 {
1366         McdBlock *blocks1, *blocks2;
1367         int maxicons = 15;
1368         int i, y, row2;
1369
1370         blocks1 = malloc(15 * sizeof(blocks1[0]));
1371         blocks2 = malloc(15 * sizeof(blocks1[0]));
1372         if (blocks1 == NULL || blocks2 == NULL)
1373                 goto out;
1374
1375         for (i = 0; i < 15; i++) {
1376                 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1377                 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1378         }
1379
1380         menu_draw_begin(1);
1381
1382         memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1383
1384         y = g_menuscreen_h / 2 - 15 * 32 / 2;
1385         if (y < 0) {
1386                 // doesn't fit..
1387                 y = 0;
1388                 maxicons = g_menuscreen_h / 32;
1389         }
1390
1391         row2 = g_menuscreen_w / 2;
1392         for (i = 0; i < maxicons; i++) {
1393                 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1394                 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1395
1396                 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1397                 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1398         }
1399
1400         menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1401
1402         menu_draw_end();
1403 out:
1404         free(blocks1);
1405         free(blocks2);
1406 }
1407
1408 static void handle_memcard_sel(void)
1409 {
1410         Config.Mcd1[0] = 0;
1411         if (memcard1_sel != 0)
1412                 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1413         Config.Mcd2[0] = 0;
1414         if (memcard2_sel != 0)
1415                 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1416         LoadMcds(Config.Mcd1, Config.Mcd2);
1417         draw_mc_bg();
1418 }
1419
1420 static menu_entry e_memcard_options[] =
1421 {
1422         mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1423         mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1424         mee_end,
1425 };
1426
1427 static int menu_loop_memcards(int id, int keys)
1428 {
1429         static int sel = 0;
1430         char *p;
1431         int i;
1432
1433         memcard1_sel = memcard2_sel = 0;
1434         p = strrchr(Config.Mcd1, '/');
1435         if (p != NULL)
1436                 for (i = 0; memcards[i] != NULL; i++)
1437                         if (strcmp(p + 1, memcards[i]) == 0)
1438                                 { memcard1_sel = i; break; }
1439         p = strrchr(Config.Mcd2, '/');
1440         if (p != NULL)
1441                 for (i = 0; memcards[i] != NULL; i++)
1442                         if (strcmp(p + 1, memcards[i]) == 0)
1443                                 { memcard2_sel = i; break; }
1444
1445         me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1446
1447         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1448
1449         return 0;
1450 }
1451
1452 // --------- main menu help ----------
1453
1454 static void menu_bios_warn(void)
1455 {
1456         int inp;
1457         static const char msg[] =
1458                 "You don't seem to have copied any BIOS files to\n"
1459                 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1460                 "While many games work fine with fake (HLE) BIOS,\n"
1461                 "others (like MGS and FF8) require BIOS to work.\n"
1462                 "After copying the file, you'll also need to\n"
1463                 "select it in the emu's options->[BIOS/Plugins]\n\n"
1464                 "The file is usually named SCPH1001.BIN, but\n"
1465                 "other not compressed files can be used too.\n\n"
1466                 "Press (B) or (X) to continue";
1467
1468         while (1)
1469         {
1470                 draw_menu_message(msg, NULL);
1471
1472                 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1473                 if (inp & (PBTN_MBACK|PBTN_MOK))
1474                         return;
1475         }
1476 }
1477
1478 // ------------ main menu ------------
1479
1480 void OnFile_Exit();
1481
1482 static void draw_frame_main(void)
1483 {
1484         struct tm *tmp;
1485         time_t ltime;
1486         char ltime_s[16];
1487         char buff[64];
1488
1489         if (CdromId[0] != 0) {
1490                 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1491                          get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1492                          Config.HLE ? "HLE" : "BIOS");
1493                 smalltext_out16(4, 1, buff, 0x105f);
1494         }
1495
1496         if (ready_to_go) {
1497                 ltime = time(NULL);
1498                 tmp = localtime(&ltime);
1499                 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
1500                 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, plat_get_bat_capacity());
1501                 smalltext_out16(4, 1 + me_sfont_h, buff, 0x105f);
1502         }
1503 }
1504
1505 static void draw_frame_credits(void)
1506 {
1507         smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1508 }
1509
1510 static const char credits_text[] = 
1511         "PCSX-ReARMed\n\n"
1512         "(C) 1999-2003 PCSX Team\n"
1513         "(C) 2005-2009 PCSX-df Team\n"
1514         "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1515         "GPU and SPU code by Pete Bernert\n"
1516         "  and the P.E.Op.S. team\n"
1517         "ARM recompiler (C) 2009-2011 Ari64\n"
1518         "PCSX4ALL plugins by PCSX4ALL team\n"
1519         "  Chui, Franxis, Unai\n\n"
1520         "integration, optimization and\n"
1521         "  frontend (C) 2010-2011 notaz\n";
1522
1523 static int reset_game(void)
1524 {
1525         // sanity check
1526         if (bios_sel == 0 && !Config.HLE)
1527                 return -1;
1528
1529         ClosePlugins();
1530         OpenPlugins();
1531         SysReset();
1532         if (CheckCdrom() != -1) {
1533                 LoadCdrom();
1534         }
1535         return 0;
1536 }
1537
1538 static int reload_plugins(const char *cdimg)
1539 {
1540         pl_vout_buf = NULL;
1541
1542         ClosePlugins();
1543
1544         set_cd_image(cdimg);
1545         LoadPlugins();
1546         pcnt_hook_plugins();
1547         NetOpened = 0;
1548         if (OpenPlugins() == -1) {
1549                 me_update_msg("failed to open plugins");
1550                 return -1;
1551         }
1552         plugin_call_rearmed_cbs();
1553
1554         CdromId[0] = '\0';
1555         CdromLabel[0] = '\0';
1556
1557         return 0;
1558 }
1559
1560 static int run_bios(void)
1561 {
1562         if (bios_sel == 0)
1563                 return -1;
1564
1565         ready_to_go = 0;
1566         if (reload_plugins(NULL) != 0)
1567                 return -1;
1568         SysReset();
1569
1570         ready_to_go = 1;
1571         return 0;
1572 }
1573
1574 static int run_exe(void)
1575 {
1576         const char *fname;
1577
1578         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1579         if (fname == NULL)
1580                 return -1;
1581
1582         ready_to_go = 0;
1583         if (reload_plugins(NULL) != 0)
1584                 return -1;
1585
1586         SysReset();
1587         if (Load(fname) != 0) {
1588                 me_update_msg("exe load failed, bad file?");
1589                 printf("meh\n");
1590                 return -1;
1591         }
1592
1593         ready_to_go = 1;
1594         return 0;
1595 }
1596
1597 static int run_cd_image(const char *fname)
1598 {
1599         ready_to_go = 0;
1600         reload_plugins(fname);
1601
1602         // always autodetect, menu_sync_config will override as needed
1603         Config.PsxAuto = 1;
1604
1605         if (CheckCdrom() == -1) {
1606                 // Only check the CD if we are starting the console with a CD
1607                 ClosePlugins();
1608                 me_update_msg("unsupported/invalid CD image");
1609                 return -1;
1610         }
1611
1612         SysReset();
1613
1614         // Read main executable directly from CDRom and start it
1615         if (LoadCdrom() == -1) {
1616                 ClosePlugins();
1617                 me_update_msg("failed to load CD image");
1618                 return -1;
1619         }
1620
1621         ready_to_go = 1;
1622         return 0;
1623 }
1624
1625 static int romsel_run(void)
1626 {
1627         int prev_gpu, prev_spu;
1628         const char *fname;
1629
1630         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1631         if (fname == NULL)
1632                 return -1;
1633
1634         printf("selected file: %s\n", fname);
1635
1636         new_dynarec_clear_full();
1637
1638         if (run_cd_image(fname) != 0)
1639                 return -1;
1640
1641         prev_gpu = gpu_plugsel;
1642         prev_spu = spu_plugsel;
1643         if (menu_load_config(1) != 0)
1644                 menu_load_config(0);
1645
1646         // check for plugin changes, have to repeat
1647         // loading if game config changed plugins to reload them
1648         if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1649                 printf("plugin change detected, reloading plugins..\n");
1650                 if (run_cd_image(fname) != 0)
1651                         return -1;
1652         }
1653
1654         strcpy(last_selected_fname, rom_fname_reload);
1655         return 0;
1656 }
1657
1658 static int swap_cd_image(void)
1659 {
1660         char *fname;
1661
1662         fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1663         if (fname == NULL)
1664                 return -1;
1665
1666         printf("selected file: %s\n", fname);
1667
1668         CdromId[0] = '\0';
1669         CdromLabel[0] = '\0';
1670
1671         set_cd_image(fname);
1672         if (ReloadCdromPlugin() < 0) {
1673                 me_update_msg("failed to load cdr plugin");
1674                 return -1;
1675         }
1676         if (CDR_open() < 0) {
1677                 me_update_msg("failed to open cdr plugin");
1678                 return -1;
1679         }
1680
1681         SetCdOpenCaseTime(time(NULL) + 2);
1682         LidInterrupt();
1683
1684         strcpy(last_selected_fname, rom_fname_reload);
1685         return 0;
1686 }
1687
1688 static int main_menu_handler(int id, int keys)
1689 {
1690         switch (id)
1691         {
1692         case MA_MAIN_RESUME_GAME:
1693                 if (ready_to_go)
1694                         return 1;
1695                 break;
1696         case MA_MAIN_SAVE_STATE:
1697                 if (ready_to_go)
1698                         return menu_loop_savestate(0);
1699                 break;
1700         case MA_MAIN_LOAD_STATE:
1701                 if (ready_to_go)
1702                         return menu_loop_savestate(1);
1703                 break;
1704         case MA_MAIN_RESET_GAME:
1705                 if (ready_to_go && reset_game() == 0)
1706                         return 1;
1707                 break;
1708         case MA_MAIN_LOAD_ROM:
1709                 if (romsel_run() == 0)
1710                         return 1;
1711                 break;
1712         case MA_MAIN_SWAP_CD:
1713                 if (swap_cd_image() == 0)
1714                         return 1;
1715                 break;
1716         case MA_MAIN_RUN_BIOS:
1717                 if (run_bios() == 0)
1718                         return 1;
1719                 break;
1720         case MA_MAIN_RUN_EXE:
1721                 if (run_exe() == 0)
1722                         return 1;
1723                 break;
1724         case MA_MAIN_CREDITS:
1725                 draw_menu_message(credits_text, draw_frame_credits);
1726                 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1727                 break;
1728         case MA_MAIN_EXIT:
1729                 OnFile_Exit();
1730                 break;
1731         default:
1732                 lprintf("%s: something unknown selected\n", __FUNCTION__);
1733                 break;
1734         }
1735
1736         return 0;
1737 }
1738
1739 static menu_entry e_menu_main2[] =
1740 {
1741         mee_handler_id("Change CD image",    MA_MAIN_SWAP_CD,     main_menu_handler),
1742         mee_handler_id("Run BIOS",           MA_MAIN_RUN_BIOS,    main_menu_handler),
1743         mee_handler_id("Run EXE",            MA_MAIN_RUN_EXE,     main_menu_handler),
1744         mee_handler   ("Memcard manager",    menu_loop_memcards),
1745         mee_end,
1746 };
1747
1748 static int main_menu2_handler(int id, int keys)
1749 {
1750         static int sel = 0;
1751
1752         me_enable(e_menu_main2, MA_MAIN_SWAP_CD,  ready_to_go);
1753         me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1754
1755         return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1756 }
1757
1758 static const char h_extra[] = "Change CD, manage memcards..\n";
1759
1760 static menu_entry e_menu_main[] =
1761 {
1762         mee_label     (""),
1763         mee_label     (""),
1764         mee_handler_id("Resume game",        MA_MAIN_RESUME_GAME, main_menu_handler),
1765         mee_handler_id("Save State",         MA_MAIN_SAVE_STATE,  main_menu_handler),
1766         mee_handler_id("Load State",         MA_MAIN_LOAD_STATE,  main_menu_handler),
1767         mee_handler_id("Reset game",         MA_MAIN_RESET_GAME,  main_menu_handler),
1768         mee_handler_id("Load CD image",      MA_MAIN_LOAD_ROM,    main_menu_handler),
1769         mee_handler   ("Options",            menu_loop_options),
1770         mee_handler   ("Controls",           menu_loop_keyconfig),
1771         mee_handler_h ("Extra stuff",        main_menu2_handler,  h_extra),
1772         mee_handler_id("Credits",            MA_MAIN_CREDITS,     main_menu_handler),
1773         mee_handler_id("Exit",               MA_MAIN_EXIT,        main_menu_handler),
1774         mee_end,
1775 };
1776
1777 // ----------------------------
1778
1779 static void menu_leave_emu(void);
1780
1781 void menu_loop(void)
1782 {
1783         static int sel = 0;
1784
1785         menu_leave_emu();
1786
1787         if (bioses[1] == NULL && !warned_about_bios) {
1788                 menu_bios_warn();
1789                 warned_about_bios = 1;
1790         }
1791
1792         me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1793         me_enable(e_menu_main, MA_MAIN_SAVE_STATE,  ready_to_go && CdromId[0]);
1794         me_enable(e_menu_main, MA_MAIN_LOAD_STATE,  ready_to_go && CdromId[0]);
1795         me_enable(e_menu_main, MA_MAIN_RESET_GAME,  ready_to_go);
1796
1797         in_set_config_int(0, IN_CFG_BLOCKING, 1);
1798
1799         do {
1800                 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
1801         } while (!ready_to_go);
1802
1803         /* wait until menu, ok, back is released */
1804         while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1805                 ;
1806
1807         in_set_config_int(0, IN_CFG_BLOCKING, 0);
1808
1809         menu_prepare_emu();
1810 }
1811
1812 static int qsort_strcmp(const void *p1, const void *p2)
1813 {
1814         char * const *s1 = (char * const *)p1;
1815         char * const *s2 = (char * const *)p2;
1816         return strcasecmp(*s1, *s2);
1817 }
1818
1819 static void scan_bios_plugins(void)
1820 {
1821         char fname[MAXPATHLEN];
1822         struct dirent *ent;
1823         int bios_i, gpu_i, spu_i, mc_i;
1824         char *p;
1825         DIR *dir;
1826
1827         bioses[0] = "HLE";
1828         gpu_plugins[0] = "builtin_gpu";
1829         spu_plugins[0] = "builtin_spu";
1830         memcards[0] = "(none)";
1831         bios_i = gpu_i = spu_i = mc_i = 1;
1832
1833         snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1834         dir = opendir(fname);
1835         if (dir == NULL) {
1836                 perror("scan_bios_plugins bios opendir");
1837                 goto do_plugins;
1838         }
1839
1840         while (1) {
1841                 struct stat st;
1842
1843                 errno = 0;
1844                 ent = readdir(dir);
1845                 if (ent == NULL) {
1846                         if (errno != 0)
1847                                 perror("readdir");
1848                         break;
1849                 }
1850
1851                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1852                         continue;
1853
1854                 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1855                 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1856                         printf("bad BIOS file: %s\n", ent->d_name);
1857                         continue;
1858                 }
1859
1860                 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1861                         bioses[bios_i++] = strdup(ent->d_name);
1862                         continue;
1863                 }
1864
1865                 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1866         }
1867
1868         closedir(dir);
1869
1870 do_plugins:
1871         snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1872         dir = opendir(fname);
1873         if (dir == NULL) {
1874                 perror("scan_bios_plugins plugins opendir");
1875                 goto do_memcards;
1876         }
1877
1878         while (1) {
1879                 void *h, *tmp;
1880
1881                 errno = 0;
1882                 ent = readdir(dir);
1883                 if (ent == NULL) {
1884                         if (errno != 0)
1885                                 perror("readdir");
1886                         break;
1887                 }
1888                 p = strstr(ent->d_name, ".so");
1889                 if (p == NULL)
1890                         continue;
1891
1892                 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1893                 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1894                 if (h == NULL) {
1895                         fprintf(stderr, "%s\n", dlerror());
1896                         continue;
1897                 }
1898
1899                 // now what do we have here?
1900                 tmp = dlsym(h, "GPUinit");
1901                 if (tmp) {
1902                         dlclose(h);
1903                         if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1904                                 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1905                         continue;
1906                 }
1907
1908                 tmp = dlsym(h, "SPUinit");
1909                 if (tmp) {
1910                         dlclose(h);
1911                         if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1912                                 spu_plugins[spu_i++] = strdup(ent->d_name);
1913                         continue;
1914                 }
1915
1916                 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1917                 dlclose(h);
1918         }
1919
1920         closedir(dir);
1921
1922 do_memcards:
1923         dir = opendir("." MEMCARD_DIR);
1924         if (dir == NULL) {
1925                 perror("scan_bios_plugins memcards opendir");
1926                 return;
1927         }
1928
1929         while (1) {
1930                 struct stat st;
1931
1932                 errno = 0;
1933                 ent = readdir(dir);
1934                 if (ent == NULL) {
1935                         if (errno != 0)
1936                                 perror("readdir");
1937                         break;
1938                 }
1939
1940                 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1941                         continue;
1942
1943                 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
1944                 if (stat(fname, &st) != 0) {
1945                         printf("bad memcard file: %s\n", ent->d_name);
1946                         continue;
1947                 }
1948
1949                 if (mc_i < ARRAY_SIZE(memcards) - 1) {
1950                         memcards[mc_i++] = strdup(ent->d_name);
1951                         continue;
1952                 }
1953
1954                 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
1955         }
1956
1957         if (mc_i > 2)
1958                 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
1959
1960         closedir(dir);
1961 }
1962
1963 void menu_init(void)
1964 {
1965         char buff[MAXPATHLEN];
1966
1967         strcpy(last_selected_fname, "/media");
1968
1969         scan_bios_plugins();
1970         pnd_menu_init();
1971         menu_init_common();
1972
1973         menu_set_defconfig();
1974         menu_load_config(0);
1975         last_psx_w = 320;
1976         last_psx_h = 240;
1977         last_psx_bpp = 16;
1978
1979         g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1980         if (g_menubg_src_ptr == NULL)
1981                 exit(1);
1982         emu_make_path(buff, "skin/background.png", sizeof(buff));
1983         readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1984
1985 #ifndef __ARM_ARCH_7A__ /* XXX */
1986         me_enable(e_menu_options, MA_OPT_DISP_OPTS, 0);
1987         me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
1988 #else
1989         me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
1990 #endif
1991 }
1992
1993 void menu_notify_mode_change(int w, int h, int bpp)
1994 {
1995         float mult;
1996         int imult;
1997
1998         last_psx_w = w;
1999         last_psx_h = h;
2000         last_psx_bpp = bpp;
2001
2002         switch (scaling) {
2003         case SCALE_1_1:
2004                 g_layer_w = w; g_layer_h = h;
2005                 break;
2006
2007         case SCALE_4_3:
2008                 mult = 240.0f / (float)h * 4.0f / 3.0f;
2009                 if (h > 256)
2010                         mult *= 2.0f;
2011                 g_layer_w = mult * (float)g_menuscreen_h;
2012                 g_layer_h = g_menuscreen_h;
2013                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2014                 break;
2015
2016         case SCALE_4_3v2:
2017                 // 4:3 that prefers integer scaling
2018                 imult = g_menuscreen_h / h;
2019                 g_layer_w = w * imult;
2020                 g_layer_h = h * imult;
2021                 mult = (float)g_layer_w / (float)g_layer_h;
2022                 if (mult < 1.25f || mult > 1.666f)
2023                         g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2024                 printf("  -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2025                 break;
2026
2027         case SCALE_FULLSCREEN:
2028                 g_layer_w = g_menuscreen_w;
2029                 g_layer_h = g_menuscreen_h;
2030                 break;
2031
2032         default:
2033                 break;
2034         }
2035
2036         g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2037         g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2038         if (g_layer_x < 0) g_layer_x = 0;
2039         if (g_layer_y < 0) g_layer_y = 0;
2040         if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2041         if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
2042 }
2043
2044 static void menu_leave_emu(void)
2045 {
2046         if (GPU_close != NULL) {
2047                 int ret = GPU_close();
2048                 if (ret)
2049                         fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2050         }
2051
2052         plat_video_menu_enter(ready_to_go);
2053
2054         memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
2055         if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
2056                 int x = max(0, g_menuscreen_w - last_psx_w);
2057                 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2058                 int w = min(g_menuscreen_w, last_psx_w);
2059                 int h = min(g_menuscreen_h, last_psx_h);
2060                 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
2061                 u16 *s = pl_vout_buf;
2062
2063                 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
2064                         menu_darken_bg(d, s, w, 0);
2065         }
2066
2067         if (ready_to_go)
2068                 cpu_clock = plat_cpu_clock_get();
2069 }
2070
2071 void menu_prepare_emu(void)
2072 {
2073         R3000Acpu *prev_cpu = psxCpu;
2074
2075         plat_video_menu_leave();
2076
2077         menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
2078
2079         psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2080         if (psxCpu != prev_cpu)
2081                 // note that this does not really reset, just clears drc caches
2082                 psxCpu->Reset();
2083
2084         // core doesn't care about Config.Cdda changes,
2085         // so handle them manually here
2086         if (Config.Cdda)
2087                 CDR_stop();
2088
2089         menu_sync_config();
2090         apply_lcdrate(Config.PsxType);
2091         apply_filter(filter);
2092         plat_cpu_clock_apply(cpu_clock);
2093
2094         // push config to GPU plugin
2095         plugin_call_rearmed_cbs();
2096
2097         if (GPU_open != NULL) {
2098                 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2099                 if (ret)
2100                         fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2101         }
2102
2103         dfinput_activate();
2104 }
2105
2106 void me_update_msg(const char *msg)
2107 {
2108         strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2109         menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2110
2111         menu_error_time = plat_get_ticks_ms();
2112         lprintf("msg: %s\n", menu_error_msg);
2113 }
2114