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