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