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