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