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