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