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