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