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