frontend: add BIOS warning
[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 "pcnt.h"
24#include "common/plat.h"
25#include "../libpcsxcore/misc.h"
26#include "../libpcsxcore/cdrom.h"
27#include "../libpcsxcore/psemu_plugin_defs.h"
28#include "../plugins/dfinput/pad.h"
29#include "revision.h"
30
31#define MENU_X2 1
32#define array_size(x) (sizeof(x) / sizeof(x[0]))
33
34typedef enum
35{
36 MA_NONE = 1,
37 MA_MAIN_RESUME_GAME,
38 MA_MAIN_SAVE_STATE,
39 MA_MAIN_LOAD_STATE,
40 MA_MAIN_RESET_GAME,
41 MA_MAIN_LOAD_ROM,
42 MA_MAIN_SWAP_CD,
43 MA_MAIN_RUN_BIOS,
44 MA_MAIN_CONTROLS,
45 MA_MAIN_CREDITS,
46 MA_MAIN_EXIT,
47 MA_CTRL_PLAYER1,
48 MA_CTRL_PLAYER2,
49 MA_CTRL_EMU,
50 MA_CTRL_DEV_FIRST,
51 MA_CTRL_DEV_NEXT,
52 MA_CTRL_DONE,
53 MA_OPT_SAVECFG,
54 MA_OPT_SAVECFG_GAME,
55 MA_OPT_CPU_CLOCKS,
56 MA_OPT_FILTERING,
57} menu_id;
58
59enum {
60 SCALE_1_1,
61 SCALE_4_3,
62 SCALE_FULLSCREEN,
63 SCALE_CUSTOM,
64};
65
66static int last_psx_w, last_psx_h, last_psx_bpp;
67static int scaling, filter, cpu_clock, cpu_clock_st;
68static char rom_fname_reload[MAXPATHLEN];
69static char last_selected_fname[MAXPATHLEN];
70static int warned_about_bios, region, in_type_sel;
71int g_opts;
72
73// from softgpu plugin
74extern int iUseDither;
75extern int UseFrameSkip;
76extern uint32_t dwActFixes;
77extern float fFrameRateHz;
78extern int dwFrameRateTicks;
79
80// sound plugin
81extern int iUseReverb;
82extern int iUseInterpolation;
83extern int iXAPitch;
84extern int iSPUIRQWait;
85extern int iUseTimer;
86
87static const char *bioses[24];
88static const char *gpu_plugins[16];
89static const char *spu_plugins[16];
90static int bios_sel, gpu_plugsel, spu_plugsel;
91
92
93static int min(int x, int y) { return x < y ? x : y; }
94static int max(int x, int y) { return x > y ? x : y; }
95
96void emu_make_path(char *buff, const char *end, int size)
97{
98 int pos, end_len;
99
100 end_len = strlen(end);
101 pos = plat_get_root_dir(buff, size);
102 strncpy(buff + pos, end, size - pos);
103 buff[size - 1] = 0;
104 if (pos + end_len > size - 1)
105 printf("Warning: path truncated: %s\n", buff);
106}
107
108static int emu_check_save_file(int slot)
109{
110 int ret = emu_check_state(slot);
111 return ret == 0 ? 1 : 0;
112}
113
114static int emu_save_load_game(int load, int unused)
115{
116 int ret;
117
118 if (load) {
119 ret = emu_load_state(state_slot);
120
121 // reflect hle/bios mode from savestate
122 if (Config.HLE)
123 bios_sel = 0;
124 else if (bios_sel == 0 && bioses[1] != NULL)
125 // XXX: maybe find the right bios instead
126 bios_sel = 1;
127 }
128 else
129 ret = emu_save_state(state_slot);
130
131 return ret;
132}
133
134// propagate menu settings to the emu vars
135static void menu_sync_config(void)
136{
137 Config.PsxAuto = 1;
138 if (region > 0) {
139 Config.PsxAuto = 0;
140 Config.PsxType = region - 1;
141 }
142 in_type = in_type_sel ? PSE_PAD_TYPE_ANALOGPAD : PSE_PAD_TYPE_STANDARD;
143
144 pl_frame_interval = Config.PsxType ? 20000 : 16667;
145 // used by P.E.Op.S. frameskip code
146 fFrameRateHz = Config.PsxType ? 50.0f : 59.94f;
147 dwFrameRateTicks = (100000*100 / (unsigned long)(fFrameRateHz*100));
148}
149
150static void menu_set_defconfig(void)
151{
152 g_opts = 0;
153 scaling = SCALE_4_3;
154
155 region = 0;
156 in_type_sel = 0;
157 Config.Xa = Config.Cdda = Config.Sio =
158 Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
159
160 iUseDither = 0;
161 UseFrameSkip = 1;
162 dwActFixes = 1<<7;
163
164 iUseReverb = 2;
165 iUseInterpolation = 1;
166 iXAPitch = 0;
167 iSPUIRQWait = 1;
168 iUseTimer = 2;
169
170 menu_sync_config();
171}
172
173#define CE_CONFIG_STR(val) \
174 { #val, 0, Config.val }
175
176#define CE_CONFIG_VAL(val) \
177 { #val, sizeof(Config.val), &Config.val }
178
179#define CE_STR(val) \
180 { #val, 0, val }
181
182#define CE_INTVAL(val) \
183 { #val, sizeof(val), &val }
184
185// 'versioned' var, used when defaults change
186#define CE_INTVAL_V(val, ver) \
187 { #val #ver, sizeof(val), &val }
188
189static const struct {
190 const char *name;
191 size_t len;
192 void *val;
193} config_data[] = {
194 CE_CONFIG_STR(Bios),
195 CE_CONFIG_STR(Gpu),
196 CE_CONFIG_STR(Spu),
197// CE_CONFIG_STR(Cdr),
198 CE_CONFIG_VAL(Xa),
199 CE_CONFIG_VAL(Sio),
200 CE_CONFIG_VAL(Mdec),
201 CE_CONFIG_VAL(Cdda),
202 CE_CONFIG_VAL(Debug),
203 CE_CONFIG_VAL(PsxOut),
204 CE_CONFIG_VAL(SpuIrq),
205 CE_CONFIG_VAL(RCntFix),
206 CE_CONFIG_VAL(VSyncWA),
207 CE_CONFIG_VAL(Cpu),
208 CE_INTVAL(region),
209 CE_INTVAL(scaling),
210 CE_INTVAL(g_layer_x),
211 CE_INTVAL(g_layer_y),
212 CE_INTVAL(g_layer_w),
213 CE_INTVAL(g_layer_h),
214 CE_INTVAL(filter),
215 CE_INTVAL(state_slot),
216 CE_INTVAL(cpu_clock),
217 CE_INTVAL(g_opts),
218 CE_INTVAL(in_type_sel),
219 CE_INTVAL(iUseDither),
220 CE_INTVAL(UseFrameSkip),
221 CE_INTVAL(dwActFixes),
222 CE_INTVAL(iUseReverb),
223 CE_INTVAL(iXAPitch),
224 CE_INTVAL_V(iUseInterpolation, 2),
225 CE_INTVAL_V(iSPUIRQWait, 2),
226 CE_INTVAL(iUseTimer),
227 CE_INTVAL(warned_about_bios),
228};
229
230static char *get_cd_label(void)
231{
232 static char trimlabel[33];
233 int j;
234
235 strncpy(trimlabel, CdromLabel, 32);
236 trimlabel[32] = 0;
237 for (j = 31; j >= 0; j--)
238 if (trimlabel[j] == ' ')
239 trimlabel[j] = 0;
240
241 return trimlabel;
242}
243
244static void make_cfg_fname(char *buf, size_t size, int is_game)
245{
246 if (is_game)
247 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
248 else
249 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
250}
251
252static void keys_write_all(FILE *f);
253
254static int menu_write_config(int is_game)
255{
256 char cfgfile[MAXPATHLEN];
257 FILE *f;
258 int i;
259
260 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
261 f = fopen(cfgfile, "w");
262 if (f == NULL) {
263 printf("menu_write_config: failed to open: %s\n", cfgfile);
264 return -1;
265 }
266
267 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
268 fprintf(f, "%s = ", config_data[i].name);
269 switch (config_data[i].len) {
270 case 0:
271 fprintf(f, "%s\n", (char *)config_data[i].val);
272 break;
273 case 1:
274 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
275 break;
276 case 2:
277 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
278 break;
279 case 4:
280 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
281 break;
282 default:
283 printf("menu_write_config: unhandled len %d for %s\n",
284 config_data[i].len, config_data[i].name);
285 break;
286 }
287 }
288
289 if (!is_game)
290 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
291
292 keys_write_all(f);
293 fclose(f);
294
295 return 0;
296}
297
298static void parse_str_val(char *cval, const char *src)
299{
300 char *tmp;
301 strncpy(cval, src, MAXPATHLEN);
302 cval[MAXPATHLEN - 1] = 0;
303 tmp = strchr(cval, '\n');
304 if (tmp == NULL)
305 tmp = strchr(cval, '\r');
306 if (tmp != NULL)
307 *tmp = 0;
308}
309
310static void keys_load_all(const char *cfg);
311
312static int menu_load_config(int is_game)
313{
314 char cfgfile[MAXPATHLEN];
315 int i, ret = -1;
316 long size;
317 char *cfg;
318 FILE *f;
319
320 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
321 f = fopen(cfgfile, "r");
322 if (f == NULL) {
323 printf("menu_load_config: failed to open: %s\n", cfgfile);
324 return -1;
325 }
326
327 fseek(f, 0, SEEK_END);
328 size = ftell(f);
329 if (size <= 0) {
330 printf("bad size %ld: %s\n", size, cfgfile);
331 goto fail;
332 }
333
334 cfg = malloc(size + 1);
335 if (cfg == NULL)
336 goto fail;
337
338 fseek(f, 0, SEEK_SET);
339 if (fread(cfg, 1, size, f) != size) {
340 printf("failed to read: %s\n", cfgfile);
341 goto fail_read;
342 }
343 cfg[size] = 0;
344
345 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
346 char *tmp, *tmp2;
347 u32 val;
348
349 tmp = strstr(cfg, config_data[i].name);
350 if (tmp == NULL)
351 continue;
352 tmp += strlen(config_data[i].name);
353 if (strncmp(tmp, " = ", 3) != 0)
354 continue;
355 tmp += 3;
356
357 if (config_data[i].len == 0) {
358 parse_str_val(config_data[i].val, tmp);
359 continue;
360 }
361
362 tmp2 = NULL;
363 val = strtoul(tmp, &tmp2, 16);
364 if (tmp2 == NULL || tmp == tmp2)
365 continue; // parse failed
366
367 switch (config_data[i].len) {
368 case 1:
369 *(u8 *)config_data[i].val = val;
370 break;
371 case 2:
372 *(u16 *)config_data[i].val = val;
373 break;
374 case 4:
375 *(u32 *)config_data[i].val = val;
376 break;
377 default:
378 printf("menu_load_config: unhandled len %d for %s\n",
379 config_data[i].len, config_data[i].name);
380 break;
381 }
382 }
383
384 if (!is_game) {
385 char *tmp = strstr(cfg, "lastcdimg = ");
386 if (tmp != NULL) {
387 tmp += 12;
388 parse_str_val(last_selected_fname, tmp);
389 }
390 }
391
392 menu_sync_config();
393
394 // sync plugins
395 for (i = bios_sel = 0; bioses[i] != NULL; i++)
396 if (strcmp(Config.Bios, bioses[i]) == 0)
397 { bios_sel = i; break; }
398
399 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
400 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
401 { gpu_plugsel = i; break; }
402
403 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
404 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
405 { spu_plugsel = i; break; }
406
407 keys_load_all(cfg);
408 ret = 0;
409fail_read:
410 free(cfg);
411fail:
412 fclose(f);
413 return ret;
414}
415
416// rrrr rggg gggb bbbb
417static unsigned short fname2color(const char *fname)
418{
419 static const char *cdimg_exts[] = { ".bin", ".img", ".iso", ".cue", ".z", ".bz", ".znx", ".pbp" };
420 static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub", ".table", ".index", ".sbi" };
421 const char *ext = strrchr(fname, '.');
422 int i;
423
424 if (ext == NULL)
425 return 0xffff;
426 for (i = 0; i < array_size(cdimg_exts); i++)
427 if (strcasecmp(ext, cdimg_exts[i]) == 0)
428 return 0x7bff;
429 for (i = 0; i < array_size(other_exts); i++)
430 if (strcasecmp(ext, other_exts[i]) == 0)
431 return 0xa514;
432 return 0xffff;
433}
434
435static void draw_savestate_bg(int slot);
436
437#define MENU_ALIGN_LEFT
438#define menu_init menu_init_common
439#include "common/menu.c"
440#undef menu_init
441
442// a bit of black magic here
443static void draw_savestate_bg(int slot)
444{
445 extern void bgr555_to_rgb565(void *dst, void *src, int bytes);
446 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
447 int x, y, w, h;
448 char fname[MAXPATHLEN];
449 GPUFreeze_t *gpu;
450 u16 *s, *d;
451 gzFile f;
452 int ret;
453 u32 tmp;
454
455 ret = get_state_filename(fname, sizeof(fname), slot);
456 if (ret != 0)
457 return;
458
459 f = gzopen(fname, "rb");
460 if (f == NULL)
461 return;
462
463 if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
464 fprintf(stderr, "gzseek failed\n");
465 gzclose(f);
466 return;
467 }
468
469 gpu = malloc(sizeof(*gpu));
470 if (gpu == NULL) {
471 gzclose(f);
472 return;
473 }
474
475 ret = gzread(f, gpu, sizeof(*gpu));
476 gzclose(f);
477 if (ret != sizeof(*gpu)) {
478 fprintf(stderr, "gzread failed\n");
479 goto out;
480 }
481
482 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
483
484 if ((gpu->ulStatus & 0x800000) || (gpu->ulStatus & 0x200000))
485 goto out; // disabled || 24bpp (NYET)
486
487 x = gpu->ulControl[5] & 0x3ff;
488 y = (gpu->ulControl[5] >> 10) & 0x1ff;
489 s = (u16 *)gpu->psxVRam + y * 1024 + (x & ~3);
490 w = psx_widths[(gpu->ulStatus >> 16) & 7];
491 tmp = gpu->ulControl[7];
492 h = ((tmp >> 10) & 0x3ff) - (tmp & 0x3ff);
493 if (gpu->ulStatus & 0x80000) // doubleheight
494 h *= 2;
495
496 x = max(0, g_menuscreen_w - w) & ~3;
497 y = max(0, g_menuscreen_h / 2 - h / 2);
498 w = min(g_menuscreen_w, w);
499 h = min(g_menuscreen_h, h);
500 d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
501
502 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
503 bgr555_to_rgb565(d, s, w * 2);
504
505out:
506 free(gpu);
507}
508
509// ---------- pandora specific -----------
510
511static const char pnd_script_base[] = "sudo -n /usr/pandora/scripts";
512static char **pnd_filter_list;
513
514static int get_cpu_clock(void)
515{
516 FILE *f;
517 int ret = 0;
518 f = fopen("/proc/pandora/cpu_mhz_max", "r");
519 if (f) {
520 fscanf(f, "%d", &ret);
521 fclose(f);
522 }
523 return ret;
524}
525
526static void apply_cpu_clock(void)
527{
528 char buf[128];
529
530 if (cpu_clock != 0 && cpu_clock != get_cpu_clock()) {
531 snprintf(buf, sizeof(buf), "unset DISPLAY; echo y | %s/op_cpuspeed.sh %d",
532 pnd_script_base, cpu_clock);
533 system(buf);
534 }
535}
536
537static void apply_filter(int which)
538{
539 static int old = -1;
540 char buf[128];
541 int i;
542
543 if (pnd_filter_list == NULL || which == old)
544 return;
545
546 for (i = 0; i < which; i++)
547 if (pnd_filter_list[i] == NULL)
548 return;
549
550 if (pnd_filter_list[i] == NULL)
551 return;
552
553 snprintf(buf, sizeof(buf), "%s/op_videofir.sh %s", pnd_script_base, pnd_filter_list[i]);
554 system(buf);
555 old = which;
556}
557
558static menu_entry e_menu_gfx_options[];
559
560static void pnd_menu_init(void)
561{
562 struct dirent *ent;
563 int i, count = 0;
564 char **mfilters;
565 char buff[64];
566 DIR *dir;
567
568 cpu_clock_st = cpu_clock = get_cpu_clock();
569
570 dir = opendir("/etc/pandora/conf/dss_fir");
571 if (dir == NULL) {
572 perror("filter opendir");
573 return;
574 }
575
576 while (1) {
577 errno = 0;
578 ent = readdir(dir);
579 if (ent == NULL) {
580 if (errno != 0)
581 perror("readdir");
582 break;
583 }
584
585 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
586 continue;
587
588 count++;
589 }
590
591 if (count == 0)
592 return;
593
594 mfilters = calloc(count + 1, sizeof(mfilters[0]));
595 if (mfilters == NULL)
596 return;
597
598 rewinddir(dir);
599 for (i = 0; (ent = readdir(dir)); ) {
600 size_t len;
601
602 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
603 continue;
604
605 len = strlen(ent->d_name);
606
607 // skip pre-HF5 extra files
608 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
609 continue;
610 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
611 continue;
612
613 // have to cut "_up_h" for pre-HF5
614 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
615 len -= 5;
616
617 if (len > sizeof(buff) - 1)
618 continue;
619
620 strncpy(buff, ent->d_name, len);
621 buff[len] = 0;
622 mfilters[i] = strdup(buff);
623 if (mfilters[i] != NULL)
624 i++;
625 }
626 closedir(dir);
627
628 i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
629 e_menu_gfx_options[i].data = (void *)mfilters;
630 pnd_filter_list = mfilters;
631}
632
633void menu_finish(void)
634{
635 cpu_clock = cpu_clock_st;
636 apply_cpu_clock();
637}
638
639// -------------- key config --------------
640
641me_bind_action me_ctrl_actions[] =
642{
643 { "UP ", 1 << DKEY_UP},
644 { "DOWN ", 1 << DKEY_DOWN },
645 { "LEFT ", 1 << DKEY_LEFT },
646 { "RIGHT ", 1 << DKEY_RIGHT },
647 { "TRIANGLE", 1 << DKEY_TRIANGLE },
648 { "CIRCLE ", 1 << DKEY_CIRCLE },
649 { "CROSS ", 1 << DKEY_CROSS },
650 { "SQUARE ", 1 << DKEY_SQUARE },
651 { "L1 ", 1 << DKEY_L1 },
652 { "R1 ", 1 << DKEY_R1 },
653 { "L2 ", 1 << DKEY_L2 },
654 { "R2 ", 1 << DKEY_R2 },
655 { "L3 ", 1 << DKEY_L3 },
656 { "R3 ", 1 << DKEY_R3 },
657 { "START ", 1 << DKEY_START },
658 { "SELECT ", 1 << DKEY_SELECT },
659 { NULL, 0 }
660};
661
662me_bind_action emuctrl_actions[] =
663{
664 { "Save State ", 1 << SACTION_SAVE_STATE },
665 { "Load State ", 1 << SACTION_LOAD_STATE },
666 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
667 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
668 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
669 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
670 { NULL, 0 }
671};
672
673static char *mystrip(char *str)
674{
675 int i, len;
676
677 len = strlen(str);
678 for (i = 0; i < len; i++)
679 if (str[i] != ' ') break;
680 if (i > 0) memmove(str, str + i, len - i + 1);
681
682 len = strlen(str);
683 for (i = len - 1; i >= 0; i--)
684 if (str[i] != ' ') break;
685 str[i+1] = 0;
686
687 return str;
688}
689
690static void get_line(char *d, size_t size, const char *s)
691{
692 const char *pe;
693 size_t len;
694
695 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
696 ;
697 len = pe - s;
698 if (len > size - 1)
699 len = size - 1;
700 strncpy(d, s, len);
701 d[len] = 0;
702
703 mystrip(d);
704}
705
706static void keys_write_all(FILE *f)
707{
708 int d;
709
710 for (d = 0; d < IN_MAX_DEVS; d++)
711 {
712 const int *binds = in_get_dev_binds(d);
713 const char *name = in_get_dev_name(d, 0, 0);
714 int k, count = 0;
715
716 if (binds == NULL || name == NULL)
717 continue;
718
719 fprintf(f, "binddev = %s\n", name);
720 in_get_config(d, IN_CFG_BIND_COUNT, &count);
721
722 for (k = 0; k < count; k++)
723 {
724 int i, kbinds, mask;
725 char act[32];
726
727 act[0] = act[31] = 0;
728 name = in_get_key_name(d, k);
729
730 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
731 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
732 mask = me_ctrl_actions[i].mask;
733 if (mask & kbinds) {
734 strncpy(act, me_ctrl_actions[i].name, 31);
735 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
736 kbinds &= ~mask;
737 }
738 mask = me_ctrl_actions[i].mask << 16;
739 if (mask & kbinds) {
740 strncpy(act, me_ctrl_actions[i].name, 31);
741 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
742 kbinds &= ~mask;
743 }
744 }
745
746 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
747 for (i = 0; kbinds && i < ARRAY_SIZE(emuctrl_actions) - 1; i++) {
748 mask = emuctrl_actions[i].mask;
749 if (mask & kbinds) {
750 strncpy(act, emuctrl_actions[i].name, 31);
751 fprintf(f, "bind %s = %s\n", name, mystrip(act));
752 kbinds &= ~mask;
753 }
754 }
755 }
756 }
757}
758
759static int parse_bind_val(const char *val, int *type)
760{
761 int i;
762
763 *type = IN_BINDTYPE_NONE;
764 if (val[0] == 0)
765 return 0;
766
767 if (strncasecmp(val, "player", 6) == 0)
768 {
769 int player, shift = 0;
770 player = atoi(val + 6) - 1;
771
772 if ((unsigned int)player > 1)
773 return -1;
774 if (player == 1)
775 shift = 16;
776
777 *type = IN_BINDTYPE_PLAYER12;
778 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
779 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
780 return me_ctrl_actions[i].mask << shift;
781 }
782 }
783 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
784 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
785 *type = IN_BINDTYPE_EMU;
786 return emuctrl_actions[i].mask;
787 }
788 }
789
790 return -1;
791}
792
793static void keys_load_all(const char *cfg)
794{
795 char dev[256], key[128], *act;
796 const char *p;
797 int bind, bindtype;
798 int dev_id;
799
800 p = cfg;
801 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
802 p += 10;
803
804 get_line(dev, sizeof(dev), p);
805 dev_id = in_config_parse_dev(dev);
806 if (dev_id < 0) {
807 printf("input: can't handle dev: %s\n", dev);
808 continue;
809 }
810
811 in_unbind_all(dev_id, -1, -1);
812 while ((p = strstr(p, "bind"))) {
813 if (strncmp(p, "binddev = ", 10) == 0)
814 break;
815
816 p += 4;
817 if (*p != ' ') {
818 printf("input: parse error: %16s..\n", p);
819 continue;
820 }
821
822 get_line(key, sizeof(key), p);
823 act = strchr(key, '=');
824 if (act == NULL) {
825 printf("parse failed: %16s..\n", p);
826 continue;
827 }
828 *act = 0;
829 act++;
830 mystrip(key);
831 mystrip(act);
832
833 bind = parse_bind_val(act, &bindtype);
834 if (bind != -1 && bind != 0) {
835 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
836 in_config_bind_key(dev_id, key, bind, bindtype);
837 }
838 else
839 lprintf("config: unhandled action \"%s\"\n", act);
840 }
841 }
842}
843
844static int key_config_loop_wrap(int id, int keys)
845{
846 switch (id) {
847 case MA_CTRL_PLAYER1:
848 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
849 break;
850 case MA_CTRL_PLAYER2:
851 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
852 break;
853 case MA_CTRL_EMU:
854 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
855 break;
856 default:
857 break;
858 }
859 return 0;
860}
861
862static const char *mgn_dev_name(int id, int *offs)
863{
864 const char *name = NULL;
865 static int it = 0;
866
867 if (id == MA_CTRL_DEV_FIRST)
868 it = 0;
869
870 for (; it < IN_MAX_DEVS; it++) {
871 name = in_get_dev_name(it, 1, 1);
872 if (name != NULL)
873 break;
874 }
875
876 it++;
877 return name;
878}
879
880static const char *mgn_saveloadcfg(int id, int *offs)
881{
882 return "";
883}
884
885static int mh_savecfg(int id, int keys)
886{
887 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
888 me_update_msg("config saved");
889 else
890 me_update_msg("failed to write config");
891
892 return 1;
893}
894
895static const char *men_in_type_sel[] = { "Standard (SCPH-1080)", "Analog (SCPH-1150)", NULL };
896
897static menu_entry e_menu_keyconfig[] =
898{
899 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
900 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
901 mee_handler_id("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap),
902 mee_label (""),
903 mee_enum ("Controller", 0, in_type_sel, men_in_type_sel),
904 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
905 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
906 mee_label (""),
907 mee_label ("Input devices:"),
908 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
909 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
910 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
911 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
912 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
913 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
914 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
915 mee_end,
916};
917
918static int menu_loop_keyconfig(int id, int keys)
919{
920 static int sel = 0;
921
922// me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
923 me_loop(e_menu_keyconfig, &sel, NULL);
924 return 0;
925}
926
927// ------------ gfx options menu ------------
928
929static const char *men_scaler[] = { "1x1", "scaled 4:3", "fullscreen", "custom", NULL };
930static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
931 "using d-pad or move it using R+d-pad";
932static const char *men_dummy[] = { NULL };
933
934static int menu_loop_cscaler(int id, int keys)
935{
936 unsigned int inp;
937
938 scaling = SCALE_CUSTOM;
939
940 omap_enable_layer(1);
941
942 for (;;)
943 {
944 menu_draw_begin(0);
945 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
946 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
947 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
948 menu_draw_end();
949
950 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
951 if (inp & PBTN_UP) g_layer_y--;
952 if (inp & PBTN_DOWN) g_layer_y++;
953 if (inp & PBTN_LEFT) g_layer_x--;
954 if (inp & PBTN_RIGHT) g_layer_x++;
955 if (!(inp & PBTN_R)) {
956 if (inp & PBTN_UP) g_layer_h += 2;
957 if (inp & PBTN_DOWN) g_layer_h -= 2;
958 if (inp & PBTN_LEFT) g_layer_w += 2;
959 if (inp & PBTN_RIGHT) g_layer_w -= 2;
960 }
961 if (inp & (PBTN_MOK|PBTN_MBACK))
962 break;
963
964 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
965 if (g_layer_x < 0) g_layer_x = 0;
966 if (g_layer_x > 640) g_layer_x = 640;
967 if (g_layer_y < 0) g_layer_y = 0;
968 if (g_layer_y > 420) g_layer_y = 420;
969 if (g_layer_w < 160) g_layer_w = 160;
970 if (g_layer_h < 60) g_layer_h = 60;
971 if (g_layer_x + g_layer_w > 800)
972 g_layer_w = 800 - g_layer_x;
973 if (g_layer_y + g_layer_h > 480)
974 g_layer_h = 480 - g_layer_y;
975 omap_enable_layer(1);
976 }
977 }
978
979 omap_enable_layer(0);
980
981 return 0;
982}
983
984static menu_entry e_menu_gfx_options[] =
985{
986 mee_enum ("Scaler", 0, scaling, men_scaler),
987 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
988// mee_onoff ("Vsync", 0, vsync, 1),
989 mee_cust_h ("Setup custom scaler", 0, menu_loop_cscaler, NULL, h_cscaler),
990 mee_end,
991};
992
993static int menu_loop_gfx_options(int id, int keys)
994{
995 static int sel = 0;
996
997 me_loop(e_menu_gfx_options, &sel, NULL);
998
999 return 0;
1000}
1001
1002// ------------ bios/plugins ------------
1003
1004static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1005static const char h_gpu_0[] = "Needed for Chrono Cross";
1006static const char h_gpu_1[] = "Capcom fighting games";
1007static const char h_gpu_2[] = "Black screens in Lunar";
1008static const char h_gpu_3[] = "Compatibility mode";
1009static const char h_gpu_6[] = "Pandemonium 2";
1010static const char h_gpu_7[] = "Skip every second frame";
1011static const char h_gpu_8[] = "Needed by Dark Forces";
1012static const char h_gpu_9[] = "better g-colors, worse textures";
1013static const char h_gpu_10[] = "Toggle busy flags after drawing";
1014
1015static menu_entry e_menu_plugin_gpu[] =
1016{
1017 mee_enum ("Dithering", 0, iUseDither, men_gpu_dithering),
1018 mee_onoff_h ("Odd/even bit hack", 0, dwActFixes, 1<<0, h_gpu_0),
1019 mee_onoff_h ("Expand screen width", 0, dwActFixes, 1<<1, h_gpu_1),
1020 mee_onoff_h ("Ignore brightness color", 0, dwActFixes, 1<<2, h_gpu_2),
1021 mee_onoff_h ("Disable coordinate check", 0, dwActFixes, 1<<3, h_gpu_3),
1022 mee_onoff_h ("Lazy screen update", 0, dwActFixes, 1<<6, h_gpu_6),
1023 mee_onoff_h ("Old frame skipping", 0, dwActFixes, 1<<7, h_gpu_7),
1024 mee_onoff_h ("Repeated flat tex triangles ",0,dwActFixes, 1<<8, h_gpu_8),
1025 mee_onoff_h ("Draw quads with triangles", 0, dwActFixes, 1<<9, h_gpu_9),
1026 mee_onoff_h ("Fake 'gpu busy' states", 0, dwActFixes, 1<<10, h_gpu_10),
1027 mee_end,
1028};
1029
1030static int menu_loop_plugin_gpu(int id, int keys)
1031{
1032 static int sel = 0;
1033 me_loop(e_menu_plugin_gpu, &sel, NULL);
1034 return 0;
1035}
1036
1037static const char *men_spu_reverb[] = { "Off", "Fake", "On", NULL };
1038static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1039static const char h_spu_irq_wait[] = "Wait for CPU (recommended set to ON)";
1040static const char h_spu_thread[] = "Run sound emulation in main thread (recommended)";
1041
1042static menu_entry e_menu_plugin_spu[] =
1043{
1044 mee_enum ("Reverb", 0, iUseReverb, men_spu_reverb),
1045 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
1046 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
1047 mee_onoff_h ("SPU IRQ Wait", 0, iSPUIRQWait, 1, h_spu_irq_wait),
1048 mee_onoff_h ("Sound in main thread", 0, iUseTimer, 2, h_spu_thread),
1049 mee_end,
1050};
1051
1052static int menu_loop_plugin_spu(int id, int keys)
1053{
1054 static int sel = 0;
1055 me_loop(e_menu_plugin_spu, &sel, NULL);
1056 return 0;
1057}
1058
1059static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in savestates\n"
1060 "and can't be changed there. Must save config and reload\n"
1061 "the game for change to take effect";
1062static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1063 "for plugin change to take effect";
1064static const char h_gpu[] = "Configure built-in P.E.Op.S. SoftGL Driver V1.17";
1065static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1066
1067static menu_entry e_menu_plugin_options[] =
1068{
1069 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1070 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1071 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_xpu),
1072 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu, h_gpu),
1073 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1074 mee_end,
1075};
1076
1077static menu_entry e_menu_main[];
1078
1079static int menu_loop_plugin_options(int id, int keys)
1080{
1081 static int sel = 0;
1082 me_loop(e_menu_plugin_options, &sel, NULL);
1083
1084 // sync BIOS/plugins
1085 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1086 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1087 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1088 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1089
1090 return 0;
1091}
1092
1093// ------------ adv options menu ------------
1094
1095static const char h_cfg_cpul[] = "Shows CPU usage in %";
1096static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1097static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1098static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1099 "(proper .cue/.bin dump is needed otherwise)";
1100static const char h_cfg_sio[] = "This should be enabled for certain memcards/gamepads";
1101static const char h_cfg_spuirq[] = "Compatibility tweak; should probably be left off";
1102static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix";
1103static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix";
1104static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1105 "Might be useful to overcome some dynarec bugs";
1106
1107static menu_entry e_menu_adv_options[] =
1108{
1109 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1110 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1111 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1112 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1113 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1114 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1115 mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1116 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
1117 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1118 mee_end,
1119};
1120
1121static int menu_loop_adv_options(int id, int keys)
1122{
1123 static int sel = 0;
1124 me_loop(e_menu_adv_options, &sel, NULL);
1125 return 0;
1126}
1127
1128// ------------ options menu ------------
1129
1130static int mh_restore_defaults(int id, int keys)
1131{
1132 menu_set_defconfig();
1133 me_update_msg("defaults restored");
1134 return 1;
1135}
1136
1137static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1138/*
1139static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1140static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1141 "loading state or both";
1142*/
1143static const char h_restore_def[] = "Switches back to default / recommended\n"
1144 "configuration";
1145
1146static menu_entry e_menu_options[] =
1147{
1148// mee_range ("Save slot", 0, state_slot, 0, 9),
1149// mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1150 mee_onoff ("Frameskip", 0, UseFrameSkip, 1),
1151 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1152 mee_enum ("Region", 0, region, men_region),
1153 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1154 mee_handler ("[Display]", menu_loop_gfx_options),
1155 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1156 mee_handler ("[Advanced]", menu_loop_adv_options),
1157 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1158 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1159 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1160 mee_end,
1161};
1162
1163static int menu_loop_options(int id, int keys)
1164{
1165 static int sel = 0;
1166 int i;
1167
1168 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1169 e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1170 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1171
1172 me_loop(e_menu_options, &sel, NULL);
1173
1174 return 0;
1175}
1176
1177// ------------ debug menu ------------
1178
1179static void draw_frame_debug(void)
1180{
1181 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1182}
1183
1184static void debug_menu_loop(void)
1185{
1186 int inp;
1187
1188 while (1)
1189 {
1190 menu_draw_begin(1);
1191 draw_frame_debug();
1192 menu_draw_end();
1193
1194 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1195 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1196 if (inp & PBTN_MBACK)
1197 return;
1198 }
1199}
1200
1201// ------------ main menu ------------
1202
1203static void menu_bios_warn(void)
1204{
1205 int inp;
1206 static const char msg[] =
1207 "You don't seem to have copied any BIOS files to\n"
1208 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1209 "While many games work fine with fake (HLE) BIOS,\n"
1210 "others (like MGS and FF8) require BIOS to work.\n"
1211 "After copying the file, you'll also need to\n"
1212 "select it in the emu's options->[BIOS/Plugins]\n\n"
1213 "The file is usually named SCPH1001.BIN, but\n"
1214 "other not compressed files can be used too.\n\n"
1215 "Press (B) or (X) to continue";
1216
1217 while (1)
1218 {
1219 menu_draw_begin(1);
1220 draw_menu_message(msg, NULL);
1221 menu_draw_end();
1222
1223 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1224 if (inp & (PBTN_MBACK|PBTN_MOK))
1225 return;
1226 }
1227}
1228
1229// ------------ main menu ------------
1230
1231void OnFile_Exit();
1232
1233static void draw_frame_main(void)
1234{
1235 if (CdromId[0] != 0) {
1236 char buff[64];
1237 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1238 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1239 Config.HLE ? "HLE" : "BIOS");
1240 smalltext_out16(4, 1, buff, 0x105f);
1241 }
1242}
1243
1244static void draw_frame_credits(void)
1245{
1246 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1247}
1248
1249static const char credits_text[] =
1250 "PCSX-ReARMed\n\n"
1251 "(C) 1999-2003 PCSX Team\n"
1252 "(C) 2005-2009 PCSX-df Team\n"
1253 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1254 "GPU and SPU code by Pete Bernert\n"
1255 " and the P.E.Op.S. team\n"
1256 "ARM recompiler (C) 2009-2011 Ari64\n"
1257 "PCSX4ALL plugins by PCSX4ALL team\n"
1258 " Chui, Franxis, Unai\n\n"
1259 "integration, optimization and\n"
1260 " frontend (C) 2010-2011 notaz\n";
1261
1262static int reset_game(void)
1263{
1264 // sanity check
1265 if (bios_sel == 0 && !Config.HLE)
1266 return -1;
1267
1268 ClosePlugins();
1269 OpenPlugins();
1270 SysReset();
1271 if (CheckCdrom() != -1) {
1272 LoadCdrom();
1273 }
1274 return 0;
1275}
1276
1277static int run_bios(void)
1278{
1279 if (bios_sel == 0)
1280 return -1;
1281
1282 ready_to_go = 0;
1283 pl_fbdev_buf = NULL;
1284
1285 ClosePlugins();
1286 set_cd_image(NULL);
1287 LoadPlugins();
1288 pcnt_hook_plugins();
1289 NetOpened = 0;
1290 if (OpenPlugins() == -1) {
1291 me_update_msg("failed to open plugins");
1292 return -1;
1293 }
1294 plugin_call_rearmed_cbs();
1295
1296 CdromId[0] = '\0';
1297 CdromLabel[0] = '\0';
1298
1299 SysReset();
1300
1301 ready_to_go = 1;
1302 return 0;
1303}
1304
1305static int run_cd_image(const char *fname)
1306{
1307 ready_to_go = 0;
1308 pl_fbdev_buf = NULL;
1309
1310 ClosePlugins();
1311 set_cd_image(fname);
1312 LoadPlugins();
1313 pcnt_hook_plugins();
1314 NetOpened = 0;
1315 if (OpenPlugins() == -1) {
1316 me_update_msg("failed to open plugins");
1317 return -1;
1318 }
1319 plugin_call_rearmed_cbs();
1320
1321 if (CheckCdrom() == -1) {
1322 // Only check the CD if we are starting the console with a CD
1323 ClosePlugins();
1324 me_update_msg("unsupported/invalid CD image");
1325 return -1;
1326 }
1327
1328 SysReset();
1329
1330 // Read main executable directly from CDRom and start it
1331 if (LoadCdrom() == -1) {
1332 ClosePlugins();
1333 me_update_msg("failed to load CD image");
1334 return -1;
1335 }
1336
1337 ready_to_go = 1;
1338 return 0;
1339}
1340
1341static int romsel_run(void)
1342{
1343 int prev_gpu, prev_spu;
1344 char *fname;
1345
1346 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1347 if (fname == NULL)
1348 return -1;
1349
1350 printf("selected file: %s\n", fname);
1351
1352 if (run_cd_image(fname) != 0)
1353 return -1;
1354
1355 prev_gpu = gpu_plugsel;
1356 prev_spu = spu_plugsel;
1357 if (menu_load_config(1) != 0)
1358 menu_load_config(0);
1359
1360 // check for plugin changes, have to repeat
1361 // loading if game config changed plugins to reload them
1362 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1363 printf("plugin change detected, reloading plugins..\n");
1364 if (run_cd_image(fname) != 0)
1365 return -1;
1366 }
1367
1368 strcpy(last_selected_fname, rom_fname_reload);
1369 return 0;
1370}
1371
1372static int swap_cd_image(void)
1373{
1374 char *fname;
1375
1376 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1377 if (fname == NULL)
1378 return -1;
1379
1380 printf("selected file: %s\n", fname);
1381
1382 CdromId[0] = '\0';
1383 CdromLabel[0] = '\0';
1384
1385 set_cd_image(fname);
1386 if (ReloadCdromPlugin() < 0) {
1387 me_update_msg("failed to load cdr plugin");
1388 return -1;
1389 }
1390 if (CDR_open() < 0) {
1391 me_update_msg("failed to open cdr plugin");
1392 return -1;
1393 }
1394
1395 SetCdOpenCaseTime(time(NULL) + 2);
1396 LidInterrupt();
1397
1398 strcpy(last_selected_fname, rom_fname_reload);
1399 return 0;
1400}
1401
1402static int main_menu_handler(int id, int keys)
1403{
1404 switch (id)
1405 {
1406 case MA_MAIN_RESUME_GAME:
1407 if (ready_to_go)
1408 return 1;
1409 break;
1410 case MA_MAIN_SAVE_STATE:
1411 if (ready_to_go)
1412 return menu_loop_savestate(0);
1413 break;
1414 case MA_MAIN_LOAD_STATE:
1415 if (ready_to_go)
1416 return menu_loop_savestate(1);
1417 break;
1418 case MA_MAIN_RESET_GAME:
1419 if (ready_to_go && reset_game() == 0)
1420 return 1;
1421 break;
1422 case MA_MAIN_LOAD_ROM:
1423 if (romsel_run() == 0)
1424 return 1;
1425 break;
1426 case MA_MAIN_SWAP_CD:
1427 if (swap_cd_image() == 0)
1428 return 1;
1429 break;
1430 case MA_MAIN_RUN_BIOS:
1431 if (run_bios() == 0)
1432 return 1;
1433 break;
1434 case MA_MAIN_CREDITS:
1435 draw_menu_message(credits_text, draw_frame_credits);
1436 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1437 break;
1438 case MA_MAIN_EXIT:
1439 OnFile_Exit();
1440 break;
1441 default:
1442 lprintf("%s: something unknown selected\n", __FUNCTION__);
1443 break;
1444 }
1445
1446 return 0;
1447}
1448
1449static menu_entry e_menu_main[] =
1450{
1451 mee_label (""),
1452 mee_label (""),
1453 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1454 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1455 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1456 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1457 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1458 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1459 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1460 mee_handler ("Options", menu_loop_options),
1461 mee_handler ("Controls", menu_loop_keyconfig),
1462 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1463 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1464 mee_end,
1465};
1466
1467// ----------------------------
1468
1469static void menu_leave_emu(void);
1470
1471void menu_loop(void)
1472{
1473 static int sel = 0;
1474
1475 menu_leave_emu();
1476
1477 if (bioses[1] == NULL && !warned_about_bios) {
1478 menu_bios_warn();
1479 warned_about_bios = 1;
1480 }
1481
1482 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1483 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1484 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
1485 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1486 me_enable(e_menu_main, MA_MAIN_SWAP_CD, ready_to_go);
1487 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1488
1489 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1490
1491 do {
1492 me_loop(e_menu_main, &sel, draw_frame_main);
1493 } while (!ready_to_go);
1494
1495 /* wait until menu, ok, back is released */
1496 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1497 ;
1498
1499 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1500
1501 menu_prepare_emu();
1502}
1503
1504static void scan_bios_plugins(void)
1505{
1506 char fname[MAXPATHLEN];
1507 struct dirent *ent;
1508 int bios_i, gpu_i, spu_i;
1509 char *p;
1510 DIR *dir;
1511
1512 bioses[0] = "HLE";
1513 gpu_plugins[0] = "builtin_gpu";
1514 spu_plugins[0] = "builtin_spu";
1515 bios_i = gpu_i = spu_i = 1;
1516
1517 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1518 dir = opendir(fname);
1519 if (dir == NULL) {
1520 perror("scan_bios_plugins bios opendir");
1521 goto do_plugins;
1522 }
1523
1524 while (1) {
1525 struct stat st;
1526
1527 errno = 0;
1528 ent = readdir(dir);
1529 if (ent == NULL) {
1530 if (errno != 0)
1531 perror("readdir");
1532 break;
1533 }
1534
1535 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1536 continue;
1537
1538 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1539 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1540 printf("bad BIOS file: %s\n", ent->d_name);
1541 continue;
1542 }
1543
1544 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1545 bioses[bios_i++] = strdup(ent->d_name);
1546 continue;
1547 }
1548
1549 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1550 }
1551
1552 closedir(dir);
1553
1554do_plugins:
1555 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1556 dir = opendir(fname);
1557 if (dir == NULL) {
1558 perror("scan_bios_plugins opendir");
1559 return;
1560 }
1561
1562 while (1) {
1563 void *h, *tmp;
1564
1565 errno = 0;
1566 ent = readdir(dir);
1567 if (ent == NULL) {
1568 if (errno != 0)
1569 perror("readdir");
1570 break;
1571 }
1572 p = strstr(ent->d_name, ".so");
1573 if (p == NULL)
1574 continue;
1575
1576 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1577 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1578 if (h == NULL) {
1579 fprintf(stderr, "%s\n", dlerror());
1580 continue;
1581 }
1582
1583 // now what do we have here?
1584 tmp = dlsym(h, "GPUinit");
1585 if (tmp) {
1586 dlclose(h);
1587 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1588 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1589 continue;
1590 }
1591
1592 tmp = dlsym(h, "SPUinit");
1593 if (tmp) {
1594 dlclose(h);
1595 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1596 spu_plugins[spu_i++] = strdup(ent->d_name);
1597 continue;
1598 }
1599
1600 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1601 dlclose(h);
1602 }
1603
1604 closedir(dir);
1605}
1606
1607void menu_init(void)
1608{
1609 char buff[MAXPATHLEN];
1610
1611 strcpy(last_selected_fname, "/media");
1612
1613 scan_bios_plugins();
1614 pnd_menu_init();
1615 menu_init_common();
1616
1617 menu_set_defconfig();
1618 menu_load_config(0);
1619 last_psx_w = 320;
1620 last_psx_h = 240;
1621 last_psx_bpp = 16;
1622
1623 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1624 if (g_menubg_src_ptr == NULL)
1625 exit(1);
1626 emu_make_path(buff, "skin/background.png", sizeof(buff));
1627 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1628}
1629
1630void menu_notify_mode_change(int w, int h, int bpp)
1631{
1632 last_psx_w = w;
1633 last_psx_h = h;
1634 last_psx_bpp = bpp;
1635
1636 if (scaling == SCALE_1_1) {
1637 g_layer_x = 800/2 - w/2; g_layer_y = 480/2 - h/2;
1638 g_layer_w = w; g_layer_h = h;
1639 }
1640}
1641
1642static void menu_leave_emu(void)
1643{
1644 if (GPU_close != NULL) {
1645 int ret = GPU_close();
1646 if (ret)
1647 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1648 }
1649
1650 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1651 if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
1652 int x = max(0, g_menuscreen_w - last_psx_w);
1653 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1654 int w = min(g_menuscreen_w, last_psx_w);
1655 int h = min(g_menuscreen_h, last_psx_h);
1656 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
1657 u16 *s = pl_fbdev_buf;
1658
1659 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
1660 menu_darken_bg(d, s, w, 0);
1661 }
1662
1663 if (ready_to_go)
1664 cpu_clock = get_cpu_clock();
1665
1666 plat_video_menu_enter(ready_to_go);
1667}
1668
1669void menu_prepare_emu(void)
1670{
1671 R3000Acpu *prev_cpu = psxCpu;
1672
1673 plat_video_menu_leave();
1674
1675 switch (scaling) {
1676 case SCALE_1_1:
1677 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
1678 break;
1679 case SCALE_4_3:
1680 g_layer_x = 80; g_layer_y = 0;
1681 g_layer_w = 640; g_layer_h = 480;
1682 break;
1683 case SCALE_FULLSCREEN:
1684 g_layer_x = 0; g_layer_y = 0;
1685 g_layer_w = 800; g_layer_h = 480;
1686 break;
1687 case SCALE_CUSTOM:
1688 break;
1689 }
1690 apply_filter(filter);
1691 apply_cpu_clock();
1692
1693 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1694 if (psxCpu != prev_cpu)
1695 // note that this does not really reset, just clears drc caches
1696 psxCpu->Reset();
1697
1698 // core doesn't care about Config.Cdda changes,
1699 // so handle them manually here
1700 if (Config.Cdda)
1701 CDR_stop();
1702
1703 menu_sync_config();
1704
1705 if (GPU_open != NULL) {
1706 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1707 if (ret)
1708 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1709 }
1710
1711 dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
1712}
1713
1714void me_update_msg(const char *msg)
1715{
1716 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1717 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1718
1719 menu_error_time = plat_get_ticks_ms();
1720 lprintf("msg: %s\n", menu_error_msg);
1721}
1722