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