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