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