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