frontend: input: detect dead devices, always set keynames
[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"
72bb6fdd 24#include "arm_utils.h"
69af03a2 25#include "common/plat.h"
26#include "../libpcsxcore/misc.h"
1df403c5 27#include "../libpcsxcore/cdrom.h"
799b0b87 28#include "../libpcsxcore/psemu_plugin_defs.h"
dc990066 29#include "../libpcsxcore/new_dynarec/new_dynarec.h"
384f5f43 30#include "../plugins/dfinput/pad.h"
3c70c47b 31#include "revision.h"
69af03a2 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,
1df403c5 44 MA_MAIN_SWAP_CD,
e16a7e51 45 MA_MAIN_RUN_BIOS,
69af03a2 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,
3c70c47b 58 MA_OPT_FILTERING,
69af03a2 59} menu_id;
60
3c70c47b 61enum {
62 SCALE_1_1,
63 SCALE_4_3,
64 SCALE_FULLSCREEN,
65 SCALE_CUSTOM,
66};
69af03a2 67
bd6267e6 68static int last_psx_w, last_psx_h, last_psx_bpp;
8f892648 69static int scaling, filter, cpu_clock, cpu_clock_st;
69af03a2 70static char rom_fname_reload[MAXPATHLEN];
71static char last_selected_fname[MAXPATHLEN];
4f5a1b2a 72static int warned_about_bios, region, in_type_sel;
bd6267e6 73int g_opts;
74
75// from softgpu plugin
76extern int iUseDither;
77extern int UseFrameSkip;
78extern uint32_t dwActFixes;
fba06457 79extern float fFrameRateHz;
80extern int dwFrameRateTicks;
bd6267e6 81
82// sound plugin
83extern int iUseReverb;
84extern int iUseInterpolation;
85extern int iXAPitch;
86extern int iSPUIRQWait;
87extern int iUseTimer;
88
e6eb2066 89static const char *bioses[24];
bbd837c6 90static const char *gpu_plugins[16];
91static const char *spu_plugins[16];
e6eb2066 92static int bios_sel, gpu_plugsel, spu_plugsel;
bbd837c6 93
bd6267e6 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; }
69af03a2 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{
8f892648 112 int ret = emu_check_state(slot);
3c70c47b 113 return ret == 0 ? 1 : 0;
69af03a2 114}
115
8f892648 116static int emu_save_load_game(int load, int unused)
69af03a2 117{
3c70c47b 118 int ret;
119
33f56da1 120 if (load) {
8f892648 121 ret = emu_load_state(state_slot);
33f56da1 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 }
3c70c47b 130 else
8f892648 131 ret = emu_save_state(state_slot);
3c70c47b 132
133 return ret;
69af03a2 134}
135
907b1e90 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 }
799b0b87 144 in_type = in_type_sel ? PSE_PAD_TYPE_ANALOGPAD : PSE_PAD_TYPE_STANDARD;
907b1e90 145
799b0b87 146 pl_frame_interval = Config.PsxType ? 20000 : 16667;
907b1e90 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
3c70c47b 152static void menu_set_defconfig(void)
153{
bce6b056 154 g_opts = 0;
3c70c47b 155 scaling = SCALE_4_3;
bd6267e6 156
907b1e90 157 region = 0;
799b0b87 158 in_type_sel = 0;
bd6267e6 159 Config.Xa = Config.Cdda = Config.Sio =
160 Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
161
799b0b87 162 iUseDither = 0;
163 UseFrameSkip = 1;
bd6267e6 164 dwActFixes = 1<<7;
165
166 iUseReverb = 2;
167 iUseInterpolation = 1;
cdb31c95 168 iXAPitch = 0;
169 iSPUIRQWait = 1;
bd6267e6 170 iUseTimer = 2;
907b1e90 171
172 menu_sync_config();
3c70c47b 173}
174
4f3639fa 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
cdb31c95 187// 'versioned' var, used when defaults change
188#define CE_INTVAL_V(val, ver) \
189 { #val #ver, sizeof(val), &val }
190
4f3639fa 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),
33716956 199// CE_CONFIG_STR(Cdr),
4f3639fa 200 CE_CONFIG_VAL(Xa),
201 CE_CONFIG_VAL(Sio),
202 CE_CONFIG_VAL(Mdec),
4f3639fa 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),
907b1e90 210 CE_INTVAL(region),
4f3639fa 211 CE_INTVAL(scaling),
cd6e8d0f 212 CE_INTVAL(g_layer_x),
213 CE_INTVAL(g_layer_y),
214 CE_INTVAL(g_layer_w),
215 CE_INTVAL(g_layer_h),
4f3639fa 216 CE_INTVAL(filter),
217 CE_INTVAL(state_slot),
218 CE_INTVAL(cpu_clock),
219 CE_INTVAL(g_opts),
799b0b87 220 CE_INTVAL(in_type_sel),
4f3639fa 221 CE_INTVAL(iUseDither),
222 CE_INTVAL(UseFrameSkip),
223 CE_INTVAL(dwActFixes),
224 CE_INTVAL(iUseReverb),
4f3639fa 225 CE_INTVAL(iXAPitch),
cdb31c95 226 CE_INTVAL_V(iUseInterpolation, 2),
227 CE_INTVAL_V(iSPUIRQWait, 2),
4f3639fa 228 CE_INTVAL(iUseTimer),
4f5a1b2a 229 CE_INTVAL(warned_about_bios),
4f3639fa 230};
231
1bd9ee68 232static char *get_cd_label(void)
cd6e8d0f 233{
1bd9ee68 234 static char trimlabel[33];
cd6e8d0f 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
1bd9ee68 243 return trimlabel;
244}
245
246static void make_cfg_fname(char *buf, size_t size, int is_game)
247{
cd6e8d0f 248 if (is_game)
1bd9ee68 249 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
cd6e8d0f 250 else
bbd837c6 251 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
cd6e8d0f 252}
253
43bca6fb 254static void keys_write_all(FILE *f);
255
3c70c47b 256static int menu_write_config(int is_game)
257{
4f3639fa 258 char cfgfile[MAXPATHLEN];
259 FILE *f;
260 int i;
261
cd6e8d0f 262 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
4f3639fa 263 f = fopen(cfgfile, "w");
264 if (f == NULL) {
bbd837c6 265 printf("menu_write_config: failed to open: %s\n", cfgfile);
4f3639fa 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
43bca6fb 294 keys_write_all(f);
4f3639fa 295 fclose(f);
43bca6fb 296
4f3639fa 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;
3c70c47b 310}
311
43bca6fb 312static void keys_load_all(const char *cfg);
313
3c70c47b 314static int menu_load_config(int is_game)
69af03a2 315{
4f3639fa 316 char cfgfile[MAXPATHLEN];
317 int i, ret = -1;
318 long size;
319 char *cfg;
320 FILE *f;
321
cd6e8d0f 322 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
4f3639fa 323 f = fopen(cfgfile, "r");
324 if (f == NULL) {
bbd837c6 325 printf("menu_load_config: failed to open: %s\n", cfgfile);
4f3639fa 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
907b1e90 394 menu_sync_config();
395
bbd837c6 396 // sync plugins
e6eb2066 397 for (i = bios_sel = 0; bioses[i] != NULL; i++)
398 if (strcmp(Config.Bios, bioses[i]) == 0)
399 { bios_sel = i; break; }
400
bbd837c6 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
43bca6fb 409 keys_load_all(cfg);
bbd837c6 410 ret = 0;
4f3639fa 411fail_read:
412 free(cfg);
413fail:
414 fclose(f);
415 return ret;
69af03a2 416}
417
9564e73d 418// rrrr rggg gggb bbbb
419static unsigned short fname2color(const char *fname)
420{
33716956 421 static const char *cdimg_exts[] = { ".bin", ".img", ".iso", ".cue", ".z", ".bz", ".znx", ".pbp" };
c22b95ab 422 static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub", ".table", ".index", ".sbi" };
9564e73d 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
61363062 437static void draw_savestate_bg(int slot);
438
bd6267e6 439#define MENU_ALIGN_LEFT
3c70c47b 440#define menu_init menu_init_common
69af03a2 441#include "common/menu.c"
3c70c47b 442#undef menu_init
69af03a2 443
61363062 444// a bit of black magic here
445static void draw_savestate_bg(int slot)
446{
61363062 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
3c70c47b 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)
69af03a2 516{
3c70c47b 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{
4f3639fa 540 static int old = -1;
3c70c47b 541 char buf[128];
542 int i;
543
4f3639fa 544 if (pnd_filter_list == NULL || which == old)
3c70c47b 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);
4f3639fa 556 old = which;
3c70c47b 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;
1bd9ee68 566 char buff[64];
3c70c47b 567 DIR *dir;
568
201c21e2 569 cpu_clock_st = cpu_clock = get_cpu_clock();
3c70c47b 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 }
1bd9ee68 585
586 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
587 continue;
588
589 count++;
3c70c47b 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
1bd9ee68 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)
3c70c47b 612 continue;
613
1bd9ee68 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
3c70c47b 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;
69af03a2 632}
633
201c21e2 634void menu_finish(void)
635{
636 cpu_clock = cpu_clock_st;
637 apply_cpu_clock();
638}
639
69af03a2 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 },
22a8a805 649 { "CIRCLE ", 1 << DKEY_CIRCLE },
69af03a2 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 },
43bca6fb 656 { "L3 ", 1 << DKEY_L3 },
657 { "R3 ", 1 << DKEY_R3 },
69af03a2 658 { "START ", 1 << DKEY_START },
659 { "SELECT ", 1 << DKEY_SELECT },
660 { NULL, 0 }
661};
662
663me_bind_action emuctrl_actions[] =
664{
8f892648 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 },
29a8c4f3 670 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
8f892648 671 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
69af03a2 672 { NULL, 0 }
673};
674
43bca6fb 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) {
8f892648 837 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
43bca6fb 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
69af03a2 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
cd6e8d0f 887static int mh_savecfg(int id, int keys)
69af03a2 888{
cd6e8d0f 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");
69af03a2 893
894 return 1;
895}
896
799b0b87 897static const char *men_in_type_sel[] = { "Standard (SCPH-1080)", "Analog (SCPH-1150)", NULL };
898
69af03a2 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),
799b0b87 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),
69af03a2 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
e16a7e51 924// me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
69af03a2 925 me_loop(e_menu_keyconfig, &sel, NULL);
926 return 0;
927}
928
69af03a2 929// ------------ gfx options menu ------------
930
3c70c47b 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);
3c70c47b 943
944 for (;;)
945 {
946 menu_draw_begin(0);
201c21e2 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);
3c70c47b 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
69af03a2 986static menu_entry e_menu_gfx_options[] =
987{
3c70c47b 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),
69af03a2 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
bd6267e6 1004// ------------ bios/plugins ------------
1005
1006static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
bd6267e6 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 };
cdb31c95 1041static const char h_spu_irq_wait[] = "Wait for CPU (recommended set to ON)";
1bd9ee68 1042static const char h_spu_thread[] = "Run sound emulation in main thread (recommended)";
bd6267e6 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),
1bd9ee68 1050 mee_onoff_h ("Sound in main thread", 0, iUseTimer, 2, h_spu_thread),
bd6267e6 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
4f5a1b2a 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";
bbd837c6 1064static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1065 "for plugin change to take effect";
e6eb2066 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";
bbd837c6 1068
bd6267e6 1069static menu_entry e_menu_plugin_options[] =
1070{
e6eb2066 1071 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
bbd837c6 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),
bd6267e6 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
e16a7e51 1079static menu_entry e_menu_main[];
1080
bd6267e6 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);
bbd837c6 1085
e6eb2066 1086 // sync BIOS/plugins
1087 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
bbd837c6 1088 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1089 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
e16a7e51 1090 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
bbd837c6 1091
bd6267e6 1092 return 0;
1093}
1094
1095// ------------ adv options menu ------------
1096
8f892648 1097static const char h_cfg_cpul[] = "Shows CPU usage in %";
1bd9ee68 1098static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
bd6267e6 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";
b5e7e49a 1106static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1107 "Might be useful to overcome some dynarec bugs";
bd6267e6 1108
1109static menu_entry e_menu_adv_options[] =
1110{
fba06457 1111 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
bce6b056 1112 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
bd6267e6 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),
b5e7e49a 1119 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
bd6267e6 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
69af03a2 1130// ------------ options menu ------------
1131
69af03a2 1132static int mh_restore_defaults(int id, int keys)
1133{
3c70c47b 1134 menu_set_defconfig();
69af03a2 1135 me_update_msg("defaults restored");
1136 return 1;
1137}
1138
907b1e90 1139static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
bd6267e6 1140/*
69af03a2 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";
bd6267e6 1144*/
1145static const char h_restore_def[] = "Switches back to default / recommended\n"
1146 "configuration";
9f4a4237 1147static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
69af03a2 1148
1149static menu_entry e_menu_options[] =
1150{
bd6267e6 1151// mee_range ("Save slot", 0, state_slot, 0, 9),
1152// mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
9f4a4237 1153 mee_onoff_h ("Frameskip", 0, UseFrameSkip, 1, h_frameskip),
bd6267e6 1154 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
907b1e90 1155 mee_enum ("Region", 0, region, men_region),
3c70c47b 1156 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
69af03a2 1157 mee_handler ("[Display]", menu_loop_gfx_options),
bd6267e6 1158 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
69af03a2 1159 mee_handler ("[Advanced]", menu_loop_adv_options),
cd6e8d0f 1160 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1161 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
bd6267e6 1162 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
69af03a2 1163 mee_end,
1164};
1165
1166static int menu_loop_options(int id, int keys)
1167{
1168 static int sel = 0;
1169 int i;
1170
1171 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
3c70c47b 1172 e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
e16a7e51 1173 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
69af03a2 1174
1175 me_loop(e_menu_options, &sel, NULL);
1176
1177 return 0;
1178}
1179
1180// ------------ debug menu ------------
1181
72bb6fdd 1182static void draw_frame_debug(GPUFreeze_t *gpuf)
69af03a2 1183{
72bb6fdd 1184 int w = min(g_menuscreen_w, 1024);
1185 int h = min(g_menuscreen_h, 512);
1186 u16 *d = g_menuscreen_ptr;
1187 u16 *s = (u16 *)gpuf->psxVRam;
1188 char buff[64];
1189 int ty = 1;
1190
1191 gpuf->ulFreezeVersion = 1;
1192 if (GPU_freeze != NULL)
1193 GPU_freeze(1, gpuf);
1194
1195 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1196 bgr555_to_rgb565(d, s, w * 2);
1197
3c70c47b 1198 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
72bb6fdd 1199 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1200 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1201 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1202 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
69af03a2 1203}
1204
1205static void debug_menu_loop(void)
1206{
72bb6fdd 1207 GPUFreeze_t *gpuf;
69af03a2 1208 int inp;
1209
72bb6fdd 1210 gpuf = malloc(sizeof(*gpuf));
1211 if (gpuf == NULL)
1212 return;
1213
69af03a2 1214 while (1)
1215 {
72bb6fdd 1216 menu_draw_begin(0);
1217 draw_frame_debug(gpuf);
69af03a2 1218 menu_draw_end();
1219
1220 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1221 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1222 if (inp & PBTN_MBACK)
72bb6fdd 1223 break;
69af03a2 1224 }
72bb6fdd 1225
1226 free(gpuf);
69af03a2 1227}
1228
1229// ------------ main menu ------------
1230
4f5a1b2a 1231static void menu_bios_warn(void)
1232{
1233 int inp;
1234 static const char msg[] =
1235 "You don't seem to have copied any BIOS files to\n"
1236 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1237 "While many games work fine with fake (HLE) BIOS,\n"
1238 "others (like MGS and FF8) require BIOS to work.\n"
1239 "After copying the file, you'll also need to\n"
1240 "select it in the emu's options->[BIOS/Plugins]\n\n"
1241 "The file is usually named SCPH1001.BIN, but\n"
1242 "other not compressed files can be used too.\n\n"
1243 "Press (B) or (X) to continue";
1244
1245 while (1)
1246 {
4f5a1b2a 1247 draw_menu_message(msg, NULL);
4f5a1b2a 1248
1249 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1250 if (inp & (PBTN_MBACK|PBTN_MOK))
1251 return;
1252 }
1253}
1254
1255// ------------ main menu ------------
1256
bd6267e6 1257void OnFile_Exit();
1258
1bd9ee68 1259static void draw_frame_main(void)
1260{
1261 if (CdromId[0] != 0) {
1262 char buff[64];
c22b95ab 1263 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1264 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1265 Config.HLE ? "HLE" : "BIOS");
1bd9ee68 1266 smalltext_out16(4, 1, buff, 0x105f);
1267 }
1268}
1269
1270static void draw_frame_credits(void)
1271{
1272 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1273}
1274
4f5a1b2a 1275static const char credits_text[] =
1276 "PCSX-ReARMed\n\n"
1277 "(C) 1999-2003 PCSX Team\n"
1278 "(C) 2005-2009 PCSX-df Team\n"
1279 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1280 "GPU and SPU code by Pete Bernert\n"
1281 " and the P.E.Op.S. team\n"
1282 "ARM recompiler (C) 2009-2011 Ari64\n"
1283 "PCSX4ALL plugins by PCSX4ALL team\n"
1284 " Chui, Franxis, Unai\n\n"
1285 "integration, optimization and\n"
1286 " frontend (C) 2010-2011 notaz\n";
69af03a2 1287
e16a7e51 1288static int reset_game(void)
1289{
1290 // sanity check
1291 if (bios_sel == 0 && !Config.HLE)
1292 return -1;
1293
1294 ClosePlugins();
1295 OpenPlugins();
1296 SysReset();
1297 if (CheckCdrom() != -1) {
1298 LoadCdrom();
1299 }
1300 return 0;
1301}
1302
1303static int run_bios(void)
1304{
1305 if (bios_sel == 0)
1306 return -1;
1307
1308 ready_to_go = 0;
1309 pl_fbdev_buf = NULL;
1310
1311 ClosePlugins();
1312 set_cd_image(NULL);
1313 LoadPlugins();
c22b95ab 1314 pcnt_hook_plugins();
e16a7e51 1315 NetOpened = 0;
1316 if (OpenPlugins() == -1) {
1317 me_update_msg("failed to open plugins");
1318 return -1;
1319 }
1320 plugin_call_rearmed_cbs();
1321
1322 CdromId[0] = '\0';
1323 CdromLabel[0] = '\0';
1324
1325 SysReset();
1326
1327 ready_to_go = 1;
1328 return 0;
1329}
1330
bbd837c6 1331static int run_cd_image(const char *fname)
69af03a2 1332{
69af03a2 1333 ready_to_go = 0;
fba06457 1334 pl_fbdev_buf = NULL;
69af03a2 1335
fba06457 1336 ClosePlugins();
bbd837c6 1337 set_cd_image(fname);
69af03a2 1338 LoadPlugins();
c22b95ab 1339 pcnt_hook_plugins();
69af03a2 1340 NetOpened = 0;
1341 if (OpenPlugins() == -1) {
1342 me_update_msg("failed to open plugins");
bbd837c6 1343 return -1;
69af03a2 1344 }
201c21e2 1345 plugin_call_rearmed_cbs();
69af03a2 1346
69af03a2 1347 if (CheckCdrom() == -1) {
1348 // Only check the CD if we are starting the console with a CD
1349 ClosePlugins();
1350 me_update_msg("unsupported/invalid CD image");
bbd837c6 1351 return -1;
69af03a2 1352 }
1353
bbd837c6 1354 SysReset();
1355
69af03a2 1356 // Read main executable directly from CDRom and start it
1357 if (LoadCdrom() == -1) {
1358 ClosePlugins();
1359 me_update_msg("failed to load CD image");
bbd837c6 1360 return -1;
69af03a2 1361 }
1362
1363 ready_to_go = 1;
bbd837c6 1364 return 0;
69af03a2 1365}
1366
bbd837c6 1367static int romsel_run(void)
69af03a2 1368{
bbd837c6 1369 int prev_gpu, prev_spu;
1370 char *fname;
1371
1372 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1373 if (fname == NULL)
1374 return -1;
69af03a2 1375
bbd837c6 1376 printf("selected file: %s\n", fname);
1377
dc990066 1378 new_dynarec_clear_full();
1379
bbd837c6 1380 if (run_cd_image(fname) != 0)
1381 return -1;
1382
1383 prev_gpu = gpu_plugsel;
1384 prev_spu = spu_plugsel;
1385 if (menu_load_config(1) != 0)
1386 menu_load_config(0);
1387
1388 // check for plugin changes, have to repeat
1389 // loading if game config changed plugins to reload them
1390 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1391 printf("plugin change detected, reloading plugins..\n");
1392 if (run_cd_image(fname) != 0)
1393 return -1;
1394 }
1395
1396 strcpy(last_selected_fname, rom_fname_reload);
1397 return 0;
1398}
1399
1df403c5 1400static int swap_cd_image(void)
1401{
1402 char *fname;
1403
1404 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1405 if (fname == NULL)
1406 return -1;
1407
1408 printf("selected file: %s\n", fname);
1409
1410 CdromId[0] = '\0';
1411 CdromLabel[0] = '\0';
1412
1413 set_cd_image(fname);
1414 if (ReloadCdromPlugin() < 0) {
1415 me_update_msg("failed to load cdr plugin");
1416 return -1;
1417 }
1418 if (CDR_open() < 0) {
1419 me_update_msg("failed to open cdr plugin");
1420 return -1;
1421 }
1422
1423 SetCdOpenCaseTime(time(NULL) + 2);
1424 LidInterrupt();
1425
1426 strcpy(last_selected_fname, rom_fname_reload);
1427 return 0;
1428}
1429
bbd837c6 1430static int main_menu_handler(int id, int keys)
1431{
69af03a2 1432 switch (id)
1433 {
1434 case MA_MAIN_RESUME_GAME:
3c70c47b 1435 if (ready_to_go)
1436 return 1;
69af03a2 1437 break;
1438 case MA_MAIN_SAVE_STATE:
1439 if (ready_to_go)
1440 return menu_loop_savestate(0);
1441 break;
1442 case MA_MAIN_LOAD_STATE:
1443 if (ready_to_go)
1444 return menu_loop_savestate(1);
1445 break;
1446 case MA_MAIN_RESET_GAME:
e16a7e51 1447 if (ready_to_go && reset_game() == 0)
3c70c47b 1448 return 1;
69af03a2 1449 break;
1450 case MA_MAIN_LOAD_ROM:
bbd837c6 1451 if (romsel_run() == 0)
69af03a2 1452 return 1;
1453 break;
1df403c5 1454 case MA_MAIN_SWAP_CD:
1455 if (swap_cd_image() == 0)
1456 return 1;
1457 break;
e16a7e51 1458 case MA_MAIN_RUN_BIOS:
1459 if (run_bios() == 0)
1460 return 1;
1461 break;
69af03a2 1462 case MA_MAIN_CREDITS:
4f5a1b2a 1463 draw_menu_message(credits_text, draw_frame_credits);
69af03a2 1464 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1465 break;
1466 case MA_MAIN_EXIT:
bd6267e6 1467 OnFile_Exit();
1468 break;
69af03a2 1469 default:
1470 lprintf("%s: something unknown selected\n", __FUNCTION__);
1471 break;
1472 }
1473
1474 return 0;
1475}
1476
1477static menu_entry e_menu_main[] =
1478{
1479 mee_label (""),
1480 mee_label (""),
1481 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1482 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1483 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1484 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1485 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1df403c5 1486 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
e16a7e51 1487 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
69af03a2 1488 mee_handler ("Options", menu_loop_options),
1489 mee_handler ("Controls", menu_loop_keyconfig),
1490 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1491 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1492 mee_end,
1493};
1494
3c70c47b 1495// ----------------------------
1496
bd6267e6 1497static void menu_leave_emu(void);
1498
69af03a2 1499void menu_loop(void)
1500{
1501 static int sel = 0;
1502
bd6267e6 1503 menu_leave_emu();
69af03a2 1504
4f5a1b2a 1505 if (bioses[1] == NULL && !warned_about_bios) {
1506 menu_bios_warn();
1507 warned_about_bios = 1;
1508 }
1509
69af03a2 1510 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
e16a7e51 1511 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1512 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
69af03a2 1513 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1df403c5 1514 me_enable(e_menu_main, MA_MAIN_SWAP_CD, ready_to_go);
e16a7e51 1515 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
69af03a2 1516
69af03a2 1517 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1518
1519 do {
1bd9ee68 1520 me_loop(e_menu_main, &sel, draw_frame_main);
69af03a2 1521 } while (!ready_to_go);
1522
1523 /* wait until menu, ok, back is released */
1524 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1525 ;
1526
1527 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1528
3c70c47b 1529 menu_prepare_emu();
1530}
1531
e6eb2066 1532static void scan_bios_plugins(void)
bbd837c6 1533{
1534 char fname[MAXPATHLEN];
1535 struct dirent *ent;
e6eb2066 1536 int bios_i, gpu_i, spu_i;
bbd837c6 1537 char *p;
1538 DIR *dir;
1539
e6eb2066 1540 bioses[0] = "HLE";
bbd837c6 1541 gpu_plugins[0] = "builtin_gpu";
1542 spu_plugins[0] = "builtin_spu";
e6eb2066 1543 bios_i = gpu_i = spu_i = 1;
1544
1545 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1546 dir = opendir(fname);
1547 if (dir == NULL) {
1548 perror("scan_bios_plugins bios opendir");
1549 goto do_plugins;
1550 }
1551
1552 while (1) {
1553 struct stat st;
1554
1555 errno = 0;
1556 ent = readdir(dir);
1557 if (ent == NULL) {
1558 if (errno != 0)
1559 perror("readdir");
1560 break;
1561 }
1562
1563 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1564 continue;
1565
1566 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1567 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1568 printf("bad BIOS file: %s\n", ent->d_name);
1569 continue;
1570 }
1571
1572 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1573 bioses[bios_i++] = strdup(ent->d_name);
1574 continue;
1575 }
1576
1577 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1578 }
1579
1580 closedir(dir);
bbd837c6 1581
e6eb2066 1582do_plugins:
bbd837c6 1583 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1584 dir = opendir(fname);
1585 if (dir == NULL) {
e6eb2066 1586 perror("scan_bios_plugins opendir");
bbd837c6 1587 return;
1588 }
1589
1590 while (1) {
1591 void *h, *tmp;
1592
1593 errno = 0;
1594 ent = readdir(dir);
1595 if (ent == NULL) {
1596 if (errno != 0)
1597 perror("readdir");
1598 break;
1599 }
1600 p = strstr(ent->d_name, ".so");
1601 if (p == NULL)
1602 continue;
1603
1604 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1605 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1606 if (h == NULL) {
1607 fprintf(stderr, "%s\n", dlerror());
1608 continue;
1609 }
1610
1611 // now what do we have here?
1612 tmp = dlsym(h, "GPUinit");
1613 if (tmp) {
1614 dlclose(h);
1615 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1616 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1617 continue;
1618 }
1619
1620 tmp = dlsym(h, "SPUinit");
1621 if (tmp) {
1622 dlclose(h);
1623 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1624 spu_plugins[spu_i++] = strdup(ent->d_name);
1625 continue;
1626 }
1627
1628 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1629 dlclose(h);
1630 }
1631
1632 closedir(dir);
1633}
1634
3c70c47b 1635void menu_init(void)
1636{
4f3639fa 1637 char buff[MAXPATHLEN];
1638
1639 strcpy(last_selected_fname, "/media");
1640
e6eb2066 1641 scan_bios_plugins();
4f3639fa 1642 pnd_menu_init();
1643 menu_init_common();
1644
3c70c47b 1645 menu_set_defconfig();
1646 menu_load_config(0);
3c70c47b 1647 last_psx_w = 320;
1648 last_psx_h = 240;
bd6267e6 1649 last_psx_bpp = 16;
1650
4f3639fa 1651 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1652 if (g_menubg_src_ptr == NULL)
1653 exit(1);
1654 emu_make_path(buff, "skin/background.png", sizeof(buff));
1655 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
3c70c47b 1656}
1657
bd6267e6 1658void menu_notify_mode_change(int w, int h, int bpp)
3c70c47b 1659{
1660 last_psx_w = w;
1661 last_psx_h = h;
bd6267e6 1662 last_psx_bpp = bpp;
3c70c47b 1663
1664 if (scaling == SCALE_1_1) {
1665 g_layer_x = 800/2 - w/2; g_layer_y = 480/2 - h/2;
1666 g_layer_w = w; g_layer_h = h;
3c70c47b 1667 }
1668}
1669
bd6267e6 1670static void menu_leave_emu(void)
1671{
6d1a1ac2 1672 if (GPU_close != NULL) {
1673 int ret = GPU_close();
1674 if (ret)
1675 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1676 }
bd6267e6 1677
4f3639fa 1678 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
fba06457 1679 if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
bd6267e6 1680 int x = max(0, g_menuscreen_w - last_psx_w);
1681 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1682 int w = min(g_menuscreen_w, last_psx_w);
1683 int h = min(g_menuscreen_h, last_psx_h);
4f3639fa 1684 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
bd6267e6 1685 u16 *s = pl_fbdev_buf;
1686
1687 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
4f3639fa 1688 menu_darken_bg(d, s, w, 0);
bd6267e6 1689 }
fba06457 1690
1bd9ee68 1691 if (ready_to_go)
1692 cpu_clock = get_cpu_clock();
1693
fba06457 1694 plat_video_menu_enter(ready_to_go);
bd6267e6 1695}
1696
3c70c47b 1697void menu_prepare_emu(void)
1698{
b5e7e49a 1699 R3000Acpu *prev_cpu = psxCpu;
1700
fba06457 1701 plat_video_menu_leave();
1702
3c70c47b 1703 switch (scaling) {
1704 case SCALE_1_1:
bd6267e6 1705 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
3c70c47b 1706 break;
1707 case SCALE_4_3:
1708 g_layer_x = 80; g_layer_y = 0;
1709 g_layer_w = 640; g_layer_h = 480;
1710 break;
1711 case SCALE_FULLSCREEN:
1712 g_layer_x = 0; g_layer_y = 0;
1713 g_layer_w = 800; g_layer_h = 480;
1714 break;
1715 case SCALE_CUSTOM:
1716 break;
1717 }
3c70c47b 1718 apply_filter(filter);
1719 apply_cpu_clock();
bd6267e6 1720
b5e7e49a 1721 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1722 if (psxCpu != prev_cpu)
1723 // note that this does not really reset, just clears drc caches
1724 psxCpu->Reset();
1725
bd6267e6 1726 // core doesn't care about Config.Cdda changes,
1727 // so handle them manually here
1728 if (Config.Cdda)
1729 CDR_stop();
6d1a1ac2 1730
907b1e90 1731 menu_sync_config();
fba06457 1732
6d1a1ac2 1733 if (GPU_open != NULL) {
6d1a1ac2 1734 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1735 if (ret)
1736 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1737 }
384f5f43 1738
1739 dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
69af03a2 1740}
1741
1742void me_update_msg(const char *msg)
1743{
1744 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1745 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1746
1747 menu_error_time = plat_get_ticks_ms();
1748 lprintf("msg: %s\n", menu_error_msg);
1749}
1750