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