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