gpu-gles: keep ClientState and texcache ptrs consistent
[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"
69af03a2 28#include "common/plat.h"
ef94866c 29#include "common/input.h"
30#include "linux/in_evdev.h"
69af03a2 31#include "../libpcsxcore/misc.h"
1df403c5 32#include "../libpcsxcore/cdrom.h"
0c2871a7 33#include "../libpcsxcore/cdriso.h"
799b0b87 34#include "../libpcsxcore/psemu_plugin_defs.h"
dc990066 35#include "../libpcsxcore/new_dynarec/new_dynarec.h"
4c08b9e7 36#include "../plugins/dfinput/main.h"
62d7fa95 37#include "../plugins/gpulib/cspace.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 },
a805c855 759#ifdef __ARM_ARCH_7A__ /* XXX */
760 { "Minimize ", 1 << SACTION_MINIMIZE },
761#endif
4c08b9e7 762 { "Gun Trigger ", 1 << SACTION_GUN_TRIGGER },
763 { "Gun A button ", 1 << SACTION_GUN_A },
764 { "Gun B button ", 1 << SACTION_GUN_B },
765 { "Gun Offscreen Trigger", 1 << SACTION_GUN_TRIGGER2 },
221be40d 766#ifndef __ARM_ARCH_7A__ /* XXX */
767 { "Volume Up ", 1 << SACTION_VOLUME_UP },
768 { "Volume Down ", 1 << SACTION_VOLUME_DOWN },
769#endif
69af03a2 770 { NULL, 0 }
771};
772
43bca6fb 773static char *mystrip(char *str)
774{
775 int i, len;
776
777 len = strlen(str);
778 for (i = 0; i < len; i++)
779 if (str[i] != ' ') break;
780 if (i > 0) memmove(str, str + i, len - i + 1);
781
782 len = strlen(str);
783 for (i = len - 1; i >= 0; i--)
784 if (str[i] != ' ') break;
785 str[i+1] = 0;
786
787 return str;
788}
789
790static void get_line(char *d, size_t size, const char *s)
791{
792 const char *pe;
793 size_t len;
794
795 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
796 ;
797 len = pe - s;
798 if (len > size - 1)
799 len = size - 1;
800 strncpy(d, s, len);
801 d[len] = 0;
802
803 mystrip(d);
804}
805
806static void keys_write_all(FILE *f)
807{
808 int d;
809
810 for (d = 0; d < IN_MAX_DEVS; d++)
811 {
812 const int *binds = in_get_dev_binds(d);
813 const char *name = in_get_dev_name(d, 0, 0);
814 int k, count = 0;
815
816 if (binds == NULL || name == NULL)
817 continue;
818
819 fprintf(f, "binddev = %s\n", name);
820 in_get_config(d, IN_CFG_BIND_COUNT, &count);
821
822 for (k = 0; k < count; k++)
823 {
824 int i, kbinds, mask;
825 char act[32];
826
827 act[0] = act[31] = 0;
828 name = in_get_key_name(d, k);
829
830 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
831 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
832 mask = me_ctrl_actions[i].mask;
833 if (mask & kbinds) {
834 strncpy(act, me_ctrl_actions[i].name, 31);
835 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
836 kbinds &= ~mask;
837 }
838 mask = me_ctrl_actions[i].mask << 16;
839 if (mask & kbinds) {
840 strncpy(act, me_ctrl_actions[i].name, 31);
841 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
842 kbinds &= ~mask;
843 }
844 }
845
846 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
221be40d 847 for (i = 0; kbinds && emuctrl_actions[i].name != NULL; i++) {
43bca6fb 848 mask = emuctrl_actions[i].mask;
849 if (mask & kbinds) {
850 strncpy(act, emuctrl_actions[i].name, 31);
851 fprintf(f, "bind %s = %s\n", name, mystrip(act));
852 kbinds &= ~mask;
853 }
854 }
855 }
1b0c5139 856
857 for (k = 0; k < array_size(in_adev); k++)
858 {
859 if (in_adev[k] == d)
860 fprintf(f, "bind_analog = %d\n", k);
861 }
43bca6fb 862 }
863}
864
865static int parse_bind_val(const char *val, int *type)
866{
867 int i;
868
869 *type = IN_BINDTYPE_NONE;
870 if (val[0] == 0)
871 return 0;
872
873 if (strncasecmp(val, "player", 6) == 0)
874 {
875 int player, shift = 0;
876 player = atoi(val + 6) - 1;
877
878 if ((unsigned int)player > 1)
879 return -1;
880 if (player == 1)
881 shift = 16;
882
883 *type = IN_BINDTYPE_PLAYER12;
884 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
885 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
886 return me_ctrl_actions[i].mask << shift;
887 }
888 }
889 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
890 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
891 *type = IN_BINDTYPE_EMU;
892 return emuctrl_actions[i].mask;
893 }
894 }
895
896 return -1;
897}
898
899static void keys_load_all(const char *cfg)
900{
901 char dev[256], key[128], *act;
902 const char *p;
903 int bind, bindtype;
1b0c5139 904 int ret, dev_id;
43bca6fb 905
906 p = cfg;
907 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
908 p += 10;
909
910 get_line(dev, sizeof(dev), p);
911 dev_id = in_config_parse_dev(dev);
912 if (dev_id < 0) {
913 printf("input: can't handle dev: %s\n", dev);
914 continue;
915 }
916
917 in_unbind_all(dev_id, -1, -1);
918 while ((p = strstr(p, "bind"))) {
919 if (strncmp(p, "binddev = ", 10) == 0)
920 break;
921
1b0c5139 922 if (strncmp(p, "bind_analog", 11) == 0) {
923 ret = sscanf(p, "bind_analog = %d", &bind);
924 p += 11;
925 if (ret != 1) {
926 printf("input: parse error: %16s..\n", p);
927 continue;
928 }
929 if ((unsigned int)bind >= array_size(in_adev)) {
930 printf("input: analog id %d out of range\n", bind);
931 continue;
932 }
933 in_adev[bind] = dev_id;
934 continue;
935 }
936
43bca6fb 937 p += 4;
938 if (*p != ' ') {
939 printf("input: parse error: %16s..\n", p);
940 continue;
941 }
942
943 get_line(key, sizeof(key), p);
944 act = strchr(key, '=');
945 if (act == NULL) {
946 printf("parse failed: %16s..\n", p);
947 continue;
948 }
949 *act = 0;
950 act++;
951 mystrip(key);
952 mystrip(act);
953
954 bind = parse_bind_val(act, &bindtype);
955 if (bind != -1 && bind != 0) {
8f892648 956 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
43bca6fb 957 in_config_bind_key(dev_id, key, bind, bindtype);
958 }
959 else
960 lprintf("config: unhandled action \"%s\"\n", act);
961 }
962 }
ef94866c 963 in_clean_binds();
43bca6fb 964}
965
69af03a2 966static int key_config_loop_wrap(int id, int keys)
967{
968 switch (id) {
969 case MA_CTRL_PLAYER1:
970 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
971 break;
972 case MA_CTRL_PLAYER2:
973 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
974 break;
975 case MA_CTRL_EMU:
976 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
977 break;
978 default:
979 break;
980 }
981 return 0;
982}
983
1b0c5139 984static const char *adevnames[IN_MAX_DEVS + 2];
985static int stick_sel[2];
986
987static menu_entry e_menu_keyconfig_analog[] =
988{
989 mee_enum ("Left stick (L3)", 0, stick_sel[0], adevnames),
990 mee_range(" X axis", 0, in_adev_axis[0][0], 0, 7),
991 mee_range(" Y axis", 0, in_adev_axis[0][1], 0, 7),
992 mee_enum ("Right stick (R3)", 0, stick_sel[1], adevnames),
993 mee_range(" X axis", 0, in_adev_axis[1][0], 0, 7),
994 mee_range(" Y axis", 0, in_adev_axis[1][1], 0, 7),
995 mee_end,
996};
997
998static int key_config_analog(int id, int keys)
999{
1000 int i, d, count, sel = 0;
1001 int sel2dev_map[IN_MAX_DEVS];
1002
1003 memset(adevnames, 0, sizeof(adevnames));
1004 memset(sel2dev_map, 0xff, sizeof(sel2dev_map));
1005 memset(stick_sel, 0, sizeof(stick_sel));
1006
1007 adevnames[0] = "None";
1008 i = 1;
1009 for (d = 0; d < IN_MAX_DEVS; d++)
1010 {
1011 const char *name = in_get_dev_name(d, 0, 1);
1012 if (name == NULL)
1013 continue;
1014
1015 count = 0;
1016 in_get_config(d, IN_CFG_ABS_AXIS_COUNT, &count);
1017 if (count == 0)
1018 continue;
1019
1020 if (in_adev[0] == d) stick_sel[0] = i;
1021 if (in_adev[1] == d) stick_sel[1] = i;
1022 sel2dev_map[i] = d;
1023 adevnames[i++] = name;
1024 }
1025 adevnames[i] = NULL;
1026
1027 me_loop(e_menu_keyconfig_analog, &sel);
1028
1029 in_adev[0] = sel2dev_map[stick_sel[0]];
1030 in_adev[1] = sel2dev_map[stick_sel[1]];
1031
1032 return 0;
1033}
1034
69af03a2 1035static const char *mgn_dev_name(int id, int *offs)
1036{
1037 const char *name = NULL;
1038 static int it = 0;
1039
1040 if (id == MA_CTRL_DEV_FIRST)
1041 it = 0;
1042
1043 for (; it < IN_MAX_DEVS; it++) {
1044 name = in_get_dev_name(it, 1, 1);
1045 if (name != NULL)
1046 break;
1047 }
1048
1049 it++;
1050 return name;
1051}
1052
1053static const char *mgn_saveloadcfg(int id, int *offs)
1054{
1055 return "";
1056}
1057
cd6e8d0f 1058static int mh_savecfg(int id, int keys)
69af03a2 1059{
cd6e8d0f 1060 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
1061 me_update_msg("config saved");
1062 else
1063 me_update_msg("failed to write config");
69af03a2 1064
1065 return 1;
1066}
1067
ef94866c 1068static int mh_input_rescan(int id, int keys)
1069{
1070 //menu_sync_config();
9b4bd105 1071 in_probe();
ef94866c 1072 me_update_msg("rescan complete.");
1073
1074 return 0;
1075}
1076
4c08b9e7 1077static const char *men_in_type_sel[] = {
1078 "Standard (SCPH-1080)",
1079 "Analog (SCPH-1150)",
1080 "GunCon",
1081 NULL
1082};
ef94866c 1083static const char h_nub_btns[] = "Experimental, keep this OFF if unsure. Select rescan after change.";
b944a30e 1084static const char h_notsgun[] = "Don't trigger (shoot) when touching screen in gun games.";
1085static const char h_vibration[]= "Must select analog above and enable this ingame too.";
799b0b87 1086
69af03a2 1087static menu_entry e_menu_keyconfig[] =
1088{
4c08b9e7 1089 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
1090 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
1b0c5139 1091 mee_handler_id("Analog controls", MA_CTRL_ANALOG, key_config_analog),
4c08b9e7 1092 mee_handler_id("Emulator/Gun controls", MA_CTRL_EMU, key_config_loop_wrap),
799b0b87 1093 mee_label (""),
4c08b9e7 1094 mee_enum ("Port 1 device", 0, in_type_sel1, men_in_type_sel),
1095 mee_enum ("Port 2 device", 0, in_type_sel2, men_in_type_sel),
55b0eeea 1096 mee_onoff_h ("Nubs as buttons", MA_CTRL_NUBS_BTNS, in_evdev_allow_abs_only, 1, h_nub_btns),
b944a30e 1097 mee_onoff_h ("Vibration", MA_CTRL_VIBRATION, in_enable_vibration, 1, h_vibration),
55b0eeea 1098 mee_range ("Analog deadzone", MA_CTRL_DEADZONE, analog_deadzone, 1, 99),
4c08b9e7 1099 mee_onoff_h ("No TS Gun trigger", 0, g_opts, OPT_TSGUN_NOTRIGGER, h_notsgun),
799b0b87 1100 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1101 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1b0c5139 1102 mee_handler ("Rescan devices:", mh_input_rescan),
69af03a2 1103 mee_label (""),
69af03a2 1104 mee_label_mk (MA_CTRL_DEV_FIRST, 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_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1109 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1110 mee_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
1111 mee_end,
1112};
1113
1114static int menu_loop_keyconfig(int id, int keys)
1115{
1116 static int sel = 0;
1117
e16a7e51 1118// me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
51f77282 1119 me_loop(e_menu_keyconfig, &sel);
69af03a2 1120 return 0;
1121}
1122
69af03a2 1123// ------------ gfx options menu ------------
1124
a185be70 1125static const char *men_scaler[] = { "1x1", "scaled 4:3", "integer scaled 4:3", "fullscreen", "custom", NULL };
3c70c47b 1126static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
1127 "using d-pad or move it using R+d-pad";
1128static const char *men_dummy[] = { NULL };
1129
1130static int menu_loop_cscaler(int id, int keys)
1131{
1132 unsigned int inp;
1133
1134 scaling = SCALE_CUSTOM;
1135
1136 omap_enable_layer(1);
3c70c47b 1137
1138 for (;;)
1139 {
1140 menu_draw_begin(0);
201c21e2 1141 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
1142 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
1143 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
3c70c47b 1144 menu_draw_end();
1145
1146 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
1147 if (inp & PBTN_UP) g_layer_y--;
1148 if (inp & PBTN_DOWN) g_layer_y++;
1149 if (inp & PBTN_LEFT) g_layer_x--;
1150 if (inp & PBTN_RIGHT) g_layer_x++;
1151 if (!(inp & PBTN_R)) {
1152 if (inp & PBTN_UP) g_layer_h += 2;
1153 if (inp & PBTN_DOWN) g_layer_h -= 2;
1154 if (inp & PBTN_LEFT) g_layer_w += 2;
1155 if (inp & PBTN_RIGHT) g_layer_w -= 2;
1156 }
1157 if (inp & (PBTN_MOK|PBTN_MBACK))
1158 break;
1159
1160 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
1161 if (g_layer_x < 0) g_layer_x = 0;
1162 if (g_layer_x > 640) g_layer_x = 640;
1163 if (g_layer_y < 0) g_layer_y = 0;
1164 if (g_layer_y > 420) g_layer_y = 420;
1165 if (g_layer_w < 160) g_layer_w = 160;
1166 if (g_layer_h < 60) g_layer_h = 60;
1167 if (g_layer_x + g_layer_w > 800)
1168 g_layer_w = 800 - g_layer_x;
1169 if (g_layer_y + g_layer_h > 480)
1170 g_layer_h = 480 - g_layer_y;
1171 omap_enable_layer(1);
1172 }
1173 }
1174
1175 omap_enable_layer(0);
1176
1177 return 0;
1178}
1179
69af03a2 1180static menu_entry e_menu_gfx_options[] =
1181{
3c70c47b 1182 mee_enum ("Scaler", 0, scaling, men_scaler),
1183 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
1184// mee_onoff ("Vsync", 0, vsync, 1),
1185 mee_cust_h ("Setup custom scaler", 0, menu_loop_cscaler, NULL, h_cscaler),
69af03a2 1186 mee_end,
1187};
1188
1189static int menu_loop_gfx_options(int id, int keys)
1190{
1191 static int sel = 0;
1192
51f77282 1193 me_loop(e_menu_gfx_options, &sel);
69af03a2 1194
1195 return 0;
1196}
1197
bd6267e6 1198// ------------ bios/plugins ------------
1199
5440b88e 1200#ifdef __ARM_NEON__
1201
1202static const char h_gpu_neon[] = "Configure built-in NEON GPU plugin";
1203static const char *men_gpu_interlace[] = { "Off", "On", "Auto", NULL };
1204
1205static menu_entry e_menu_plugin_gpu_neon[] =
1206{
1207 mee_enum ("Enable interlace mode", 0, pl_rearmed_cbs.gpu_neon.allow_interlace, men_gpu_interlace),
1208 mee_end,
1209};
1210
1211static int menu_loop_plugin_gpu_neon(int id, int keys)
1212{
1213 int sel = 0;
1214 me_loop(e_menu_plugin_gpu_neon, &sel);
1215 return 0;
1216}
1217
1218#endif
1219
17a54a4a 1220static menu_entry e_menu_plugin_gpu_unai[] =
1221{
89c0de42 1222 mee_onoff ("Skip every 2nd line", 0, pl_rearmed_cbs.gpu_unai.lineskip, 1),
17a54a4a 1223 mee_onoff ("Abe's Odyssey hack", 0, pl_rearmed_cbs.gpu_unai.abe_hack, 1),
1224 mee_onoff ("Disable lighting", 0, pl_rearmed_cbs.gpu_unai.no_light, 1),
1225 mee_onoff ("Disable blending", 0, pl_rearmed_cbs.gpu_unai.no_blend, 1),
1226 mee_end,
1227};
1228
1229static int menu_loop_plugin_gpu_unai(int id, int keys)
1230{
1231 int sel = 0;
1232 me_loop(e_menu_plugin_gpu_unai, &sel);
1233 return 0;
1234}
1235
bd6267e6 1236static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
5440b88e 1237//static const char h_gpu_0[] = "Needed for Chrono Cross";
bd6267e6 1238static const char h_gpu_1[] = "Capcom fighting games";
1239static const char h_gpu_2[] = "Black screens in Lunar";
1240static const char h_gpu_3[] = "Compatibility mode";
1241static const char h_gpu_6[] = "Pandemonium 2";
5440b88e 1242//static const char h_gpu_7[] = "Skip every second frame";
bd6267e6 1243static const char h_gpu_8[] = "Needed by Dark Forces";
1244static const char h_gpu_9[] = "better g-colors, worse textures";
1245static const char h_gpu_10[] = "Toggle busy flags after drawing";
1246
17a54a4a 1247static menu_entry e_menu_plugin_gpu_peops[] =
bd6267e6 1248{
e64dc4c5 1249 mee_enum ("Dithering", 0, pl_rearmed_cbs.gpu_peops.iUseDither, men_gpu_dithering),
5440b88e 1250// mee_onoff_h ("Odd/even bit hack", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<0, h_gpu_0),
e64dc4c5 1251 mee_onoff_h ("Expand screen width", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<1, h_gpu_1),
1252 mee_onoff_h ("Ignore brightness color", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<2, h_gpu_2),
1253 mee_onoff_h ("Disable coordinate check", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<3, h_gpu_3),
1254 mee_onoff_h ("Lazy screen update", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<6, h_gpu_6),
5440b88e 1255// mee_onoff_h ("Old frame skipping", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<7, h_gpu_7),
e64dc4c5 1256 mee_onoff_h ("Repeated flat tex triangles ",0,pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<8, h_gpu_8),
1257 mee_onoff_h ("Draw quads with triangles", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<9, h_gpu_9),
1258 mee_onoff_h ("Fake 'gpu busy' states", 0, pl_rearmed_cbs.gpu_peops.dwActFixes, 1<<10, h_gpu_10),
bd6267e6 1259 mee_end,
1260};
1261
17a54a4a 1262static int menu_loop_plugin_gpu_peops(int id, int keys)
bd6267e6 1263{
1264 static int sel = 0;
17a54a4a 1265 me_loop(e_menu_plugin_gpu_peops, &sel);
bd6267e6 1266 return 0;
1267}
1268
bd6267e6 1269static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
9e7a7352 1270static const char h_spu_volboost[] = "Large values cause distortion";
cdb31c95 1271static const char h_spu_irq_wait[] = "Wait for CPU (recommended set to ON)";
1bd9ee68 1272static const char h_spu_thread[] = "Run sound emulation in main thread (recommended)";
bd6267e6 1273
1274static menu_entry e_menu_plugin_spu[] =
1275{
9e7a7352 1276 mee_range_h ("Volume boost", 0, volume_boost, -5, 30, h_spu_volboost),
381ea103 1277 mee_onoff ("Reverb", 0, iUseReverb, 2),
bd6267e6 1278 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
1279 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
1280 mee_onoff_h ("SPU IRQ Wait", 0, iSPUIRQWait, 1, h_spu_irq_wait),
1bd9ee68 1281 mee_onoff_h ("Sound in main thread", 0, iUseTimer, 2, h_spu_thread),
bd6267e6 1282 mee_end,
1283};
1284
1285static int menu_loop_plugin_spu(int id, int keys)
1286{
1287 static int sel = 0;
51f77282 1288 me_loop(e_menu_plugin_spu, &sel);
bd6267e6 1289 return 0;
1290}
1291
55b0eeea 1292static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in\n"
1293 "savestates and can't be changed there. Must save\n"
1294 "config and reload the game for change to take effect";
bbd837c6 1295static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1296 "for plugin change to take effect";
17a54a4a 1297static const char h_gpu_peops[] = "Configure P.E.Op.S. SoftGL Driver V1.17";
1298static const char h_gpu_unai[] = "Configure Unai/PCSX4ALL Team GPU plugin";
e6eb2066 1299static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
bbd837c6 1300
bd6267e6 1301static menu_entry e_menu_plugin_options[] =
1302{
e6eb2066 1303 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
bbd837c6 1304 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1305 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_xpu),
5440b88e 1306#ifdef __ARM_NEON__
1307 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu_neon, h_gpu_neon),
1308#endif
17a54a4a 1309 mee_handler_h ("Configure gpu_peops plugin", menu_loop_plugin_gpu_peops, h_gpu_peops),
1310 mee_handler_h ("Configure PCSX4ALL GPU plugin", menu_loop_plugin_gpu_unai, h_gpu_unai),
bd6267e6 1311 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1312 mee_end,
1313};
1314
51f77282 1315static menu_entry e_menu_main2[];
e16a7e51 1316
bd6267e6 1317static int menu_loop_plugin_options(int id, int keys)
1318{
1319 static int sel = 0;
51f77282 1320 me_loop(e_menu_plugin_options, &sel);
bbd837c6 1321
e6eb2066 1322 // sync BIOS/plugins
1323 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
bbd837c6 1324 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1325 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
51f77282 1326 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
bbd837c6 1327
bd6267e6 1328 return 0;
1329}
1330
1331// ------------ adv options menu ------------
1332
0ff8c62c 1333static const char h_cfg_psxclk[] = "Over/under-clock the PSX, default is " DEFAULT_PSX_CLOCK_S "\n";
bab59f00 1334static const char h_cfg_nosmc[] = "Will cause crashes when loading, break memcards";
0ff8c62c 1335static const char h_cfg_gteunn[] = "May cause graphical glitches";
1336static const char h_cfg_gteflgs[] = "Will cause graphical glitches";
1337
1338static menu_entry e_menu_speed_hacks[] =
1339{
1340 mee_range_h ("PSX CPU clock, %%", 0, psx_clock, 1, 500, h_cfg_psxclk),
1341 mee_onoff_h ("Disable SMC checks", 0, new_dynarec_hacks, NDHACK_NO_SMC_CHECK, h_cfg_nosmc),
1342 mee_onoff_h ("Assume GTE regs unneeded", 0, new_dynarec_hacks, NDHACK_GTE_UNNEEDED, h_cfg_gteunn),
1343 mee_onoff_h ("Disable GTE flags", 0, new_dynarec_hacks, NDHACK_GTE_NO_FLAGS, h_cfg_gteflgs),
1344 mee_end,
1345};
1346
1347static int menu_loop_speed_hacks(int id, int keys)
1348{
1349 static int sel = 0;
1350 me_loop(e_menu_speed_hacks, &sel);
1351 return 0;
1352}
1353
4feed8d3 1354static const char *men_cfg_cdrr[] = { "Auto", "ON", "OFF", NULL };
8f892648 1355static const char h_cfg_cpul[] = "Shows CPU usage in %";
90f1d26c 1356static const char h_cfg_spu[] = "Shows active SPU channels\n"
1357 "(green: normal, red: fmod, blue: noise)";
1bd9ee68 1358static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
bd6267e6 1359static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1360static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1361 "(proper .cue/.bin dump is needed otherwise)";
4feed8d3 1362static const char h_cfg_sio[] = "You should not need this, breaks games";
1363static const char h_cfg_spuirq[] = "Compatibility tweak; should be left off";
1364static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix\n"
1365 "(timing hack, breaks other games)";
1366static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix\n"
1367 "(timing hack, breaks other games)";
0ff8c62c 1368static const char h_cfg_cdrr[] = "Compatibility tweak (CD timing hack, breaks FMVs)";
b5e7e49a 1369static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1370 "Might be useful to overcome some dynarec bugs";
0ff8c62c 1371static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
1372 "must reload game for any change to take effect";
bd6267e6 1373
1374static menu_entry e_menu_adv_options[] =
1375{
fba06457 1376 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
90f1d26c 1377 mee_onoff_h ("Show SPU channels", 0, g_opts, OPT_SHOWSPU, h_cfg_spu),
bce6b056 1378 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
bd6267e6 1379 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1380 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1381 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1382 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
b1be1eee 1383 //mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
bd6267e6 1384 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
4feed8d3 1385 mee_enum_h ("CD read reschedule hack",0, Config.CdrReschedule, men_cfg_cdrr, h_cfg_cdrr),
b5e7e49a 1386 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
0ff8c62c 1387 mee_handler_h ("[Speed hacks]", menu_loop_speed_hacks, h_cfg_shacks),
bd6267e6 1388 mee_end,
1389};
1390
1391static int menu_loop_adv_options(int id, int keys)
1392{
1393 static int sel = 0;
51f77282 1394 me_loop(e_menu_adv_options, &sel);
bd6267e6 1395 return 0;
1396}
1397
69af03a2 1398// ------------ options menu ------------
1399
69af03a2 1400static int mh_restore_defaults(int id, int keys)
1401{
3c70c47b 1402 menu_set_defconfig();
69af03a2 1403 me_update_msg("defaults restored");
1404 return 1;
1405}
1406
907b1e90 1407static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
9fe27e25 1408static const char *men_frameskip[] = { "Auto", "Off", "1", "2", "3", NULL };
bd6267e6 1409/*
69af03a2 1410static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1411static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1412 "loading state or both";
bd6267e6 1413*/
1414static const char h_restore_def[] = "Switches back to default / recommended\n"
1415 "configuration";
9f4a4237 1416static const char h_frameskip[] = "Warning: frameskip sometimes causes glitches\n";
69af03a2 1417
1418static menu_entry e_menu_options[] =
1419{
bd6267e6 1420// mee_range ("Save slot", 0, state_slot, 0, 9),
1421// mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
ea4a16e7 1422 mee_enum_h ("Frameskip", 0, frameskip, men_frameskip, h_frameskip),
bd6267e6 1423 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
907b1e90 1424 mee_enum ("Region", 0, region, men_region),
3c70c47b 1425 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
55b0eeea 1426 mee_handler_id("[Display]", MA_OPT_DISP_OPTS, menu_loop_gfx_options),
bd6267e6 1427 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
69af03a2 1428 mee_handler ("[Advanced]", menu_loop_adv_options),
cd6e8d0f 1429 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1430 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
bd6267e6 1431 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
69af03a2 1432 mee_end,
1433};
1434
1435static int menu_loop_options(int id, int keys)
1436{
1437 static int sel = 0;
1438 int i;
1439
1440 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
3c70c47b 1441 e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
e16a7e51 1442 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
69af03a2 1443
51f77282 1444 me_loop(e_menu_options, &sel);
69af03a2 1445
1446 return 0;
1447}
1448
1449// ------------ debug menu ------------
1450
72bb6fdd 1451static void draw_frame_debug(GPUFreeze_t *gpuf)
69af03a2 1452{
72bb6fdd 1453 int w = min(g_menuscreen_w, 1024);
1454 int h = min(g_menuscreen_h, 512);
1455 u16 *d = g_menuscreen_ptr;
1456 u16 *s = (u16 *)gpuf->psxVRam;
1457 char buff[64];
1458 int ty = 1;
1459
1460 gpuf->ulFreezeVersion = 1;
1461 if (GPU_freeze != NULL)
1462 GPU_freeze(1, gpuf);
1463
1464 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1465 bgr555_to_rgb565(d, s, w * 2);
1466
3c70c47b 1467 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
72bb6fdd 1468 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1469 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1470 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1471 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
69af03a2 1472}
1473
1474static void debug_menu_loop(void)
1475{
72bb6fdd 1476 GPUFreeze_t *gpuf;
69af03a2 1477 int inp;
1478
72bb6fdd 1479 gpuf = malloc(sizeof(*gpuf));
1480 if (gpuf == NULL)
1481 return;
1482
69af03a2 1483 while (1)
1484 {
72bb6fdd 1485 menu_draw_begin(0);
1486 draw_frame_debug(gpuf);
69af03a2 1487 menu_draw_end();
1488
1489 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1490 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1491 if (inp & PBTN_MBACK)
72bb6fdd 1492 break;
69af03a2 1493 }
72bb6fdd 1494
1495 free(gpuf);
69af03a2 1496}
1497
51f77282 1498// --------- memcard manager ---------
1499
1500static void draw_mc_icon(int dx, int dy, const u16 *s)
1501{
1502 u16 *d;
1503 int x, y, l, p;
1504
1505 d = (u16 *)g_menuscreen_ptr + g_menuscreen_w * dy + dx;
1506
1507 for (y = 0; y < 16; y++, s += 16) {
1508 for (l = 0; l < 2; l++, d += g_menuscreen_w) {
1509 for (x = 0; x < 16; x++) {
1510 p = s[x];
1511 d[x*2] = d[x*2 + 1] = ((p & 0x7c00) >> 10)
1512 | ((p & 0x03e0) << 1) | ((p & 0x1f) << 11);
1513 }
1514 }
1515 }
1516}
1517
1518static void draw_mc_bg(void)
1519{
1520 McdBlock *blocks1, *blocks2;
1521 int maxicons = 15;
1522 int i, y, row2;
1523
1524 blocks1 = malloc(15 * sizeof(blocks1[0]));
1525 blocks2 = malloc(15 * sizeof(blocks1[0]));
1526 if (blocks1 == NULL || blocks2 == NULL)
1527 goto out;
1528
1529 for (i = 0; i < 15; i++) {
1530 GetMcdBlockInfo(1, i + 1, &blocks1[i]);
1531 GetMcdBlockInfo(2, i + 1, &blocks2[i]);
1532 }
1533
1534 menu_draw_begin(1);
1535
1536 memcpy(g_menuscreen_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1537
1538 y = g_menuscreen_h / 2 - 15 * 32 / 2;
1539 if (y < 0) {
1540 // doesn't fit..
1541 y = 0;
1542 maxicons = g_menuscreen_h / 32;
1543 }
1544
1545 row2 = g_menuscreen_w / 2;
1546 for (i = 0; i < maxicons; i++) {
1547 draw_mc_icon(8, y + i * 32, (u16 *)blocks1[i].Icon);
1548 smalltext_out16(10+32, y + i * 32 + 8, blocks1[i].sTitle, 0xf71e);
1549
1550 draw_mc_icon(row2 + 8, y + i * 32, (u16 *)blocks2[i].Icon);
1551 smalltext_out16(row2 + 10+32, y + i * 32 + 8, blocks2[i].sTitle, 0xf71e);
1552 }
1553
1554 menu_darken_bg(g_menubg_ptr, g_menuscreen_ptr, g_menuscreen_w * g_menuscreen_h, 0);
1555
1556 menu_draw_end();
1557out:
1558 free(blocks1);
1559 free(blocks2);
1560}
1561
1562static void handle_memcard_sel(void)
1563{
1564 Config.Mcd1[0] = 0;
1565 if (memcard1_sel != 0)
1566 snprintf(Config.Mcd1, sizeof(Config.Mcd1), ".%s%s", MEMCARD_DIR, memcards[memcard1_sel]);
1567 Config.Mcd2[0] = 0;
1568 if (memcard2_sel != 0)
1569 snprintf(Config.Mcd2, sizeof(Config.Mcd2), ".%s%s", MEMCARD_DIR, memcards[memcard2_sel]);
1570 LoadMcds(Config.Mcd1, Config.Mcd2);
1571 draw_mc_bg();
1572}
1573
1574static menu_entry e_memcard_options[] =
1575{
1576 mee_enum("Memory card 1", 0, memcard1_sel, memcards),
1577 mee_enum("Memory card 2", 0, memcard2_sel, memcards),
1578 mee_end,
1579};
1580
1581static int menu_loop_memcards(int id, int keys)
1582{
1583 static int sel = 0;
1584 char *p;
1585 int i;
1586
1587 memcard1_sel = memcard2_sel = 0;
1588 p = strrchr(Config.Mcd1, '/');
1589 if (p != NULL)
1590 for (i = 0; memcards[i] != NULL; i++)
1591 if (strcmp(p + 1, memcards[i]) == 0)
1592 { memcard1_sel = i; break; }
1593 p = strrchr(Config.Mcd2, '/');
1594 if (p != NULL)
1595 for (i = 0; memcards[i] != NULL; i++)
1596 if (strcmp(p + 1, memcards[i]) == 0)
1597 { memcard2_sel = i; break; }
1598
1599 me_loop_d(e_memcard_options, &sel, handle_memcard_sel, NULL);
1600
1601 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1602
1603 return 0;
1604}
1605
1606// --------- main menu help ----------
69af03a2 1607
4f5a1b2a 1608static void menu_bios_warn(void)
1609{
1610 int inp;
1611 static const char msg[] =
cbd45cda 1612 "You don't seem to have copied any BIOS\n"
1613 "files to\n"
1614#ifdef __ARM_ARCH_7A__ // XXX
4f5a1b2a 1615 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
cbd45cda 1616#else
1617 "pcsx_rearmed/bios/\n\n"
1618#endif
1619 "While many games work fine with fake\n"
1620 "(HLE) BIOS, others (like MGS and FF8)\n"
1621 "require BIOS to work.\n"
1622 "After copying the file, you'll also need\n"
1623 "to select it in the emu's menu:\n"
1624 "options->[BIOS/Plugins]\n\n"
1625 "The file is usually named SCPH1001.BIN,\n"
1626 "but other not compressed files can be\n"
1627 "used too.\n\n"
4f5a1b2a 1628 "Press (B) or (X) to continue";
1629
1630 while (1)
1631 {
4f5a1b2a 1632 draw_menu_message(msg, NULL);
4f5a1b2a 1633
1634 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1635 if (inp & (PBTN_MBACK|PBTN_MOK))
1636 return;
1637 }
1638}
1639
1640// ------------ main menu ------------
1641
bd6267e6 1642void OnFile_Exit();
1643
1bd9ee68 1644static void draw_frame_main(void)
1645{
65092fd8 1646 struct tm *tmp;
1647 time_t ltime;
1648 char ltime_s[16];
1649 char buff[64];
1650
1bd9ee68 1651 if (CdromId[0] != 0) {
c22b95ab 1652 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1653 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1654 Config.HLE ? "HLE" : "BIOS");
1bd9ee68 1655 smalltext_out16(4, 1, buff, 0x105f);
1656 }
65092fd8 1657
1658 if (ready_to_go) {
1659 ltime = time(NULL);
1660 tmp = localtime(&ltime);
1661 strftime(ltime_s, sizeof(ltime_s), "%H:%M", tmp);
55b0eeea 1662 snprintf(buff, sizeof(buff), "%s %3d%%", ltime_s, plat_get_bat_capacity());
65092fd8 1663 smalltext_out16(4, 1 + me_sfont_h, buff, 0x105f);
1664 }
1bd9ee68 1665}
1666
1667static void draw_frame_credits(void)
1668{
3ce7adeb 1669 smalltext_out16(4, 1, "build: " __DATE__ " " __TIME__ " " REV, 0xe7fc);
1bd9ee68 1670}
1671
4f5a1b2a 1672static const char credits_text[] =
1673 "PCSX-ReARMed\n\n"
1674 "(C) 1999-2003 PCSX Team\n"
1675 "(C) 2005-2009 PCSX-df Team\n"
1676 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
4f5a1b2a 1677 "ARM recompiler (C) 2009-2011 Ari64\n"
c069dc1b 1678#ifdef __ARM_NEON__
3ce7adeb 1679 "ARM NEON GPU (c) 2011-2012 Exophase\n"
c069dc1b 1680#endif
1681 "PEOpS GPU and SPU by Pete Bernert\n"
1682 " and the P.E.Op.S. team\n"
1683 "PCSX4ALL plugin by PCSX4ALL team\n"
4f5a1b2a 1684 " Chui, Franxis, Unai\n\n"
1685 "integration, optimization and\n"
3ce7adeb 1686 " frontend (C) 2010-2012 notaz\n";
69af03a2 1687
e16a7e51 1688static int reset_game(void)
1689{
1690 // sanity check
1691 if (bios_sel == 0 && !Config.HLE)
1692 return -1;
1693
1694 ClosePlugins();
1695 OpenPlugins();
1696 SysReset();
1697 if (CheckCdrom() != -1) {
1698 LoadCdrom();
1699 }
1700 return 0;
1701}
1702
51f77282 1703static int reload_plugins(const char *cdimg)
e16a7e51 1704{
76f7048e 1705 pl_vout_buf = NULL;
e16a7e51 1706
1707 ClosePlugins();
51f77282 1708
1709 set_cd_image(cdimg);
e16a7e51 1710 LoadPlugins();
c22b95ab 1711 pcnt_hook_plugins();
e16a7e51 1712 NetOpened = 0;
1713 if (OpenPlugins() == -1) {
1714 me_update_msg("failed to open plugins");
1715 return -1;
1716 }
1717 plugin_call_rearmed_cbs();
1718
0c2871a7 1719 cdrIsoMultidiskCount = 1;
e16a7e51 1720 CdromId[0] = '\0';
1721 CdromLabel[0] = '\0';
1722
51f77282 1723 return 0;
1724}
1725
1726static int run_bios(void)
1727{
1728 if (bios_sel == 0)
1729 return -1;
1730
1731 ready_to_go = 0;
1732 if (reload_plugins(NULL) != 0)
1733 return -1;
e16a7e51 1734 SysReset();
1735
1736 ready_to_go = 1;
1737 return 0;
1738}
1739
51f77282 1740static int run_exe(void)
69af03a2 1741{
51f77282 1742 const char *fname;
1743
1744 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1745 if (fname == NULL)
1746 return -1;
1747
69af03a2 1748 ready_to_go = 0;
51f77282 1749 if (reload_plugins(NULL) != 0)
1750 return -1;
69af03a2 1751
51f77282 1752 SysReset();
1753 if (Load(fname) != 0) {
1754 me_update_msg("exe load failed, bad file?");
1755 printf("meh\n");
bbd837c6 1756 return -1;
69af03a2 1757 }
51f77282 1758
1759 ready_to_go = 1;
1760 return 0;
1761}
1762
1763static int run_cd_image(const char *fname)
1764{
1765 ready_to_go = 0;
1766 reload_plugins(fname);
69af03a2 1767
76d63edf 1768 // always autodetect, menu_sync_config will override as needed
1769 Config.PsxAuto = 1;
1770
69af03a2 1771 if (CheckCdrom() == -1) {
1772 // Only check the CD if we are starting the console with a CD
1773 ClosePlugins();
1774 me_update_msg("unsupported/invalid CD image");
bbd837c6 1775 return -1;
69af03a2 1776 }
1777
bbd837c6 1778 SysReset();
1779
69af03a2 1780 // Read main executable directly from CDRom and start it
1781 if (LoadCdrom() == -1) {
1782 ClosePlugins();
1783 me_update_msg("failed to load CD image");
bbd837c6 1784 return -1;
69af03a2 1785 }
1786
1787 ready_to_go = 1;
47232ea4 1788 snprintf(hud_msg, sizeof(hud_msg), "Booting up...");
1789 hud_new_msg = 2;
bbd837c6 1790 return 0;
69af03a2 1791}
1792
bbd837c6 1793static int romsel_run(void)
69af03a2 1794{
bbd837c6 1795 int prev_gpu, prev_spu;
51f77282 1796 const char *fname;
bbd837c6 1797
1798 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1799 if (fname == NULL)
1800 return -1;
69af03a2 1801
bbd837c6 1802 printf("selected file: %s\n", fname);
1803
dc990066 1804 new_dynarec_clear_full();
1805
bbd837c6 1806 if (run_cd_image(fname) != 0)
1807 return -1;
1808
1809 prev_gpu = gpu_plugsel;
1810 prev_spu = spu_plugsel;
1811 if (menu_load_config(1) != 0)
1812 menu_load_config(0);
1813
1814 // check for plugin changes, have to repeat
1815 // loading if game config changed plugins to reload them
1816 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1817 printf("plugin change detected, reloading plugins..\n");
1818 if (run_cd_image(fname) != 0)
1819 return -1;
1820 }
1821
bab59f00 1822 if (Config.HLE)
1823 printf("note: running without BIOS, expect compatibility problems\n");
1824
bbd837c6 1825 strcpy(last_selected_fname, rom_fname_reload);
1826 return 0;
1827}
1828
1df403c5 1829static int swap_cd_image(void)
1830{
1831 char *fname;
1832
1833 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1834 if (fname == NULL)
1835 return -1;
1836
1837 printf("selected file: %s\n", fname);
1838
1839 CdromId[0] = '\0';
1840 CdromLabel[0] = '\0';
1841
1842 set_cd_image(fname);
1843 if (ReloadCdromPlugin() < 0) {
1844 me_update_msg("failed to load cdr plugin");
1845 return -1;
1846 }
1847 if (CDR_open() < 0) {
1848 me_update_msg("failed to open cdr plugin");
1849 return -1;
1850 }
1851
1852 SetCdOpenCaseTime(time(NULL) + 2);
1853 LidInterrupt();
1854
1855 strcpy(last_selected_fname, rom_fname_reload);
1856 return 0;
1857}
1858
0c2871a7 1859static int swap_cd_multidisk(void)
1860{
1861 cdrIsoMultidiskSelect++;
1862 CdromId[0] = '\0';
1863 CdromLabel[0] = '\0';
1864
1865 CDR_close();
1866 if (CDR_open() < 0) {
1867 me_update_msg("failed to open cdr plugin");
1868 return -1;
1869 }
1870
1871 SetCdOpenCaseTime(time(NULL) + 2);
1872 LidInterrupt();
1873
1874 return 0;
1875}
1876
bbd837c6 1877static int main_menu_handler(int id, int keys)
1878{
69af03a2 1879 switch (id)
1880 {
1881 case MA_MAIN_RESUME_GAME:
3c70c47b 1882 if (ready_to_go)
1883 return 1;
69af03a2 1884 break;
1885 case MA_MAIN_SAVE_STATE:
1886 if (ready_to_go)
1887 return menu_loop_savestate(0);
1888 break;
1889 case MA_MAIN_LOAD_STATE:
1890 if (ready_to_go)
1891 return menu_loop_savestate(1);
1892 break;
1893 case MA_MAIN_RESET_GAME:
e16a7e51 1894 if (ready_to_go && reset_game() == 0)
3c70c47b 1895 return 1;
69af03a2 1896 break;
1897 case MA_MAIN_LOAD_ROM:
bbd837c6 1898 if (romsel_run() == 0)
69af03a2 1899 return 1;
1900 break;
1df403c5 1901 case MA_MAIN_SWAP_CD:
1902 if (swap_cd_image() == 0)
1903 return 1;
1904 break;
0c2871a7 1905 case MA_MAIN_SWAP_CD_MULTI:
1906 if (swap_cd_multidisk() == 0)
1907 return 1;
1908 break;
e16a7e51 1909 case MA_MAIN_RUN_BIOS:
1910 if (run_bios() == 0)
1911 return 1;
1912 break;
51f77282 1913 case MA_MAIN_RUN_EXE:
1914 if (run_exe() == 0)
1915 return 1;
1916 break;
69af03a2 1917 case MA_MAIN_CREDITS:
4f5a1b2a 1918 draw_menu_message(credits_text, draw_frame_credits);
69af03a2 1919 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1920 break;
1921 case MA_MAIN_EXIT:
bd6267e6 1922 OnFile_Exit();
1923 break;
69af03a2 1924 default:
1925 lprintf("%s: something unknown selected\n", __FUNCTION__);
1926 break;
1927 }
1928
1929 return 0;
1930}
1931
51f77282 1932static menu_entry e_menu_main2[] =
1933{
0c2871a7 1934 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1935 mee_handler_id("Next multidisk CD", MA_MAIN_SWAP_CD_MULTI, main_menu_handler),
1936 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1937 mee_handler_id("Run EXE", MA_MAIN_RUN_EXE, main_menu_handler),
51f77282 1938 mee_handler ("Memcard manager", menu_loop_memcards),
1939 mee_end,
1940};
1941
1942static int main_menu2_handler(int id, int keys)
1943{
1944 static int sel = 0;
1945
1946 me_enable(e_menu_main2, MA_MAIN_SWAP_CD, ready_to_go);
0c2871a7 1947 me_enable(e_menu_main2, MA_MAIN_SWAP_CD_MULTI, ready_to_go && cdrIsoMultidiskCount > 1);
51f77282 1948 me_enable(e_menu_main2, MA_MAIN_RUN_BIOS, bios_sel != 0);
1949
1950 return me_loop_d(e_menu_main2, &sel, NULL, draw_frame_main);
1951}
1952
1953static const char h_extra[] = "Change CD, manage memcards..\n";
1954
69af03a2 1955static menu_entry e_menu_main[] =
1956{
1957 mee_label (""),
1958 mee_label (""),
1959 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1960 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1961 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1962 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1963 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1964 mee_handler ("Options", menu_loop_options),
1965 mee_handler ("Controls", menu_loop_keyconfig),
51f77282 1966 mee_handler_h ("Extra stuff", main_menu2_handler, h_extra),
69af03a2 1967 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1968 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1969 mee_end,
1970};
1971
3c70c47b 1972// ----------------------------
1973
bd6267e6 1974static void menu_leave_emu(void);
1975
69af03a2 1976void menu_loop(void)
1977{
1978 static int sel = 0;
1979
bd6267e6 1980 menu_leave_emu();
69af03a2 1981
4f5a1b2a 1982 if (bioses[1] == NULL && !warned_about_bios) {
1983 menu_bios_warn();
1984 warned_about_bios = 1;
1985 }
1986
69af03a2 1987 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
e16a7e51 1988 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1989 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
69af03a2 1990 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1991
69af03a2 1992 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1993
1994 do {
51f77282 1995 me_loop_d(e_menu_main, &sel, NULL, draw_frame_main);
69af03a2 1996 } while (!ready_to_go);
1997
1998 /* wait until menu, ok, back is released */
1999 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
2000 ;
2001
2002 in_set_config_int(0, IN_CFG_BLOCKING, 0);
2003
3c70c47b 2004 menu_prepare_emu();
2005}
2006
51f77282 2007static int qsort_strcmp(const void *p1, const void *p2)
2008{
2009 char * const *s1 = (char * const *)p1;
2010 char * const *s2 = (char * const *)p2;
2011 return strcasecmp(*s1, *s2);
2012}
2013
e6eb2066 2014static void scan_bios_plugins(void)
bbd837c6 2015{
2016 char fname[MAXPATHLEN];
2017 struct dirent *ent;
51f77282 2018 int bios_i, gpu_i, spu_i, mc_i;
bbd837c6 2019 char *p;
2020 DIR *dir;
2021
e6eb2066 2022 bioses[0] = "HLE";
bbd837c6 2023 gpu_plugins[0] = "builtin_gpu";
2024 spu_plugins[0] = "builtin_spu";
51f77282 2025 memcards[0] = "(none)";
2026 bios_i = gpu_i = spu_i = mc_i = 1;
e6eb2066 2027
2028 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
2029 dir = opendir(fname);
2030 if (dir == NULL) {
2031 perror("scan_bios_plugins bios opendir");
2032 goto do_plugins;
2033 }
2034
2035 while (1) {
2036 struct stat st;
2037
2038 errno = 0;
2039 ent = readdir(dir);
2040 if (ent == NULL) {
2041 if (errno != 0)
2042 perror("readdir");
2043 break;
2044 }
2045
2046 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2047 continue;
2048
2049 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
2050 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
2051 printf("bad BIOS file: %s\n", ent->d_name);
2052 continue;
2053 }
2054
2055 if (bios_i < ARRAY_SIZE(bioses) - 1) {
2056 bioses[bios_i++] = strdup(ent->d_name);
2057 continue;
2058 }
2059
2060 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
2061 }
2062
2063 closedir(dir);
bbd837c6 2064
e6eb2066 2065do_plugins:
bbd837c6 2066 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
2067 dir = opendir(fname);
2068 if (dir == NULL) {
51f77282 2069 perror("scan_bios_plugins plugins opendir");
2070 goto do_memcards;
bbd837c6 2071 }
2072
2073 while (1) {
2074 void *h, *tmp;
2075
2076 errno = 0;
2077 ent = readdir(dir);
2078 if (ent == NULL) {
2079 if (errno != 0)
2080 perror("readdir");
2081 break;
2082 }
2083 p = strstr(ent->d_name, ".so");
2084 if (p == NULL)
2085 continue;
2086
2087 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
2088 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
2089 if (h == NULL) {
2090 fprintf(stderr, "%s\n", dlerror());
2091 continue;
2092 }
2093
2094 // now what do we have here?
2095 tmp = dlsym(h, "GPUinit");
2096 if (tmp) {
2097 dlclose(h);
2098 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
2099 gpu_plugins[gpu_i++] = strdup(ent->d_name);
2100 continue;
2101 }
2102
2103 tmp = dlsym(h, "SPUinit");
2104 if (tmp) {
2105 dlclose(h);
2106 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
2107 spu_plugins[spu_i++] = strdup(ent->d_name);
2108 continue;
2109 }
2110
2111 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
2112 dlclose(h);
2113 }
2114
2115 closedir(dir);
51f77282 2116
2117do_memcards:
2118 dir = opendir("." MEMCARD_DIR);
2119 if (dir == NULL) {
2120 perror("scan_bios_plugins memcards opendir");
2121 return;
2122 }
2123
2124 while (1) {
2125 struct stat st;
2126
2127 errno = 0;
2128 ent = readdir(dir);
2129 if (ent == NULL) {
2130 if (errno != 0)
2131 perror("readdir");
2132 break;
2133 }
2134
2135 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
2136 continue;
2137
2138 snprintf(fname, sizeof(fname), "." MEMCARD_DIR "%s", ent->d_name);
2139 if (stat(fname, &st) != 0) {
2140 printf("bad memcard file: %s\n", ent->d_name);
2141 continue;
2142 }
2143
2144 if (mc_i < ARRAY_SIZE(memcards) - 1) {
2145 memcards[mc_i++] = strdup(ent->d_name);
2146 continue;
2147 }
2148
2149 printf("too many memcards, dropping \"%s\"\n", ent->d_name);
2150 }
2151
2152 if (mc_i > 2)
2153 qsort(memcards + 1, mc_i - 1, sizeof(memcards[0]), qsort_strcmp);
2154
2155 closedir(dir);
bbd837c6 2156}
2157
3c70c47b 2158void menu_init(void)
2159{
4f3639fa 2160 char buff[MAXPATHLEN];
2161
2162 strcpy(last_selected_fname, "/media");
2163
e6eb2066 2164 scan_bios_plugins();
4f3639fa 2165 pnd_menu_init();
2166 menu_init_common();
2167
3c70c47b 2168 menu_set_defconfig();
2169 menu_load_config(0);
3c70c47b 2170 last_psx_w = 320;
2171 last_psx_h = 240;
bd6267e6 2172 last_psx_bpp = 16;
2173
4f3639fa 2174 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
2175 if (g_menubg_src_ptr == NULL)
2176 exit(1);
2177 emu_make_path(buff, "skin/background.png", sizeof(buff));
2178 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
55b0eeea 2179
2180#ifndef __ARM_ARCH_7A__ /* XXX */
2181 me_enable(e_menu_options, MA_OPT_DISP_OPTS, 0);
2182 me_enable(e_menu_keyconfig, MA_CTRL_NUBS_BTNS, 0);
2183#else
b944a30e 2184 me_enable(e_menu_keyconfig, MA_CTRL_VIBRATION, 0);
55b0eeea 2185 me_enable(e_menu_keyconfig, MA_CTRL_DEADZONE, 0);
2186#endif
3c70c47b 2187}
2188
bd6267e6 2189void menu_notify_mode_change(int w, int h, int bpp)
3c70c47b 2190{
a185be70 2191 float mult;
2192 int imult;
2193
3c70c47b 2194 last_psx_w = w;
2195 last_psx_h = h;
bd6267e6 2196 last_psx_bpp = bpp;
3c70c47b 2197
a185be70 2198 switch (scaling) {
2199 case SCALE_1_1:
3c70c47b 2200 g_layer_w = w; g_layer_h = h;
a185be70 2201 break;
2202
a185be70 2203 case SCALE_4_3v2:
fb005d87 2204 if (h > g_menuscreen_h || (240 < h && h <= 360))
2205 goto fractional_4_3;
2206
a185be70 2207 // 4:3 that prefers integer scaling
2208 imult = g_menuscreen_h / h;
2209 g_layer_w = w * imult;
2210 g_layer_h = h * imult;
2211 mult = (float)g_layer_w / (float)g_layer_h;
2212 if (mult < 1.25f || mult > 1.666f)
2213 g_layer_w = 4.0f/3.0f * (float)g_layer_h;
2214 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2215 break;
2216
fb005d87 2217 fractional_4_3:
2218 case SCALE_4_3:
2219 mult = 240.0f / (float)h * 4.0f / 3.0f;
2220 if (h > 256)
2221 mult *= 2.0f;
2222 g_layer_w = mult * (float)g_menuscreen_h;
2223 g_layer_h = g_menuscreen_h;
2224 printf(" -> %dx%d %.1f\n", g_layer_w, g_layer_h, mult);
2225 break;
2226
a185be70 2227 case SCALE_FULLSCREEN:
2228 g_layer_w = g_menuscreen_w;
2229 g_layer_h = g_menuscreen_h;
2230 break;
2231
2232 default:
2233 break;
3c70c47b 2234 }
a185be70 2235
2236 g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
2237 g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
2238 if (g_layer_x < 0) g_layer_x = 0;
2239 if (g_layer_y < 0) g_layer_y = 0;
2240 if (g_layer_w > g_menuscreen_w) g_layer_w = g_menuscreen_w;
2241 if (g_layer_h > g_menuscreen_h) g_layer_w = g_menuscreen_h;
3c70c47b 2242}
2243
bd6267e6 2244static void menu_leave_emu(void)
2245{
6d1a1ac2 2246 if (GPU_close != NULL) {
2247 int ret = GPU_close();
2248 if (ret)
2249 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
2250 }
bd6267e6 2251
55b0eeea 2252 plat_video_menu_enter(ready_to_go);
2253
4f3639fa 2254 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
76f7048e 2255 if (pl_vout_buf != NULL && ready_to_go && last_psx_bpp == 16) {
bd6267e6 2256 int x = max(0, g_menuscreen_w - last_psx_w);
2257 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
2258 int w = min(g_menuscreen_w, last_psx_w);
2259 int h = min(g_menuscreen_h, last_psx_h);
4f3639fa 2260 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
76f7048e 2261 u16 *s = pl_vout_buf;
bd6267e6 2262
2263 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
4f3639fa 2264 menu_darken_bg(d, s, w, 0);
bd6267e6 2265 }
fba06457 2266
1bd9ee68 2267 if (ready_to_go)
55b0eeea 2268 cpu_clock = plat_cpu_clock_get();
bd6267e6 2269}
2270
3c70c47b 2271void menu_prepare_emu(void)
2272{
b5e7e49a 2273 R3000Acpu *prev_cpu = psxCpu;
2274
fba06457 2275 plat_video_menu_leave();
2276
a185be70 2277 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
bd6267e6 2278
b5e7e49a 2279 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2280 if (psxCpu != prev_cpu)
2281 // note that this does not really reset, just clears drc caches
2282 psxCpu->Reset();
2283
bd6267e6 2284 // core doesn't care about Config.Cdda changes,
2285 // so handle them manually here
2286 if (Config.Cdda)
2287 CDR_stop();
6d1a1ac2 2288
907b1e90 2289 menu_sync_config();
cefe86b7 2290 apply_lcdrate(Config.PsxType);
2291 apply_filter(filter);
55b0eeea 2292 plat_cpu_clock_apply(cpu_clock);
fba06457 2293
e64dc4c5 2294 // push config to GPU plugin
2295 plugin_call_rearmed_cbs();
2296
6d1a1ac2 2297 if (GPU_open != NULL) {
6d1a1ac2 2298 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
2299 if (ret)
2300 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
2301 }
384f5f43 2302
4c08b9e7 2303 dfinput_activate();
69af03a2 2304}
2305
2306void me_update_msg(const char *msg)
2307{
2308 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
2309 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
2310
2311 menu_error_time = plat_get_ticks_ms();
2312 lprintf("msg: %s\n", menu_error_msg);
2313}
2314