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