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