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