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