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