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