frontend: make -psxout cmd arg override config
[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
cefe86b7 570static void apply_lcdrate(int pal)
571{
572 static int old = -1;
573 char buf[128];
574
575 if (pal == old)
576 return;
577
578 snprintf(buf, sizeof(buf), "%s/op_lcdrate.sh %d",
579 pnd_script_base, pal ? 50 : 60);
580 system(buf);
581 old = pal;
582}
583
3c70c47b 584static menu_entry e_menu_gfx_options[];
585
586static void pnd_menu_init(void)
587{
588 struct dirent *ent;
589 int i, count = 0;
590 char **mfilters;
1bd9ee68 591 char buff[64];
3c70c47b 592 DIR *dir;
593
201c21e2 594 cpu_clock_st = cpu_clock = get_cpu_clock();
3c70c47b 595
596 dir = opendir("/etc/pandora/conf/dss_fir");
597 if (dir == NULL) {
598 perror("filter opendir");
599 return;
600 }
601
602 while (1) {
603 errno = 0;
604 ent = readdir(dir);
605 if (ent == NULL) {
606 if (errno != 0)
607 perror("readdir");
608 break;
609 }
1bd9ee68 610
611 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
612 continue;
613
614 count++;
3c70c47b 615 }
616
617 if (count == 0)
618 return;
619
620 mfilters = calloc(count + 1, sizeof(mfilters[0]));
621 if (mfilters == NULL)
622 return;
623
624 rewinddir(dir);
625 for (i = 0; (ent = readdir(dir)); ) {
626 size_t len;
627
1bd9ee68 628 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
629 continue;
630
631 len = strlen(ent->d_name);
632
633 // skip pre-HF5 extra files
634 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
635 continue;
636 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
3c70c47b 637 continue;
638
1bd9ee68 639 // have to cut "_up_h" for pre-HF5
640 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
641 len -= 5;
642
3c70c47b 643 if (len > sizeof(buff) - 1)
644 continue;
645
646 strncpy(buff, ent->d_name, len);
647 buff[len] = 0;
648 mfilters[i] = strdup(buff);
649 if (mfilters[i] != NULL)
650 i++;
651 }
652 closedir(dir);
653
654 i = me_id2offset(e_menu_gfx_options, MA_OPT_FILTERING);
655 e_menu_gfx_options[i].data = (void *)mfilters;
656 pnd_filter_list = mfilters;
69af03a2 657}
658
201c21e2 659void menu_finish(void)
660{
661 cpu_clock = cpu_clock_st;
662 apply_cpu_clock();
663}
664
69af03a2 665// -------------- key config --------------
666
667me_bind_action me_ctrl_actions[] =
668{
669 { "UP ", 1 << DKEY_UP},
670 { "DOWN ", 1 << DKEY_DOWN },
671 { "LEFT ", 1 << DKEY_LEFT },
672 { "RIGHT ", 1 << DKEY_RIGHT },
673 { "TRIANGLE", 1 << DKEY_TRIANGLE },
22a8a805 674 { "CIRCLE ", 1 << DKEY_CIRCLE },
69af03a2 675 { "CROSS ", 1 << DKEY_CROSS },
676 { "SQUARE ", 1 << DKEY_SQUARE },
677 { "L1 ", 1 << DKEY_L1 },
678 { "R1 ", 1 << DKEY_R1 },
679 { "L2 ", 1 << DKEY_L2 },
680 { "R2 ", 1 << DKEY_R2 },
43bca6fb 681 { "L3 ", 1 << DKEY_L3 },
682 { "R3 ", 1 << DKEY_R3 },
69af03a2 683 { "START ", 1 << DKEY_START },
684 { "SELECT ", 1 << DKEY_SELECT },
685 { NULL, 0 }
686};
687
688me_bind_action emuctrl_actions[] =
689{
8f892648 690 { "Save State ", 1 << SACTION_SAVE_STATE },
691 { "Load State ", 1 << SACTION_LOAD_STATE },
692 { "Prev Save Slot ", 1 << SACTION_PREV_SSLOT },
693 { "Next Save Slot ", 1 << SACTION_NEXT_SSLOT },
694 { "Toggle Frameskip ", 1 << SACTION_TOGGLE_FSKIP },
29a8c4f3 695 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
8f892648 696 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
69af03a2 697 { NULL, 0 }
698};
699
43bca6fb 700static char *mystrip(char *str)
701{
702 int i, len;
703
704 len = strlen(str);
705 for (i = 0; i < len; i++)
706 if (str[i] != ' ') break;
707 if (i > 0) memmove(str, str + i, len - i + 1);
708
709 len = strlen(str);
710 for (i = len - 1; i >= 0; i--)
711 if (str[i] != ' ') break;
712 str[i+1] = 0;
713
714 return str;
715}
716
717static void get_line(char *d, size_t size, const char *s)
718{
719 const char *pe;
720 size_t len;
721
722 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
723 ;
724 len = pe - s;
725 if (len > size - 1)
726 len = size - 1;
727 strncpy(d, s, len);
728 d[len] = 0;
729
730 mystrip(d);
731}
732
733static void keys_write_all(FILE *f)
734{
735 int d;
736
737 for (d = 0; d < IN_MAX_DEVS; d++)
738 {
739 const int *binds = in_get_dev_binds(d);
740 const char *name = in_get_dev_name(d, 0, 0);
741 int k, count = 0;
742
743 if (binds == NULL || name == NULL)
744 continue;
745
746 fprintf(f, "binddev = %s\n", name);
747 in_get_config(d, IN_CFG_BIND_COUNT, &count);
748
749 for (k = 0; k < count; k++)
750 {
751 int i, kbinds, mask;
752 char act[32];
753
754 act[0] = act[31] = 0;
755 name = in_get_key_name(d, k);
756
757 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
758 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
759 mask = me_ctrl_actions[i].mask;
760 if (mask & kbinds) {
761 strncpy(act, me_ctrl_actions[i].name, 31);
762 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
763 kbinds &= ~mask;
764 }
765 mask = me_ctrl_actions[i].mask << 16;
766 if (mask & kbinds) {
767 strncpy(act, me_ctrl_actions[i].name, 31);
768 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
769 kbinds &= ~mask;
770 }
771 }
772
773 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
774 for (i = 0; kbinds && i < ARRAY_SIZE(emuctrl_actions) - 1; i++) {
775 mask = emuctrl_actions[i].mask;
776 if (mask & kbinds) {
777 strncpy(act, emuctrl_actions[i].name, 31);
778 fprintf(f, "bind %s = %s\n", name, mystrip(act));
779 kbinds &= ~mask;
780 }
781 }
782 }
783 }
784}
785
786static int parse_bind_val(const char *val, int *type)
787{
788 int i;
789
790 *type = IN_BINDTYPE_NONE;
791 if (val[0] == 0)
792 return 0;
793
794 if (strncasecmp(val, "player", 6) == 0)
795 {
796 int player, shift = 0;
797 player = atoi(val + 6) - 1;
798
799 if ((unsigned int)player > 1)
800 return -1;
801 if (player == 1)
802 shift = 16;
803
804 *type = IN_BINDTYPE_PLAYER12;
805 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
806 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
807 return me_ctrl_actions[i].mask << shift;
808 }
809 }
810 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
811 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
812 *type = IN_BINDTYPE_EMU;
813 return emuctrl_actions[i].mask;
814 }
815 }
816
817 return -1;
818}
819
820static void keys_load_all(const char *cfg)
821{
822 char dev[256], key[128], *act;
823 const char *p;
824 int bind, bindtype;
825 int dev_id;
826
827 p = cfg;
828 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
829 p += 10;
830
831 get_line(dev, sizeof(dev), p);
832 dev_id = in_config_parse_dev(dev);
833 if (dev_id < 0) {
834 printf("input: can't handle dev: %s\n", dev);
835 continue;
836 }
837
838 in_unbind_all(dev_id, -1, -1);
839 while ((p = strstr(p, "bind"))) {
840 if (strncmp(p, "binddev = ", 10) == 0)
841 break;
842
843 p += 4;
844 if (*p != ' ') {
845 printf("input: parse error: %16s..\n", p);
846 continue;
847 }
848
849 get_line(key, sizeof(key), p);
850 act = strchr(key, '=');
851 if (act == NULL) {
852 printf("parse failed: %16s..\n", p);
853 continue;
854 }
855 *act = 0;
856 act++;
857 mystrip(key);
858 mystrip(act);
859
860 bind = parse_bind_val(act, &bindtype);
861 if (bind != -1 && bind != 0) {
8f892648 862 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
43bca6fb 863 in_config_bind_key(dev_id, key, bind, bindtype);
864 }
865 else
866 lprintf("config: unhandled action \"%s\"\n", act);
867 }
868 }
ef94866c 869 in_clean_binds();
43bca6fb 870}
871
69af03a2 872static int key_config_loop_wrap(int id, int keys)
873{
874 switch (id) {
875 case MA_CTRL_PLAYER1:
876 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
877 break;
878 case MA_CTRL_PLAYER2:
879 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
880 break;
881 case MA_CTRL_EMU:
882 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
883 break;
884 default:
885 break;
886 }
887 return 0;
888}
889
890static const char *mgn_dev_name(int id, int *offs)
891{
892 const char *name = NULL;
893 static int it = 0;
894
895 if (id == MA_CTRL_DEV_FIRST)
896 it = 0;
897
898 for (; it < IN_MAX_DEVS; it++) {
899 name = in_get_dev_name(it, 1, 1);
900 if (name != NULL)
901 break;
902 }
903
904 it++;
905 return name;
906}
907
908static const char *mgn_saveloadcfg(int id, int *offs)
909{
910 return "";
911}
912
cd6e8d0f 913static int mh_savecfg(int id, int keys)
69af03a2 914{
cd6e8d0f 915 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
916 me_update_msg("config saved");
917 else
918 me_update_msg("failed to write config");
69af03a2 919
920 return 1;
921}
922
ef94866c 923static int mh_input_rescan(int id, int keys)
924{
925 //menu_sync_config();
926 pandora_rescan_inputs();
927 me_update_msg("rescan complete.");
928
929 return 0;
930}
931
799b0b87 932static const char *men_in_type_sel[] = { "Standard (SCPH-1080)", "Analog (SCPH-1150)", NULL };
ef94866c 933static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
799b0b87 934
69af03a2 935static menu_entry e_menu_keyconfig[] =
936{
937 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
938 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
939 mee_handler_id("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap),
799b0b87 940 mee_label (""),
941 mee_enum ("Controller", 0, in_type_sel, men_in_type_sel),
ef94866c 942 mee_onoff_h ("Nubs as buttons", 0, in_evdev_allow_abs_only, 1, h_nub_btns),
799b0b87 943 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
944 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
ef94866c 945 mee_handler ("Rescan devices", mh_input_rescan),
69af03a2 946 mee_label (""),
947 mee_label ("Input devices:"),
948 mee_label_mk (MA_CTRL_DEV_FIRST, mgn_dev_name),
949 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
950 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
951 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
952 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
953 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
954 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
955 mee_end,
956};
957
958static int menu_loop_keyconfig(int id, int keys)
959{
960 static int sel = 0;
961
e16a7e51 962// me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
69af03a2 963 me_loop(e_menu_keyconfig, &sel, NULL);
964 return 0;
965}
966
69af03a2 967// ------------ gfx options menu ------------
968
3c70c47b 969static const char *men_scaler[] = { "1x1", "scaled 4:3", "fullscreen", "custom", NULL };
970static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
971 "using d-pad or move it using R+d-pad";
972static const char *men_dummy[] = { NULL };
973
974static int menu_loop_cscaler(int id, int keys)
975{
976 unsigned int inp;
977
978 scaling = SCALE_CUSTOM;
979
980 omap_enable_layer(1);
3c70c47b 981
982 for (;;)
983 {
984 menu_draw_begin(0);
201c21e2 985 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
986 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
987 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
3c70c47b 988 menu_draw_end();
989
990 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
991 if (inp & PBTN_UP) g_layer_y--;
992 if (inp & PBTN_DOWN) g_layer_y++;
993 if (inp & PBTN_LEFT) g_layer_x--;
994 if (inp & PBTN_RIGHT) g_layer_x++;
995 if (!(inp & PBTN_R)) {
996 if (inp & PBTN_UP) g_layer_h += 2;
997 if (inp & PBTN_DOWN) g_layer_h -= 2;
998 if (inp & PBTN_LEFT) g_layer_w += 2;
999 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1000 }
1001 if (inp & (PBTN_MOK|PBTN_MBACK))
1002 break;
1003
1004 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1005 if (g_layer_x < 0) g_layer_x = 0;
1006 if (g_layer_x > 640) g_layer_x = 640;
1007 if (g_layer_y < 0) g_layer_y = 0;
1008 if (g_layer_y > 420) g_layer_y = 420;
1009 if (g_layer_w < 160) g_layer_w = 160;
1010 if (g_layer_h < 60) g_layer_h = 60;
1011 if (g_layer_x + g_layer_w > 800)
1012 g_layer_w = 800 - g_layer_x;
1013 if (g_layer_y + g_layer_h > 480)
1014 g_layer_h = 480 - g_layer_y;
1015 omap_enable_layer(1);
1016 }
1017 }
1018
1019 omap_enable_layer(0);
1020
1021 return 0;
1022}
1023
69af03a2 1024static menu_entry e_menu_gfx_options[] =
1025{
3c70c47b 1026 mee_enum ("Scaler", 0, scaling, men_scaler),
1027 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
1028// mee_onoff ("Vsync", 0, vsync, 1),
1029 mee_cust_h ("Setup custom scaler", 0, menu_loop_cscaler, NULL, h_cscaler),
69af03a2 1030 mee_end,
1031};
1032
1033static int menu_loop_gfx_options(int id, int keys)
1034{
1035 static int sel = 0;
1036
1037 me_loop(e_menu_gfx_options, &sel, NULL);
1038
1039 return 0;
1040}
1041
bd6267e6 1042// ------------ bios/plugins ------------
1043
1044static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
bd6267e6 1045static const char h_gpu_0[] = "Needed for Chrono Cross";
1046static const char h_gpu_1[] = "Capcom fighting games";
1047static const char h_gpu_2[] = "Black screens in Lunar";
1048static const char h_gpu_3[] = "Compatibility mode";
1049static const char h_gpu_6[] = "Pandemonium 2";
1050static const char h_gpu_7[] = "Skip every second frame";
1051static const char h_gpu_8[] = "Needed by Dark Forces";
1052static const char h_gpu_9[] = "better g-colors, worse textures";
1053static const char h_gpu_10[] = "Toggle busy flags after drawing";
1054
1055static menu_entry e_menu_plugin_gpu[] =
1056{
1057 mee_enum ("Dithering", 0, iUseDither, men_gpu_dithering),
1058 mee_onoff_h ("Odd/even bit hack", 0, dwActFixes, 1<<0, h_gpu_0),
1059 mee_onoff_h ("Expand screen width", 0, dwActFixes, 1<<1, h_gpu_1),
1060 mee_onoff_h ("Ignore brightness color", 0, dwActFixes, 1<<2, h_gpu_2),
1061 mee_onoff_h ("Disable coordinate check", 0, dwActFixes, 1<<3, h_gpu_3),
1062 mee_onoff_h ("Lazy screen update", 0, dwActFixes, 1<<6, h_gpu_6),
1063 mee_onoff_h ("Old frame skipping", 0, dwActFixes, 1<<7, h_gpu_7),
1064 mee_onoff_h ("Repeated flat tex triangles ",0,dwActFixes, 1<<8, h_gpu_8),
1065 mee_onoff_h ("Draw quads with triangles", 0, dwActFixes, 1<<9, h_gpu_9),
1066 mee_onoff_h ("Fake 'gpu busy' states", 0, dwActFixes, 1<<10, h_gpu_10),
1067 mee_end,
1068};
1069
1070static int menu_loop_plugin_gpu(int id, int keys)
1071{
1072 static int sel = 0;
1073 me_loop(e_menu_plugin_gpu, &sel, NULL);
1074 return 0;
1075}
1076
1077static const char *men_spu_reverb[] = { "Off", "Fake", "On", NULL };
1078static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
cdb31c95 1079static const char h_spu_irq_wait[] = "Wait for CPU (recommended set to ON)";
1bd9ee68 1080static const char h_spu_thread[] = "Run sound emulation in main thread (recommended)";
bd6267e6 1081
1082static menu_entry e_menu_plugin_spu[] =
1083{
1084 mee_enum ("Reverb", 0, iUseReverb, men_spu_reverb),
1085 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
1086 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
1087 mee_onoff_h ("SPU IRQ Wait", 0, iSPUIRQWait, 1, h_spu_irq_wait),
1bd9ee68 1088 mee_onoff_h ("Sound in main thread", 0, iUseTimer, 2, h_spu_thread),
bd6267e6 1089 mee_end,
1090};
1091
1092static int menu_loop_plugin_spu(int id, int keys)
1093{
1094 static int sel = 0;
1095 me_loop(e_menu_plugin_spu, &sel, NULL);
1096 return 0;
1097}
1098
4f5a1b2a 1099static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in savestates\n"
1100 "and can't be changed there. Must save config and reload\n"
1101 "the game for change to take effect";
bbd837c6 1102static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1103 "for plugin change to take effect";
e6eb2066 1104static const char h_gpu[] = "Configure built-in P.E.Op.S. SoftGL Driver V1.17";
1105static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
bbd837c6 1106
bd6267e6 1107static menu_entry e_menu_plugin_options[] =
1108{
e6eb2066 1109 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
bbd837c6 1110 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1111 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_xpu),
bd6267e6 1112 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu, h_gpu),
1113 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1114 mee_end,
1115};
1116
e16a7e51 1117static menu_entry e_menu_main[];
1118
bd6267e6 1119static int menu_loop_plugin_options(int id, int keys)
1120{
1121 static int sel = 0;
1122 me_loop(e_menu_plugin_options, &sel, NULL);
bbd837c6 1123
e6eb2066 1124 // sync BIOS/plugins
1125 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
bbd837c6 1126 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1127 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
e16a7e51 1128 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
bbd837c6 1129
bd6267e6 1130 return 0;
1131}
1132
1133// ------------ adv options menu ------------
1134
8f892648 1135static const char h_cfg_cpul[] = "Shows CPU usage in %";
1bd9ee68 1136static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
bd6267e6 1137static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1138static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1139 "(proper .cue/.bin dump is needed otherwise)";
1140static const char h_cfg_sio[] = "This should be enabled for certain memcards/gamepads";
1141static const char h_cfg_spuirq[] = "Compatibility tweak; should probably be left off";
1142static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix";
1143static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix";
b5e7e49a 1144static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1145 "Might be useful to overcome some dynarec bugs";
bd6267e6 1146
1147static menu_entry e_menu_adv_options[] =
1148{
fba06457 1149 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
bce6b056 1150 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
bd6267e6 1151 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1152 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1153 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1154 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1155 mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1156 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
b5e7e49a 1157 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
bd6267e6 1158 mee_end,
1159};
1160
1161static int menu_loop_adv_options(int id, int keys)
1162{
1163 static int sel = 0;
1164 me_loop(e_menu_adv_options, &sel, NULL);
1165 return 0;
1166}
1167
69af03a2 1168// ------------ options menu ------------
1169
69af03a2 1170static int mh_restore_defaults(int id, int keys)
1171{
3c70c47b 1172 menu_set_defconfig();
69af03a2 1173 me_update_msg("defaults restored");
1174 return 1;
1175}
1176
907b1e90 1177static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
bd6267e6 1178/*
69af03a2 1179static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1180static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1181 "loading state or both";
bd6267e6 1182*/
1183static const char h_restore_def[] = "Switches back to default / recommended\n"
1184 "configuration";
9f4a4237 1185static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
69af03a2 1186
1187static menu_entry e_menu_options[] =
1188{
bd6267e6 1189// mee_range ("Save slot", 0, state_slot, 0, 9),
1190// mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
9f4a4237 1191 mee_onoff_h ("Frameskip", 0, UseFrameSkip, 1, h_frameskip),
bd6267e6 1192 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
907b1e90 1193 mee_enum ("Region", 0, region, men_region),
3c70c47b 1194 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
69af03a2 1195 mee_handler ("[Display]", menu_loop_gfx_options),
bd6267e6 1196 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
69af03a2 1197 mee_handler ("[Advanced]", menu_loop_adv_options),
cd6e8d0f 1198 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1199 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
bd6267e6 1200 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
69af03a2 1201 mee_end,
1202};
1203
1204static int menu_loop_options(int id, int keys)
1205{
1206 static int sel = 0;
1207 int i;
1208
1209 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
3c70c47b 1210 e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
e16a7e51 1211 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
69af03a2 1212
1213 me_loop(e_menu_options, &sel, NULL);
1214
1215 return 0;
1216}
1217
1218// ------------ debug menu ------------
1219
72bb6fdd 1220static void draw_frame_debug(GPUFreeze_t *gpuf)
69af03a2 1221{
72bb6fdd 1222 int w = min(g_menuscreen_w, 1024);
1223 int h = min(g_menuscreen_h, 512);
1224 u16 *d = g_menuscreen_ptr;
1225 u16 *s = (u16 *)gpuf->psxVRam;
1226 char buff[64];
1227 int ty = 1;
1228
1229 gpuf->ulFreezeVersion = 1;
1230 if (GPU_freeze != NULL)
1231 GPU_freeze(1, gpuf);
1232
1233 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1234 bgr555_to_rgb565(d, s, w * 2);
1235
3c70c47b 1236 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
72bb6fdd 1237 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1238 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1239 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1240 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
69af03a2 1241}
1242
1243static void debug_menu_loop(void)
1244{
72bb6fdd 1245 GPUFreeze_t *gpuf;
69af03a2 1246 int inp;
1247
72bb6fdd 1248 gpuf = malloc(sizeof(*gpuf));
1249 if (gpuf == NULL)
1250 return;
1251
69af03a2 1252 while (1)
1253 {
72bb6fdd 1254 menu_draw_begin(0);
1255 draw_frame_debug(gpuf);
69af03a2 1256 menu_draw_end();
1257
1258 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1259 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1260 if (inp & PBTN_MBACK)
72bb6fdd 1261 break;
69af03a2 1262 }
72bb6fdd 1263
1264 free(gpuf);
69af03a2 1265}
1266
1267// ------------ main menu ------------
1268
4f5a1b2a 1269static void menu_bios_warn(void)
1270{
1271 int inp;
1272 static const char msg[] =
1273 "You don't seem to have copied any BIOS files to\n"
1274 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1275 "While many games work fine with fake (HLE) BIOS,\n"
1276 "others (like MGS and FF8) require BIOS to work.\n"
1277 "After copying the file, you'll also need to\n"
1278 "select it in the emu's options->[BIOS/Plugins]\n\n"
1279 "The file is usually named SCPH1001.BIN, but\n"
1280 "other not compressed files can be used too.\n\n"
1281 "Press (B) or (X) to continue";
1282
1283 while (1)
1284 {
4f5a1b2a 1285 draw_menu_message(msg, NULL);
4f5a1b2a 1286
1287 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1288 if (inp & (PBTN_MBACK|PBTN_MOK))
1289 return;
1290 }
1291}
1292
1293// ------------ main menu ------------
1294
bd6267e6 1295void OnFile_Exit();
1296
1bd9ee68 1297static void draw_frame_main(void)
1298{
1299 if (CdromId[0] != 0) {
1300 char buff[64];
c22b95ab 1301 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1302 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1303 Config.HLE ? "HLE" : "BIOS");
1bd9ee68 1304 smalltext_out16(4, 1, buff, 0x105f);
1305 }
1306}
1307
1308static void draw_frame_credits(void)
1309{
1310 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1311}
1312
4f5a1b2a 1313static const char credits_text[] =
1314 "PCSX-ReARMed\n\n"
1315 "(C) 1999-2003 PCSX Team\n"
1316 "(C) 2005-2009 PCSX-df Team\n"
1317 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1318 "GPU and SPU code by Pete Bernert\n"
1319 " and the P.E.Op.S. team\n"
1320 "ARM recompiler (C) 2009-2011 Ari64\n"
1321 "PCSX4ALL plugins by PCSX4ALL team\n"
1322 " Chui, Franxis, Unai\n\n"
1323 "integration, optimization and\n"
1324 " frontend (C) 2010-2011 notaz\n";
69af03a2 1325
e16a7e51 1326static int reset_game(void)
1327{
1328 // sanity check
1329 if (bios_sel == 0 && !Config.HLE)
1330 return -1;
1331
1332 ClosePlugins();
1333 OpenPlugins();
1334 SysReset();
1335 if (CheckCdrom() != -1) {
1336 LoadCdrom();
1337 }
1338 return 0;
1339}
1340
1341static int run_bios(void)
1342{
1343 if (bios_sel == 0)
1344 return -1;
1345
1346 ready_to_go = 0;
1347 pl_fbdev_buf = NULL;
1348
1349 ClosePlugins();
1350 set_cd_image(NULL);
1351 LoadPlugins();
c22b95ab 1352 pcnt_hook_plugins();
e16a7e51 1353 NetOpened = 0;
1354 if (OpenPlugins() == -1) {
1355 me_update_msg("failed to open plugins");
1356 return -1;
1357 }
1358 plugin_call_rearmed_cbs();
1359
1360 CdromId[0] = '\0';
1361 CdromLabel[0] = '\0';
1362
1363 SysReset();
1364
1365 ready_to_go = 1;
1366 return 0;
1367}
1368
bbd837c6 1369static int run_cd_image(const char *fname)
69af03a2 1370{
69af03a2 1371 ready_to_go = 0;
fba06457 1372 pl_fbdev_buf = NULL;
69af03a2 1373
fba06457 1374 ClosePlugins();
bbd837c6 1375 set_cd_image(fname);
69af03a2 1376 LoadPlugins();
c22b95ab 1377 pcnt_hook_plugins();
69af03a2 1378 NetOpened = 0;
1379 if (OpenPlugins() == -1) {
1380 me_update_msg("failed to open plugins");
bbd837c6 1381 return -1;
69af03a2 1382 }
201c21e2 1383 plugin_call_rearmed_cbs();
69af03a2 1384
69af03a2 1385 if (CheckCdrom() == -1) {
1386 // Only check the CD if we are starting the console with a CD
1387 ClosePlugins();
1388 me_update_msg("unsupported/invalid CD image");
bbd837c6 1389 return -1;
69af03a2 1390 }
1391
bbd837c6 1392 SysReset();
1393
69af03a2 1394 // Read main executable directly from CDRom and start it
1395 if (LoadCdrom() == -1) {
1396 ClosePlugins();
1397 me_update_msg("failed to load CD image");
bbd837c6 1398 return -1;
69af03a2 1399 }
1400
1401 ready_to_go = 1;
bbd837c6 1402 return 0;
69af03a2 1403}
1404
bbd837c6 1405static int romsel_run(void)
69af03a2 1406{
bbd837c6 1407 int prev_gpu, prev_spu;
1408 char *fname;
1409
1410 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1411 if (fname == NULL)
1412 return -1;
69af03a2 1413
bbd837c6 1414 printf("selected file: %s\n", fname);
1415
dc990066 1416 new_dynarec_clear_full();
1417
bbd837c6 1418 if (run_cd_image(fname) != 0)
1419 return -1;
1420
1421 prev_gpu = gpu_plugsel;
1422 prev_spu = spu_plugsel;
1423 if (menu_load_config(1) != 0)
1424 menu_load_config(0);
1425
1426 // check for plugin changes, have to repeat
1427 // loading if game config changed plugins to reload them
1428 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1429 printf("plugin change detected, reloading plugins..\n");
1430 if (run_cd_image(fname) != 0)
1431 return -1;
1432 }
1433
1434 strcpy(last_selected_fname, rom_fname_reload);
1435 return 0;
1436}
1437
1df403c5 1438static int swap_cd_image(void)
1439{
1440 char *fname;
1441
1442 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1443 if (fname == NULL)
1444 return -1;
1445
1446 printf("selected file: %s\n", fname);
1447
1448 CdromId[0] = '\0';
1449 CdromLabel[0] = '\0';
1450
1451 set_cd_image(fname);
1452 if (ReloadCdromPlugin() < 0) {
1453 me_update_msg("failed to load cdr plugin");
1454 return -1;
1455 }
1456 if (CDR_open() < 0) {
1457 me_update_msg("failed to open cdr plugin");
1458 return -1;
1459 }
1460
1461 SetCdOpenCaseTime(time(NULL) + 2);
1462 LidInterrupt();
1463
1464 strcpy(last_selected_fname, rom_fname_reload);
1465 return 0;
1466}
1467
bbd837c6 1468static int main_menu_handler(int id, int keys)
1469{
69af03a2 1470 switch (id)
1471 {
1472 case MA_MAIN_RESUME_GAME:
3c70c47b 1473 if (ready_to_go)
1474 return 1;
69af03a2 1475 break;
1476 case MA_MAIN_SAVE_STATE:
1477 if (ready_to_go)
1478 return menu_loop_savestate(0);
1479 break;
1480 case MA_MAIN_LOAD_STATE:
1481 if (ready_to_go)
1482 return menu_loop_savestate(1);
1483 break;
1484 case MA_MAIN_RESET_GAME:
e16a7e51 1485 if (ready_to_go && reset_game() == 0)
3c70c47b 1486 return 1;
69af03a2 1487 break;
1488 case MA_MAIN_LOAD_ROM:
bbd837c6 1489 if (romsel_run() == 0)
69af03a2 1490 return 1;
1491 break;
1df403c5 1492 case MA_MAIN_SWAP_CD:
1493 if (swap_cd_image() == 0)
1494 return 1;
1495 break;
e16a7e51 1496 case MA_MAIN_RUN_BIOS:
1497 if (run_bios() == 0)
1498 return 1;
1499 break;
69af03a2 1500 case MA_MAIN_CREDITS:
4f5a1b2a 1501 draw_menu_message(credits_text, draw_frame_credits);
69af03a2 1502 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1503 break;
1504 case MA_MAIN_EXIT:
bd6267e6 1505 OnFile_Exit();
1506 break;
69af03a2 1507 default:
1508 lprintf("%s: something unknown selected\n", __FUNCTION__);
1509 break;
1510 }
1511
1512 return 0;
1513}
1514
1515static menu_entry e_menu_main[] =
1516{
1517 mee_label (""),
1518 mee_label (""),
1519 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1520 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1521 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1522 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1523 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1df403c5 1524 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
e16a7e51 1525 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
69af03a2 1526 mee_handler ("Options", menu_loop_options),
1527 mee_handler ("Controls", menu_loop_keyconfig),
1528 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1529 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1530 mee_end,
1531};
1532
3c70c47b 1533// ----------------------------
1534
bd6267e6 1535static void menu_leave_emu(void);
1536
69af03a2 1537void menu_loop(void)
1538{
1539 static int sel = 0;
1540
bd6267e6 1541 menu_leave_emu();
69af03a2 1542
4f5a1b2a 1543 if (bioses[1] == NULL && !warned_about_bios) {
1544 menu_bios_warn();
1545 warned_about_bios = 1;
1546 }
1547
69af03a2 1548 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
e16a7e51 1549 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1550 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
69af03a2 1551 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1df403c5 1552 me_enable(e_menu_main, MA_MAIN_SWAP_CD, ready_to_go);
e16a7e51 1553 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
69af03a2 1554
69af03a2 1555 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1556
1557 do {
1bd9ee68 1558 me_loop(e_menu_main, &sel, draw_frame_main);
69af03a2 1559 } while (!ready_to_go);
1560
1561 /* wait until menu, ok, back is released */
1562 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1563 ;
1564
1565 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1566
3c70c47b 1567 menu_prepare_emu();
1568}
1569
e6eb2066 1570static void scan_bios_plugins(void)
bbd837c6 1571{
1572 char fname[MAXPATHLEN];
1573 struct dirent *ent;
e6eb2066 1574 int bios_i, gpu_i, spu_i;
bbd837c6 1575 char *p;
1576 DIR *dir;
1577
e6eb2066 1578 bioses[0] = "HLE";
bbd837c6 1579 gpu_plugins[0] = "builtin_gpu";
1580 spu_plugins[0] = "builtin_spu";
e6eb2066 1581 bios_i = gpu_i = spu_i = 1;
1582
1583 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1584 dir = opendir(fname);
1585 if (dir == NULL) {
1586 perror("scan_bios_plugins bios opendir");
1587 goto do_plugins;
1588 }
1589
1590 while (1) {
1591 struct stat st;
1592
1593 errno = 0;
1594 ent = readdir(dir);
1595 if (ent == NULL) {
1596 if (errno != 0)
1597 perror("readdir");
1598 break;
1599 }
1600
1601 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1602 continue;
1603
1604 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1605 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1606 printf("bad BIOS file: %s\n", ent->d_name);
1607 continue;
1608 }
1609
1610 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1611 bioses[bios_i++] = strdup(ent->d_name);
1612 continue;
1613 }
1614
1615 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1616 }
1617
1618 closedir(dir);
bbd837c6 1619
e6eb2066 1620do_plugins:
bbd837c6 1621 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1622 dir = opendir(fname);
1623 if (dir == NULL) {
e6eb2066 1624 perror("scan_bios_plugins opendir");
bbd837c6 1625 return;
1626 }
1627
1628 while (1) {
1629 void *h, *tmp;
1630
1631 errno = 0;
1632 ent = readdir(dir);
1633 if (ent == NULL) {
1634 if (errno != 0)
1635 perror("readdir");
1636 break;
1637 }
1638 p = strstr(ent->d_name, ".so");
1639 if (p == NULL)
1640 continue;
1641
1642 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1643 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1644 if (h == NULL) {
1645 fprintf(stderr, "%s\n", dlerror());
1646 continue;
1647 }
1648
1649 // now what do we have here?
1650 tmp = dlsym(h, "GPUinit");
1651 if (tmp) {
1652 dlclose(h);
1653 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1654 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1655 continue;
1656 }
1657
1658 tmp = dlsym(h, "SPUinit");
1659 if (tmp) {
1660 dlclose(h);
1661 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1662 spu_plugins[spu_i++] = strdup(ent->d_name);
1663 continue;
1664 }
1665
1666 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1667 dlclose(h);
1668 }
1669
1670 closedir(dir);
1671}
1672
3c70c47b 1673void menu_init(void)
1674{
4f3639fa 1675 char buff[MAXPATHLEN];
1676
1677 strcpy(last_selected_fname, "/media");
1678
e6eb2066 1679 scan_bios_plugins();
4f3639fa 1680 pnd_menu_init();
1681 menu_init_common();
1682
3c70c47b 1683 menu_set_defconfig();
1684 menu_load_config(0);
3c70c47b 1685 last_psx_w = 320;
1686 last_psx_h = 240;
bd6267e6 1687 last_psx_bpp = 16;
1688
4f3639fa 1689 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1690 if (g_menubg_src_ptr == NULL)
1691 exit(1);
1692 emu_make_path(buff, "skin/background.png", sizeof(buff));
1693 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
3c70c47b 1694}
1695
bd6267e6 1696void menu_notify_mode_change(int w, int h, int bpp)
3c70c47b 1697{
1698 last_psx_w = w;
1699 last_psx_h = h;
bd6267e6 1700 last_psx_bpp = bpp;
3c70c47b 1701
1702 if (scaling == SCALE_1_1) {
1703 g_layer_x = 800/2 - w/2; g_layer_y = 480/2 - h/2;
1704 g_layer_w = w; g_layer_h = h;
3c70c47b 1705 }
1706}
1707
bd6267e6 1708static void menu_leave_emu(void)
1709{
6d1a1ac2 1710 if (GPU_close != NULL) {
1711 int ret = GPU_close();
1712 if (ret)
1713 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1714 }
bd6267e6 1715
4f3639fa 1716 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
fba06457 1717 if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
bd6267e6 1718 int x = max(0, g_menuscreen_w - last_psx_w);
1719 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1720 int w = min(g_menuscreen_w, last_psx_w);
1721 int h = min(g_menuscreen_h, last_psx_h);
4f3639fa 1722 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
bd6267e6 1723 u16 *s = pl_fbdev_buf;
1724
1725 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
4f3639fa 1726 menu_darken_bg(d, s, w, 0);
bd6267e6 1727 }
fba06457 1728
1bd9ee68 1729 if (ready_to_go)
1730 cpu_clock = get_cpu_clock();
1731
fba06457 1732 plat_video_menu_enter(ready_to_go);
bd6267e6 1733}
1734
3c70c47b 1735void menu_prepare_emu(void)
1736{
b5e7e49a 1737 R3000Acpu *prev_cpu = psxCpu;
1738
fba06457 1739 plat_video_menu_leave();
1740
3c70c47b 1741 switch (scaling) {
1742 case SCALE_1_1:
bd6267e6 1743 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
3c70c47b 1744 break;
1745 case SCALE_4_3:
1746 g_layer_x = 80; g_layer_y = 0;
1747 g_layer_w = 640; g_layer_h = 480;
1748 break;
1749 case SCALE_FULLSCREEN:
1750 g_layer_x = 0; g_layer_y = 0;
1751 g_layer_w = 800; g_layer_h = 480;
1752 break;
1753 case SCALE_CUSTOM:
1754 break;
1755 }
bd6267e6 1756
b5e7e49a 1757 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1758 if (psxCpu != prev_cpu)
1759 // note that this does not really reset, just clears drc caches
1760 psxCpu->Reset();
1761
bd6267e6 1762 // core doesn't care about Config.Cdda changes,
1763 // so handle them manually here
1764 if (Config.Cdda)
1765 CDR_stop();
6d1a1ac2 1766
907b1e90 1767 menu_sync_config();
cefe86b7 1768 apply_lcdrate(Config.PsxType);
1769 apply_filter(filter);
1770 apply_cpu_clock();
fba06457 1771
6d1a1ac2 1772 if (GPU_open != NULL) {
6d1a1ac2 1773 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1774 if (ret)
1775 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1776 }
384f5f43 1777
1778 dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
69af03a2 1779}
1780
1781void me_update_msg(const char *msg)
1782{
1783 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1784 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1785
1786 menu_error_time = plat_get_ticks_ms();
1787 lprintf("msg: %s\n", menu_error_msg);
1788}
1789