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