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