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