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