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