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