drc: clear dynarec completely on CD image change
[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 "common/plat.h"
25#include "../libpcsxcore/misc.h"
26#include "../libpcsxcore/cdrom.h"
27#include "../libpcsxcore/psemu_plugin_defs.h"
28#include "../libpcsxcore/new_dynarec/new_dynarec.h"
29#include "../plugins/dfinput/pad.h"
30#include "revision.h"
31
32#define MENU_X2 1
33#define array_size(x) (sizeof(x) / sizeof(x[0]))
34
35typedef enum
36{
37 MA_NONE = 1,
38 MA_MAIN_RESUME_GAME,
39 MA_MAIN_SAVE_STATE,
40 MA_MAIN_LOAD_STATE,
41 MA_MAIN_RESET_GAME,
42 MA_MAIN_LOAD_ROM,
43 MA_MAIN_SWAP_CD,
44 MA_MAIN_RUN_BIOS,
45 MA_MAIN_CONTROLS,
46 MA_MAIN_CREDITS,
47 MA_MAIN_EXIT,
48 MA_CTRL_PLAYER1,
49 MA_CTRL_PLAYER2,
50 MA_CTRL_EMU,
51 MA_CTRL_DEV_FIRST,
52 MA_CTRL_DEV_NEXT,
53 MA_CTRL_DONE,
54 MA_OPT_SAVECFG,
55 MA_OPT_SAVECFG_GAME,
56 MA_OPT_CPU_CLOCKS,
57 MA_OPT_FILTERING,
58} menu_id;
59
60enum {
61 SCALE_1_1,
62 SCALE_4_3,
63 SCALE_FULLSCREEN,
64 SCALE_CUSTOM,
65};
66
67static int last_psx_w, last_psx_h, last_psx_bpp;
68static int scaling, filter, cpu_clock, cpu_clock_st;
69static char rom_fname_reload[MAXPATHLEN];
70static char last_selected_fname[MAXPATHLEN];
71static int warned_about_bios, region, in_type_sel;
72int g_opts;
73
74// from softgpu plugin
75extern int iUseDither;
76extern int UseFrameSkip;
77extern uint32_t dwActFixes;
78extern float fFrameRateHz;
79extern int dwFrameRateTicks;
80
81// sound plugin
82extern int iUseReverb;
83extern int iUseInterpolation;
84extern int iXAPitch;
85extern int iSPUIRQWait;
86extern int iUseTimer;
87
88static const char *bioses[24];
89static const char *gpu_plugins[16];
90static const char *spu_plugins[16];
91static int bios_sel, gpu_plugsel, spu_plugsel;
92
93
94static int min(int x, int y) { return x < y ? x : y; }
95static int max(int x, int y) { return x > y ? x : y; }
96
97void emu_make_path(char *buff, const char *end, int size)
98{
99 int pos, end_len;
100
101 end_len = strlen(end);
102 pos = plat_get_root_dir(buff, size);
103 strncpy(buff + pos, end, size - pos);
104 buff[size - 1] = 0;
105 if (pos + end_len > size - 1)
106 printf("Warning: path truncated: %s\n", buff);
107}
108
109static int emu_check_save_file(int slot)
110{
111 int ret = emu_check_state(slot);
112 return ret == 0 ? 1 : 0;
113}
114
115static int emu_save_load_game(int load, int unused)
116{
117 int ret;
118
119 if (load) {
120 ret = emu_load_state(state_slot);
121
122 // reflect hle/bios mode from savestate
123 if (Config.HLE)
124 bios_sel = 0;
125 else if (bios_sel == 0 && bioses[1] != NULL)
126 // XXX: maybe find the right bios instead
127 bios_sel = 1;
128 }
129 else
130 ret = emu_save_state(state_slot);
131
132 return ret;
133}
134
135// propagate menu settings to the emu vars
136static void menu_sync_config(void)
137{
138 Config.PsxAuto = 1;
139 if (region > 0) {
140 Config.PsxAuto = 0;
141 Config.PsxType = region - 1;
142 }
143 in_type = in_type_sel ? PSE_PAD_TYPE_ANALOGPAD : PSE_PAD_TYPE_STANDARD;
144
145 pl_frame_interval = Config.PsxType ? 20000 : 16667;
146 // used by P.E.Op.S. frameskip code
147 fFrameRateHz = Config.PsxType ? 50.0f : 59.94f;
148 dwFrameRateTicks = (100000*100 / (unsigned long)(fFrameRateHz*100));
149}
150
151static void menu_set_defconfig(void)
152{
153 g_opts = 0;
154 scaling = SCALE_4_3;
155
156 region = 0;
157 in_type_sel = 0;
158 Config.Xa = Config.Cdda = Config.Sio =
159 Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
160
161 iUseDither = 0;
162 UseFrameSkip = 1;
163 dwActFixes = 1<<7;
164
165 iUseReverb = 2;
166 iUseInterpolation = 1;
167 iXAPitch = 0;
168 iSPUIRQWait = 1;
169 iUseTimer = 2;
170
171 menu_sync_config();
172}
173
174#define CE_CONFIG_STR(val) \
175 { #val, 0, Config.val }
176
177#define CE_CONFIG_VAL(val) \
178 { #val, sizeof(Config.val), &Config.val }
179
180#define CE_STR(val) \
181 { #val, 0, val }
182
183#define CE_INTVAL(val) \
184 { #val, sizeof(val), &val }
185
186// 'versioned' var, used when defaults change
187#define CE_INTVAL_V(val, ver) \
188 { #val #ver, sizeof(val), &val }
189
190static const struct {
191 const char *name;
192 size_t len;
193 void *val;
194} config_data[] = {
195 CE_CONFIG_STR(Bios),
196 CE_CONFIG_STR(Gpu),
197 CE_CONFIG_STR(Spu),
198// CE_CONFIG_STR(Cdr),
199 CE_CONFIG_VAL(Xa),
200 CE_CONFIG_VAL(Sio),
201 CE_CONFIG_VAL(Mdec),
202 CE_CONFIG_VAL(Cdda),
203 CE_CONFIG_VAL(Debug),
204 CE_CONFIG_VAL(PsxOut),
205 CE_CONFIG_VAL(SpuIrq),
206 CE_CONFIG_VAL(RCntFix),
207 CE_CONFIG_VAL(VSyncWA),
208 CE_CONFIG_VAL(Cpu),
209 CE_INTVAL(region),
210 CE_INTVAL(scaling),
211 CE_INTVAL(g_layer_x),
212 CE_INTVAL(g_layer_y),
213 CE_INTVAL(g_layer_w),
214 CE_INTVAL(g_layer_h),
215 CE_INTVAL(filter),
216 CE_INTVAL(state_slot),
217 CE_INTVAL(cpu_clock),
218 CE_INTVAL(g_opts),
219 CE_INTVAL(in_type_sel),
220 CE_INTVAL(iUseDither),
221 CE_INTVAL(UseFrameSkip),
222 CE_INTVAL(dwActFixes),
223 CE_INTVAL(iUseReverb),
224 CE_INTVAL(iXAPitch),
225 CE_INTVAL_V(iUseInterpolation, 2),
226 CE_INTVAL_V(iSPUIRQWait, 2),
227 CE_INTVAL(iUseTimer),
228 CE_INTVAL(warned_about_bios),
229};
230
231static char *get_cd_label(void)
232{
233 static char trimlabel[33];
234 int j;
235
236 strncpy(trimlabel, CdromLabel, 32);
237 trimlabel[32] = 0;
238 for (j = 31; j >= 0; j--)
239 if (trimlabel[j] == ' ')
240 trimlabel[j] = 0;
241
242 return trimlabel;
243}
244
245static void make_cfg_fname(char *buf, size_t size, int is_game)
246{
247 if (is_game)
248 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
249 else
250 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
251}
252
253static void keys_write_all(FILE *f);
254
255static int menu_write_config(int is_game)
256{
257 char cfgfile[MAXPATHLEN];
258 FILE *f;
259 int i;
260
261 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
262 f = fopen(cfgfile, "w");
263 if (f == NULL) {
264 printf("menu_write_config: failed to open: %s\n", cfgfile);
265 return -1;
266 }
267
268 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
269 fprintf(f, "%s = ", config_data[i].name);
270 switch (config_data[i].len) {
271 case 0:
272 fprintf(f, "%s\n", (char *)config_data[i].val);
273 break;
274 case 1:
275 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
276 break;
277 case 2:
278 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
279 break;
280 case 4:
281 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
282 break;
283 default:
284 printf("menu_write_config: unhandled len %d for %s\n",
285 config_data[i].len, config_data[i].name);
286 break;
287 }
288 }
289
290 if (!is_game)
291 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
292
293 keys_write_all(f);
294 fclose(f);
295
296 return 0;
297}
298
299static void parse_str_val(char *cval, const char *src)
300{
301 char *tmp;
302 strncpy(cval, src, MAXPATHLEN);
303 cval[MAXPATHLEN - 1] = 0;
304 tmp = strchr(cval, '\n');
305 if (tmp == NULL)
306 tmp = strchr(cval, '\r');
307 if (tmp != NULL)
308 *tmp = 0;
309}
310
311static void keys_load_all(const char *cfg);
312
313static int menu_load_config(int is_game)
314{
315 char cfgfile[MAXPATHLEN];
316 int i, ret = -1;
317 long size;
318 char *cfg;
319 FILE *f;
320
321 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
322 f = fopen(cfgfile, "r");
323 if (f == NULL) {
324 printf("menu_load_config: failed to open: %s\n", cfgfile);
325 return -1;
326 }
327
328 fseek(f, 0, SEEK_END);
329 size = ftell(f);
330 if (size <= 0) {
331 printf("bad size %ld: %s\n", size, cfgfile);
332 goto fail;
333 }
334
335 cfg = malloc(size + 1);
336 if (cfg == NULL)
337 goto fail;
338
339 fseek(f, 0, SEEK_SET);
340 if (fread(cfg, 1, size, f) != size) {
341 printf("failed to read: %s\n", cfgfile);
342 goto fail_read;
343 }
344 cfg[size] = 0;
345
346 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
347 char *tmp, *tmp2;
348 u32 val;
349
350 tmp = strstr(cfg, config_data[i].name);
351 if (tmp == NULL)
352 continue;
353 tmp += strlen(config_data[i].name);
354 if (strncmp(tmp, " = ", 3) != 0)
355 continue;
356 tmp += 3;
357
358 if (config_data[i].len == 0) {
359 parse_str_val(config_data[i].val, tmp);
360 continue;
361 }
362
363 tmp2 = NULL;
364 val = strtoul(tmp, &tmp2, 16);
365 if (tmp2 == NULL || tmp == tmp2)
366 continue; // parse failed
367
368 switch (config_data[i].len) {
369 case 1:
370 *(u8 *)config_data[i].val = val;
371 break;
372 case 2:
373 *(u16 *)config_data[i].val = val;
374 break;
375 case 4:
376 *(u32 *)config_data[i].val = val;
377 break;
378 default:
379 printf("menu_load_config: unhandled len %d for %s\n",
380 config_data[i].len, config_data[i].name);
381 break;
382 }
383 }
384
385 if (!is_game) {
386 char *tmp = strstr(cfg, "lastcdimg = ");
387 if (tmp != NULL) {
388 tmp += 12;
389 parse_str_val(last_selected_fname, tmp);
390 }
391 }
392
393 menu_sync_config();
394
395 // sync plugins
396 for (i = bios_sel = 0; bioses[i] != NULL; i++)
397 if (strcmp(Config.Bios, bioses[i]) == 0)
398 { bios_sel = i; break; }
399
400 for (i = gpu_plugsel = 0; gpu_plugins[i] != NULL; i++)
401 if (strcmp(Config.Gpu, gpu_plugins[i]) == 0)
402 { gpu_plugsel = i; break; }
403
404 for (i = spu_plugsel = 0; spu_plugins[i] != NULL; i++)
405 if (strcmp(Config.Spu, spu_plugins[i]) == 0)
406 { spu_plugsel = i; break; }
407
408 keys_load_all(cfg);
409 ret = 0;
410fail_read:
411 free(cfg);
412fail:
413 fclose(f);
414 return ret;
415}
416
417// rrrr rggg gggb bbbb
418static unsigned short fname2color(const char *fname)
419{
420 static const char *cdimg_exts[] = { ".bin", ".img", ".iso", ".cue", ".z", ".bz", ".znx", ".pbp" };
421 static const char *other_exts[] = { ".ccd", ".toc", ".mds", ".sub", ".table", ".index", ".sbi" };
422 const char *ext = strrchr(fname, '.');
423 int i;
424
425 if (ext == NULL)
426 return 0xffff;
427 for (i = 0; i < array_size(cdimg_exts); i++)
428 if (strcasecmp(ext, cdimg_exts[i]) == 0)
429 return 0x7bff;
430 for (i = 0; i < array_size(other_exts); i++)
431 if (strcasecmp(ext, other_exts[i]) == 0)
432 return 0xa514;
433 return 0xffff;
434}
435
436static void draw_savestate_bg(int slot);
437
438#define MENU_ALIGN_LEFT
439#define menu_init menu_init_common
440#include "common/menu.c"
441#undef menu_init
442
443// a bit of black magic here
444static void draw_savestate_bg(int slot)
445{
446 extern void bgr555_to_rgb565(void *dst, void *src, int bytes);
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(void)
1181{
1182 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1183}
1184
1185static void debug_menu_loop(void)
1186{
1187 int inp;
1188
1189 while (1)
1190 {
1191 menu_draw_begin(1);
1192 draw_frame_debug();
1193 menu_draw_end();
1194
1195 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1196 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1197 if (inp & PBTN_MBACK)
1198 return;
1199 }
1200}
1201
1202// ------------ main menu ------------
1203
1204static void menu_bios_warn(void)
1205{
1206 int inp;
1207 static const char msg[] =
1208 "You don't seem to have copied any BIOS files to\n"
1209 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1210 "While many games work fine with fake (HLE) BIOS,\n"
1211 "others (like MGS and FF8) require BIOS to work.\n"
1212 "After copying the file, you'll also need to\n"
1213 "select it in the emu's options->[BIOS/Plugins]\n\n"
1214 "The file is usually named SCPH1001.BIN, but\n"
1215 "other not compressed files can be used too.\n\n"
1216 "Press (B) or (X) to continue";
1217
1218 while (1)
1219 {
1220 menu_draw_begin(1);
1221 draw_menu_message(msg, NULL);
1222 menu_draw_end();
1223
1224 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1225 if (inp & (PBTN_MBACK|PBTN_MOK))
1226 return;
1227 }
1228}
1229
1230// ------------ main menu ------------
1231
1232void OnFile_Exit();
1233
1234static void draw_frame_main(void)
1235{
1236 if (CdromId[0] != 0) {
1237 char buff[64];
1238 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1239 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1240 Config.HLE ? "HLE" : "BIOS");
1241 smalltext_out16(4, 1, buff, 0x105f);
1242 }
1243}
1244
1245static void draw_frame_credits(void)
1246{
1247 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1248}
1249
1250static const char credits_text[] =
1251 "PCSX-ReARMed\n\n"
1252 "(C) 1999-2003 PCSX Team\n"
1253 "(C) 2005-2009 PCSX-df Team\n"
1254 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1255 "GPU and SPU code by Pete Bernert\n"
1256 " and the P.E.Op.S. team\n"
1257 "ARM recompiler (C) 2009-2011 Ari64\n"
1258 "PCSX4ALL plugins by PCSX4ALL team\n"
1259 " Chui, Franxis, Unai\n\n"
1260 "integration, optimization and\n"
1261 " frontend (C) 2010-2011 notaz\n";
1262
1263static int reset_game(void)
1264{
1265 // sanity check
1266 if (bios_sel == 0 && !Config.HLE)
1267 return -1;
1268
1269 ClosePlugins();
1270 OpenPlugins();
1271 SysReset();
1272 if (CheckCdrom() != -1) {
1273 LoadCdrom();
1274 }
1275 return 0;
1276}
1277
1278static int run_bios(void)
1279{
1280 if (bios_sel == 0)
1281 return -1;
1282
1283 ready_to_go = 0;
1284 pl_fbdev_buf = NULL;
1285
1286 ClosePlugins();
1287 set_cd_image(NULL);
1288 LoadPlugins();
1289 pcnt_hook_plugins();
1290 NetOpened = 0;
1291 if (OpenPlugins() == -1) {
1292 me_update_msg("failed to open plugins");
1293 return -1;
1294 }
1295 plugin_call_rearmed_cbs();
1296
1297 CdromId[0] = '\0';
1298 CdromLabel[0] = '\0';
1299
1300 SysReset();
1301
1302 ready_to_go = 1;
1303 return 0;
1304}
1305
1306static int run_cd_image(const char *fname)
1307{
1308 ready_to_go = 0;
1309 pl_fbdev_buf = NULL;
1310
1311 ClosePlugins();
1312 set_cd_image(fname);
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 if (CheckCdrom() == -1) {
1323 // Only check the CD if we are starting the console with a CD
1324 ClosePlugins();
1325 me_update_msg("unsupported/invalid CD image");
1326 return -1;
1327 }
1328
1329 SysReset();
1330
1331 // Read main executable directly from CDRom and start it
1332 if (LoadCdrom() == -1) {
1333 ClosePlugins();
1334 me_update_msg("failed to load CD image");
1335 return -1;
1336 }
1337
1338 ready_to_go = 1;
1339 return 0;
1340}
1341
1342static int romsel_run(void)
1343{
1344 int prev_gpu, prev_spu;
1345 char *fname;
1346
1347 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1348 if (fname == NULL)
1349 return -1;
1350
1351 printf("selected file: %s\n", fname);
1352
1353 new_dynarec_clear_full();
1354
1355 if (run_cd_image(fname) != 0)
1356 return -1;
1357
1358 prev_gpu = gpu_plugsel;
1359 prev_spu = spu_plugsel;
1360 if (menu_load_config(1) != 0)
1361 menu_load_config(0);
1362
1363 // check for plugin changes, have to repeat
1364 // loading if game config changed plugins to reload them
1365 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1366 printf("plugin change detected, reloading plugins..\n");
1367 if (run_cd_image(fname) != 0)
1368 return -1;
1369 }
1370
1371 strcpy(last_selected_fname, rom_fname_reload);
1372 return 0;
1373}
1374
1375static int swap_cd_image(void)
1376{
1377 char *fname;
1378
1379 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1380 if (fname == NULL)
1381 return -1;
1382
1383 printf("selected file: %s\n", fname);
1384
1385 CdromId[0] = '\0';
1386 CdromLabel[0] = '\0';
1387
1388 set_cd_image(fname);
1389 if (ReloadCdromPlugin() < 0) {
1390 me_update_msg("failed to load cdr plugin");
1391 return -1;
1392 }
1393 if (CDR_open() < 0) {
1394 me_update_msg("failed to open cdr plugin");
1395 return -1;
1396 }
1397
1398 SetCdOpenCaseTime(time(NULL) + 2);
1399 LidInterrupt();
1400
1401 strcpy(last_selected_fname, rom_fname_reload);
1402 return 0;
1403}
1404
1405static int main_menu_handler(int id, int keys)
1406{
1407 switch (id)
1408 {
1409 case MA_MAIN_RESUME_GAME:
1410 if (ready_to_go)
1411 return 1;
1412 break;
1413 case MA_MAIN_SAVE_STATE:
1414 if (ready_to_go)
1415 return menu_loop_savestate(0);
1416 break;
1417 case MA_MAIN_LOAD_STATE:
1418 if (ready_to_go)
1419 return menu_loop_savestate(1);
1420 break;
1421 case MA_MAIN_RESET_GAME:
1422 if (ready_to_go && reset_game() == 0)
1423 return 1;
1424 break;
1425 case MA_MAIN_LOAD_ROM:
1426 if (romsel_run() == 0)
1427 return 1;
1428 break;
1429 case MA_MAIN_SWAP_CD:
1430 if (swap_cd_image() == 0)
1431 return 1;
1432 break;
1433 case MA_MAIN_RUN_BIOS:
1434 if (run_bios() == 0)
1435 return 1;
1436 break;
1437 case MA_MAIN_CREDITS:
1438 draw_menu_message(credits_text, draw_frame_credits);
1439 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1440 break;
1441 case MA_MAIN_EXIT:
1442 OnFile_Exit();
1443 break;
1444 default:
1445 lprintf("%s: something unknown selected\n", __FUNCTION__);
1446 break;
1447 }
1448
1449 return 0;
1450}
1451
1452static menu_entry e_menu_main[] =
1453{
1454 mee_label (""),
1455 mee_label (""),
1456 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1457 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1458 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1459 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1460 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1461 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1462 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1463 mee_handler ("Options", menu_loop_options),
1464 mee_handler ("Controls", menu_loop_keyconfig),
1465 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1466 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1467 mee_end,
1468};
1469
1470// ----------------------------
1471
1472static void menu_leave_emu(void);
1473
1474void menu_loop(void)
1475{
1476 static int sel = 0;
1477
1478 menu_leave_emu();
1479
1480 if (bioses[1] == NULL && !warned_about_bios) {
1481 menu_bios_warn();
1482 warned_about_bios = 1;
1483 }
1484
1485 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1486 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1487 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
1488 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1489 me_enable(e_menu_main, MA_MAIN_SWAP_CD, ready_to_go);
1490 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1491
1492 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1493
1494 do {
1495 me_loop(e_menu_main, &sel, draw_frame_main);
1496 } while (!ready_to_go);
1497
1498 /* wait until menu, ok, back is released */
1499 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1500 ;
1501
1502 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1503
1504 menu_prepare_emu();
1505}
1506
1507static void scan_bios_plugins(void)
1508{
1509 char fname[MAXPATHLEN];
1510 struct dirent *ent;
1511 int bios_i, gpu_i, spu_i;
1512 char *p;
1513 DIR *dir;
1514
1515 bioses[0] = "HLE";
1516 gpu_plugins[0] = "builtin_gpu";
1517 spu_plugins[0] = "builtin_spu";
1518 bios_i = gpu_i = spu_i = 1;
1519
1520 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1521 dir = opendir(fname);
1522 if (dir == NULL) {
1523 perror("scan_bios_plugins bios opendir");
1524 goto do_plugins;
1525 }
1526
1527 while (1) {
1528 struct stat st;
1529
1530 errno = 0;
1531 ent = readdir(dir);
1532 if (ent == NULL) {
1533 if (errno != 0)
1534 perror("readdir");
1535 break;
1536 }
1537
1538 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1539 continue;
1540
1541 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1542 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1543 printf("bad BIOS file: %s\n", ent->d_name);
1544 continue;
1545 }
1546
1547 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1548 bioses[bios_i++] = strdup(ent->d_name);
1549 continue;
1550 }
1551
1552 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1553 }
1554
1555 closedir(dir);
1556
1557do_plugins:
1558 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1559 dir = opendir(fname);
1560 if (dir == NULL) {
1561 perror("scan_bios_plugins opendir");
1562 return;
1563 }
1564
1565 while (1) {
1566 void *h, *tmp;
1567
1568 errno = 0;
1569 ent = readdir(dir);
1570 if (ent == NULL) {
1571 if (errno != 0)
1572 perror("readdir");
1573 break;
1574 }
1575 p = strstr(ent->d_name, ".so");
1576 if (p == NULL)
1577 continue;
1578
1579 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1580 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1581 if (h == NULL) {
1582 fprintf(stderr, "%s\n", dlerror());
1583 continue;
1584 }
1585
1586 // now what do we have here?
1587 tmp = dlsym(h, "GPUinit");
1588 if (tmp) {
1589 dlclose(h);
1590 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1591 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1592 continue;
1593 }
1594
1595 tmp = dlsym(h, "SPUinit");
1596 if (tmp) {
1597 dlclose(h);
1598 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1599 spu_plugins[spu_i++] = strdup(ent->d_name);
1600 continue;
1601 }
1602
1603 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1604 dlclose(h);
1605 }
1606
1607 closedir(dir);
1608}
1609
1610void menu_init(void)
1611{
1612 char buff[MAXPATHLEN];
1613
1614 strcpy(last_selected_fname, "/media");
1615
1616 scan_bios_plugins();
1617 pnd_menu_init();
1618 menu_init_common();
1619
1620 menu_set_defconfig();
1621 menu_load_config(0);
1622 last_psx_w = 320;
1623 last_psx_h = 240;
1624 last_psx_bpp = 16;
1625
1626 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1627 if (g_menubg_src_ptr == NULL)
1628 exit(1);
1629 emu_make_path(buff, "skin/background.png", sizeof(buff));
1630 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1631}
1632
1633void menu_notify_mode_change(int w, int h, int bpp)
1634{
1635 last_psx_w = w;
1636 last_psx_h = h;
1637 last_psx_bpp = bpp;
1638
1639 if (scaling == SCALE_1_1) {
1640 g_layer_x = 800/2 - w/2; g_layer_y = 480/2 - h/2;
1641 g_layer_w = w; g_layer_h = h;
1642 }
1643}
1644
1645static void menu_leave_emu(void)
1646{
1647 if (GPU_close != NULL) {
1648 int ret = GPU_close();
1649 if (ret)
1650 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1651 }
1652
1653 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1654 if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
1655 int x = max(0, g_menuscreen_w - last_psx_w);
1656 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1657 int w = min(g_menuscreen_w, last_psx_w);
1658 int h = min(g_menuscreen_h, last_psx_h);
1659 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
1660 u16 *s = pl_fbdev_buf;
1661
1662 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
1663 menu_darken_bg(d, s, w, 0);
1664 }
1665
1666 if (ready_to_go)
1667 cpu_clock = get_cpu_clock();
1668
1669 plat_video_menu_enter(ready_to_go);
1670}
1671
1672void menu_prepare_emu(void)
1673{
1674 R3000Acpu *prev_cpu = psxCpu;
1675
1676 plat_video_menu_leave();
1677
1678 switch (scaling) {
1679 case SCALE_1_1:
1680 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
1681 break;
1682 case SCALE_4_3:
1683 g_layer_x = 80; g_layer_y = 0;
1684 g_layer_w = 640; g_layer_h = 480;
1685 break;
1686 case SCALE_FULLSCREEN:
1687 g_layer_x = 0; g_layer_y = 0;
1688 g_layer_w = 800; g_layer_h = 480;
1689 break;
1690 case SCALE_CUSTOM:
1691 break;
1692 }
1693 apply_filter(filter);
1694 apply_cpu_clock();
1695
1696 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1697 if (psxCpu != prev_cpu)
1698 // note that this does not really reset, just clears drc caches
1699 psxCpu->Reset();
1700
1701 // core doesn't care about Config.Cdda changes,
1702 // so handle them manually here
1703 if (Config.Cdda)
1704 CDR_stop();
1705
1706 menu_sync_config();
1707
1708 if (GPU_open != NULL) {
1709 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1710 if (ret)
1711 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1712 }
1713
1714 dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
1715}
1716
1717void me_update_msg(const char *msg)
1718{
1719 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1720 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1721
1722 menu_error_time = plat_get_ticks_ms();
1723 lprintf("msg: %s\n", menu_error_msg);
1724}
1725