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