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