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