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