2 * (C) GraÅžvydas "notaz" Ignotas, 2010-2011
4 * This work is licensed under the terms of any of these licenses
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.
21 #include "plugin_lib.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"
34 #define array_size(x) (sizeof(x) / sizeof(x[0]))
68 static int last_psx_w, last_psx_h, last_psx_bpp;
69 static int scaling, filter, cpu_clock, cpu_clock_st;
70 static char rom_fname_reload[MAXPATHLEN];
71 static char last_selected_fname[MAXPATHLEN];
72 static int warned_about_bios, region, in_type_sel;
75 // from softgpu plugin
76 extern int iUseDither;
77 extern int UseFrameSkip;
78 extern uint32_t dwActFixes;
79 extern float fFrameRateHz;
80 extern int dwFrameRateTicks;
83 extern int iUseReverb;
84 extern int iUseInterpolation;
86 extern int iSPUIRQWait;
89 static const char *bioses[24];
90 static const char *gpu_plugins[16];
91 static const char *spu_plugins[16];
92 static int bios_sel, gpu_plugsel, spu_plugsel;
95 static int min(int x, int y) { return x < y ? x : y; }
96 static int max(int x, int y) { return x > y ? x : y; }
98 void emu_make_path(char *buff, const char *end, int size)
102 end_len = strlen(end);
103 pos = plat_get_root_dir(buff, size);
104 strncpy(buff + pos, end, size - pos);
106 if (pos + end_len > size - 1)
107 printf("Warning: path truncated: %s\n", buff);
110 static int emu_check_save_file(int slot)
112 int ret = emu_check_state(slot);
113 return ret == 0 ? 1 : 0;
116 static int emu_save_load_game(int load, int unused)
121 ret = emu_load_state(state_slot);
123 // reflect hle/bios mode from savestate
126 else if (bios_sel == 0 && bioses[1] != NULL)
127 // XXX: maybe find the right bios instead
131 ret = emu_save_state(state_slot);
136 // propagate menu settings to the emu vars
137 static void menu_sync_config(void)
142 Config.PsxType = region - 1;
144 in_type = in_type_sel ? PSE_PAD_TYPE_ANALOGPAD : PSE_PAD_TYPE_STANDARD;
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));
152 static void menu_set_defconfig(void)
159 Config.Xa = Config.Cdda = Config.Sio =
160 Config.SpuIrq = Config.RCntFix = Config.VSyncWA = 0;
167 iUseInterpolation = 1;
175 #define CE_CONFIG_STR(val) \
176 { #val, 0, Config.val }
178 #define CE_CONFIG_VAL(val) \
179 { #val, sizeof(Config.val), &Config.val }
181 #define CE_STR(val) \
184 #define CE_INTVAL(val) \
185 { #val, sizeof(val), &val }
187 // 'versioned' var, used when defaults change
188 #define CE_INTVAL_V(val, ver) \
189 { #val #ver, sizeof(val), &val }
191 static const struct {
199 // CE_CONFIG_STR(Cdr),
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),
212 CE_INTVAL(g_layer_x),
213 CE_INTVAL(g_layer_y),
214 CE_INTVAL(g_layer_w),
215 CE_INTVAL(g_layer_h),
217 CE_INTVAL(state_slot),
218 CE_INTVAL(cpu_clock),
220 CE_INTVAL(in_type_sel),
221 CE_INTVAL(iUseDither),
222 CE_INTVAL(UseFrameSkip),
223 CE_INTVAL(dwActFixes),
224 CE_INTVAL(iUseReverb),
226 CE_INTVAL_V(iUseInterpolation, 2),
227 CE_INTVAL_V(iSPUIRQWait, 2),
228 CE_INTVAL(iUseTimer),
229 CE_INTVAL(warned_about_bios),
232 static char *get_cd_label(void)
234 static char trimlabel[33];
237 strncpy(trimlabel, CdromLabel, 32);
239 for (j = 31; j >= 0; j--)
240 if (trimlabel[j] == ' ')
246 static void make_cfg_fname(char *buf, size_t size, int is_game)
249 snprintf(buf, size, "." PCSX_DOT_DIR "cfg/%.32s-%.9s.cfg", get_cd_label(), CdromId);
251 snprintf(buf, size, "." PCSX_DOT_DIR "%s", cfgfile_basename);
254 static void keys_write_all(FILE *f);
256 static int menu_write_config(int is_game)
258 char cfgfile[MAXPATHLEN];
262 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
263 f = fopen(cfgfile, "w");
265 printf("menu_write_config: failed to open: %s\n", cfgfile);
269 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
270 fprintf(f, "%s = ", config_data[i].name);
271 switch (config_data[i].len) {
273 fprintf(f, "%s\n", (char *)config_data[i].val);
276 fprintf(f, "%x\n", *(u8 *)config_data[i].val);
279 fprintf(f, "%x\n", *(u16 *)config_data[i].val);
282 fprintf(f, "%x\n", *(u32 *)config_data[i].val);
285 printf("menu_write_config: unhandled len %d for %s\n",
286 config_data[i].len, config_data[i].name);
292 fprintf(f, "lastcdimg = %s\n", last_selected_fname);
300 static void parse_str_val(char *cval, const char *src)
303 strncpy(cval, src, MAXPATHLEN);
304 cval[MAXPATHLEN - 1] = 0;
305 tmp = strchr(cval, '\n');
307 tmp = strchr(cval, '\r');
312 static void keys_load_all(const char *cfg);
314 static int menu_load_config(int is_game)
316 char cfgfile[MAXPATHLEN];
322 make_cfg_fname(cfgfile, sizeof(cfgfile), is_game);
323 f = fopen(cfgfile, "r");
325 printf("menu_load_config: failed to open: %s\n", cfgfile);
329 fseek(f, 0, SEEK_END);
332 printf("bad size %ld: %s\n", size, cfgfile);
336 cfg = malloc(size + 1);
340 fseek(f, 0, SEEK_SET);
341 if (fread(cfg, 1, size, f) != size) {
342 printf("failed to read: %s\n", cfgfile);
347 for (i = 0; i < ARRAY_SIZE(config_data); i++) {
351 tmp = strstr(cfg, config_data[i].name);
354 tmp += strlen(config_data[i].name);
355 if (strncmp(tmp, " = ", 3) != 0)
359 if (config_data[i].len == 0) {
360 parse_str_val(config_data[i].val, tmp);
365 val = strtoul(tmp, &tmp2, 16);
366 if (tmp2 == NULL || tmp == tmp2)
367 continue; // parse failed
369 switch (config_data[i].len) {
371 *(u8 *)config_data[i].val = val;
374 *(u16 *)config_data[i].val = val;
377 *(u32 *)config_data[i].val = val;
380 printf("menu_load_config: unhandled len %d for %s\n",
381 config_data[i].len, config_data[i].name);
387 char *tmp = strstr(cfg, "lastcdimg = ");
390 parse_str_val(last_selected_fname, tmp);
397 for (i = bios_sel = 0; bioses[i] != NULL; i++)
398 if (strcmp(Config.Bios, bioses[i]) == 0)
399 { bios_sel = i; break; }
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; }
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; }
418 // rrrr rggg gggb bbbb
419 static unsigned short fname2color(const char *fname)
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, '.');
428 for (i = 0; i < array_size(cdimg_exts); i++)
429 if (strcasecmp(ext, cdimg_exts[i]) == 0)
431 for (i = 0; i < array_size(other_exts); i++)
432 if (strcasecmp(ext, other_exts[i]) == 0)
437 static void draw_savestate_bg(int slot);
439 #define MENU_ALIGN_LEFT
440 #define menu_init menu_init_common
441 #include "common/menu.c"
444 // a bit of black magic here
445 static void draw_savestate_bg(int slot)
447 static const int psx_widths[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
449 char fname[MAXPATHLEN];
456 ret = get_state_filename(fname, sizeof(fname), slot);
460 f = gzopen(fname, "rb");
464 if (gzseek(f, 0x29933d, SEEK_SET) != 0x29933d) {
465 fprintf(stderr, "gzseek failed\n");
470 gpu = malloc(sizeof(*gpu));
476 ret = gzread(f, gpu, sizeof(*gpu));
478 if (ret != sizeof(*gpu)) {
479 fprintf(stderr, "gzread failed\n");
483 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
485 if ((gpu->ulStatus & 0x800000) || (gpu->ulStatus & 0x200000))
486 goto out; // disabled || 24bpp (NYET)
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
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;
503 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
504 bgr555_to_rgb565(d, s, w * 2);
510 // ---------- pandora specific -----------
512 static const char pnd_script_base[] = "sudo -n /usr/pandora/scripts";
513 static char **pnd_filter_list;
515 static int get_cpu_clock(void)
519 f = fopen("/proc/pandora/cpu_mhz_max", "r");
521 fscanf(f, "%d", &ret);
527 static void apply_cpu_clock(void)
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);
538 static void apply_filter(int which)
544 if (pnd_filter_list == NULL || which == old)
547 for (i = 0; i < which; i++)
548 if (pnd_filter_list[i] == NULL)
551 if (pnd_filter_list[i] == NULL)
554 snprintf(buf, sizeof(buf), "%s/op_videofir.sh %s", pnd_script_base, pnd_filter_list[i]);
559 static menu_entry e_menu_gfx_options[];
561 static void pnd_menu_init(void)
569 cpu_clock_st = cpu_clock = get_cpu_clock();
571 dir = opendir("/etc/pandora/conf/dss_fir");
573 perror("filter opendir");
586 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
595 mfilters = calloc(count + 1, sizeof(mfilters[0]));
596 if (mfilters == NULL)
600 for (i = 0; (ent = readdir(dir)); ) {
603 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
606 len = strlen(ent->d_name);
608 // skip pre-HF5 extra files
609 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v3") == 0)
611 if (len >= 3 && strcmp(ent->d_name + len - 3, "_v5") == 0)
614 // have to cut "_up_h" for pre-HF5
615 if (len > 5 && strcmp(ent->d_name + len - 5, "_up_h") == 0)
618 if (len > sizeof(buff) - 1)
621 strncpy(buff, ent->d_name, len);
623 mfilters[i] = strdup(buff);
624 if (mfilters[i] != NULL)
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;
634 void menu_finish(void)
636 cpu_clock = cpu_clock_st;
640 // -------------- key config --------------
642 me_bind_action me_ctrl_actions[] =
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 },
663 me_bind_action emuctrl_actions[] =
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 { "Take Screenshot ", 1 << SACTION_SCREENSHOT },
671 { "Enter Menu ", 1 << SACTION_ENTER_MENU },
675 static char *mystrip(char *str)
680 for (i = 0; i < len; i++)
681 if (str[i] != ' ') break;
682 if (i > 0) memmove(str, str + i, len - i + 1);
685 for (i = len - 1; i >= 0; i--)
686 if (str[i] != ' ') break;
692 static void get_line(char *d, size_t size, const char *s)
697 for (pe = s; *pe != '\r' && *pe != '\n' && *pe != 0; pe++)
708 static void keys_write_all(FILE *f)
712 for (d = 0; d < IN_MAX_DEVS; d++)
714 const int *binds = in_get_dev_binds(d);
715 const char *name = in_get_dev_name(d, 0, 0);
718 if (binds == NULL || name == NULL)
721 fprintf(f, "binddev = %s\n", name);
722 in_get_config(d, IN_CFG_BIND_COUNT, &count);
724 for (k = 0; k < count; k++)
729 act[0] = act[31] = 0;
730 name = in_get_key_name(d, k);
732 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_PLAYER12)];
733 for (i = 0; kbinds && i < ARRAY_SIZE(me_ctrl_actions) - 1; i++) {
734 mask = me_ctrl_actions[i].mask;
736 strncpy(act, me_ctrl_actions[i].name, 31);
737 fprintf(f, "bind %s = player1 %s\n", name, mystrip(act));
740 mask = me_ctrl_actions[i].mask << 16;
742 strncpy(act, me_ctrl_actions[i].name, 31);
743 fprintf(f, "bind %s = player2 %s\n", name, mystrip(act));
748 kbinds = binds[IN_BIND_OFFS(k, IN_BINDTYPE_EMU)];
749 for (i = 0; kbinds && i < ARRAY_SIZE(emuctrl_actions) - 1; i++) {
750 mask = emuctrl_actions[i].mask;
752 strncpy(act, emuctrl_actions[i].name, 31);
753 fprintf(f, "bind %s = %s\n", name, mystrip(act));
761 static int parse_bind_val(const char *val, int *type)
765 *type = IN_BINDTYPE_NONE;
769 if (strncasecmp(val, "player", 6) == 0)
771 int player, shift = 0;
772 player = atoi(val + 6) - 1;
774 if ((unsigned int)player > 1)
779 *type = IN_BINDTYPE_PLAYER12;
780 for (i = 0; me_ctrl_actions[i].name != NULL; i++) {
781 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
782 return me_ctrl_actions[i].mask << shift;
785 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
786 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
787 *type = IN_BINDTYPE_EMU;
788 return emuctrl_actions[i].mask;
795 static void keys_load_all(const char *cfg)
797 char dev[256], key[128], *act;
803 while (p != NULL && (p = strstr(p, "binddev = ")) != NULL) {
806 get_line(dev, sizeof(dev), p);
807 dev_id = in_config_parse_dev(dev);
809 printf("input: can't handle dev: %s\n", dev);
813 in_unbind_all(dev_id, -1, -1);
814 while ((p = strstr(p, "bind"))) {
815 if (strncmp(p, "binddev = ", 10) == 0)
820 printf("input: parse error: %16s..\n", p);
824 get_line(key, sizeof(key), p);
825 act = strchr(key, '=');
827 printf("parse failed: %16s..\n", p);
835 bind = parse_bind_val(act, &bindtype);
836 if (bind != -1 && bind != 0) {
837 //printf("bind #%d '%s' %08x (%s)\n", dev_id, key, bind, act);
838 in_config_bind_key(dev_id, key, bind, bindtype);
841 lprintf("config: unhandled action \"%s\"\n", act);
846 static int key_config_loop_wrap(int id, int keys)
849 case MA_CTRL_PLAYER1:
850 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 0);
852 case MA_CTRL_PLAYER2:
853 key_config_loop(me_ctrl_actions, array_size(me_ctrl_actions) - 1, 1);
856 key_config_loop(emuctrl_actions, array_size(emuctrl_actions) - 1, -1);
864 static const char *mgn_dev_name(int id, int *offs)
866 const char *name = NULL;
869 if (id == MA_CTRL_DEV_FIRST)
872 for (; it < IN_MAX_DEVS; it++) {
873 name = in_get_dev_name(it, 1, 1);
882 static const char *mgn_saveloadcfg(int id, int *offs)
887 static int mh_savecfg(int id, int keys)
889 if (menu_write_config(id == MA_OPT_SAVECFG_GAME ? 1 : 0) == 0)
890 me_update_msg("config saved");
892 me_update_msg("failed to write config");
897 static const char *men_in_type_sel[] = { "Standard (SCPH-1080)", "Analog (SCPH-1150)", NULL };
899 static menu_entry e_menu_keyconfig[] =
901 mee_handler_id("Player 1", MA_CTRL_PLAYER1, key_config_loop_wrap),
902 mee_handler_id("Player 2", MA_CTRL_PLAYER2, key_config_loop_wrap),
903 mee_handler_id("Emulator controls", MA_CTRL_EMU, key_config_loop_wrap),
905 mee_enum ("Controller", 0, in_type_sel, men_in_type_sel),
906 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
907 mee_cust_nosave("Save cfg for loaded game", MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
909 mee_label ("Input devices:"),
910 mee_label_mk (MA_CTRL_DEV_FIRST, 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_label_mk (MA_CTRL_DEV_NEXT, mgn_dev_name),
920 static int menu_loop_keyconfig(int id, int keys)
924 // me_enable(e_menu_keyconfig, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
925 me_loop(e_menu_keyconfig, &sel, NULL);
929 // ------------ gfx options menu ------------
931 static const char *men_scaler[] = { "1x1", "scaled 4:3", "fullscreen", "custom", NULL };
932 static const char h_cscaler[] = "Displays the scaler layer, you can resize it\n"
933 "using d-pad or move it using R+d-pad";
934 static const char *men_dummy[] = { NULL };
936 static int menu_loop_cscaler(int id, int keys)
940 scaling = SCALE_CUSTOM;
942 omap_enable_layer(1);
947 memset(g_menuscreen_ptr, 4, g_menuscreen_w * g_menuscreen_h * 2);
948 text_out16(2, 2, "%d,%d", g_layer_x, g_layer_y);
949 text_out16(2, 480 - 18, "%dx%d | d-pad: resize, R+d-pad: move", g_layer_w, g_layer_h);
952 inp = in_menu_wait(PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT|PBTN_R|PBTN_MOK|PBTN_MBACK, 40);
953 if (inp & PBTN_UP) g_layer_y--;
954 if (inp & PBTN_DOWN) g_layer_y++;
955 if (inp & PBTN_LEFT) g_layer_x--;
956 if (inp & PBTN_RIGHT) g_layer_x++;
957 if (!(inp & PBTN_R)) {
958 if (inp & PBTN_UP) g_layer_h += 2;
959 if (inp & PBTN_DOWN) g_layer_h -= 2;
960 if (inp & PBTN_LEFT) g_layer_w += 2;
961 if (inp & PBTN_RIGHT) g_layer_w -= 2;
963 if (inp & (PBTN_MOK|PBTN_MBACK))
966 if (inp & (PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT)) {
967 if (g_layer_x < 0) g_layer_x = 0;
968 if (g_layer_x > 640) g_layer_x = 640;
969 if (g_layer_y < 0) g_layer_y = 0;
970 if (g_layer_y > 420) g_layer_y = 420;
971 if (g_layer_w < 160) g_layer_w = 160;
972 if (g_layer_h < 60) g_layer_h = 60;
973 if (g_layer_x + g_layer_w > 800)
974 g_layer_w = 800 - g_layer_x;
975 if (g_layer_y + g_layer_h > 480)
976 g_layer_h = 480 - g_layer_y;
977 omap_enable_layer(1);
981 omap_enable_layer(0);
986 static menu_entry e_menu_gfx_options[] =
988 mee_enum ("Scaler", 0, scaling, men_scaler),
989 mee_enum ("Filter", MA_OPT_FILTERING, filter, men_dummy),
990 // mee_onoff ("Vsync", 0, vsync, 1),
991 mee_cust_h ("Setup custom scaler", 0, menu_loop_cscaler, NULL, h_cscaler),
995 static int menu_loop_gfx_options(int id, int keys)
999 me_loop(e_menu_gfx_options, &sel, NULL);
1004 // ------------ bios/plugins ------------
1006 static const char *men_gpu_dithering[] = { "None", "Game dependant", "Always", NULL };
1007 static const char h_gpu_0[] = "Needed for Chrono Cross";
1008 static const char h_gpu_1[] = "Capcom fighting games";
1009 static const char h_gpu_2[] = "Black screens in Lunar";
1010 static const char h_gpu_3[] = "Compatibility mode";
1011 static const char h_gpu_6[] = "Pandemonium 2";
1012 static const char h_gpu_7[] = "Skip every second frame";
1013 static const char h_gpu_8[] = "Needed by Dark Forces";
1014 static const char h_gpu_9[] = "better g-colors, worse textures";
1015 static const char h_gpu_10[] = "Toggle busy flags after drawing";
1017 static menu_entry e_menu_plugin_gpu[] =
1019 mee_enum ("Dithering", 0, iUseDither, men_gpu_dithering),
1020 mee_onoff_h ("Odd/even bit hack", 0, dwActFixes, 1<<0, h_gpu_0),
1021 mee_onoff_h ("Expand screen width", 0, dwActFixes, 1<<1, h_gpu_1),
1022 mee_onoff_h ("Ignore brightness color", 0, dwActFixes, 1<<2, h_gpu_2),
1023 mee_onoff_h ("Disable coordinate check", 0, dwActFixes, 1<<3, h_gpu_3),
1024 mee_onoff_h ("Lazy screen update", 0, dwActFixes, 1<<6, h_gpu_6),
1025 mee_onoff_h ("Old frame skipping", 0, dwActFixes, 1<<7, h_gpu_7),
1026 mee_onoff_h ("Repeated flat tex triangles ",0,dwActFixes, 1<<8, h_gpu_8),
1027 mee_onoff_h ("Draw quads with triangles", 0, dwActFixes, 1<<9, h_gpu_9),
1028 mee_onoff_h ("Fake 'gpu busy' states", 0, dwActFixes, 1<<10, h_gpu_10),
1032 static int menu_loop_plugin_gpu(int id, int keys)
1035 me_loop(e_menu_plugin_gpu, &sel, NULL);
1039 static const char *men_spu_reverb[] = { "Off", "Fake", "On", NULL };
1040 static const char *men_spu_interp[] = { "None", "Simple", "Gaussian", "Cubic", NULL };
1041 static const char h_spu_irq_wait[] = "Wait for CPU (recommended set to ON)";
1042 static const char h_spu_thread[] = "Run sound emulation in main thread (recommended)";
1044 static menu_entry e_menu_plugin_spu[] =
1046 mee_enum ("Reverb", 0, iUseReverb, men_spu_reverb),
1047 mee_enum ("Interpolation", 0, iUseInterpolation, men_spu_interp),
1048 mee_onoff ("Adjust XA pitch", 0, iXAPitch, 1),
1049 mee_onoff_h ("SPU IRQ Wait", 0, iSPUIRQWait, 1, h_spu_irq_wait),
1050 mee_onoff_h ("Sound in main thread", 0, iUseTimer, 2, h_spu_thread),
1054 static int menu_loop_plugin_spu(int id, int keys)
1057 me_loop(e_menu_plugin_spu, &sel, NULL);
1061 static const char h_bios[] = "HLE is simulated BIOS. BIOS selection is saved in savestates\n"
1062 "and can't be changed there. Must save config and reload\n"
1063 "the game for change to take effect";
1064 static const char h_plugin_xpu[] = "Must save config and reload the game\n"
1065 "for plugin change to take effect";
1066 static const char h_gpu[] = "Configure built-in P.E.Op.S. SoftGL Driver V1.17";
1067 static const char h_spu[] = "Configure built-in P.E.Op.S. Sound Driver V1.7";
1069 static menu_entry e_menu_plugin_options[] =
1071 mee_enum_h ("BIOS", 0, bios_sel, bioses, h_bios),
1072 mee_enum_h ("GPU plugin", 0, gpu_plugsel, gpu_plugins, h_plugin_xpu),
1073 mee_enum_h ("SPU plugin", 0, spu_plugsel, spu_plugins, h_plugin_xpu),
1074 mee_handler_h ("Configure built-in GPU plugin", menu_loop_plugin_gpu, h_gpu),
1075 mee_handler_h ("Configure built-in SPU plugin", menu_loop_plugin_spu, h_spu),
1079 static menu_entry e_menu_main[];
1081 static int menu_loop_plugin_options(int id, int keys)
1084 me_loop(e_menu_plugin_options, &sel, NULL);
1086 // sync BIOS/plugins
1087 snprintf(Config.Bios, sizeof(Config.Bios), "%s", bioses[bios_sel]);
1088 snprintf(Config.Gpu, sizeof(Config.Gpu), "%s", gpu_plugins[gpu_plugsel]);
1089 snprintf(Config.Spu, sizeof(Config.Spu), "%s", spu_plugins[spu_plugsel]);
1090 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1095 // ------------ adv options menu ------------
1097 static const char h_cfg_cpul[] = "Shows CPU usage in %";
1098 static const char h_cfg_fl[] = "Frame Limiter keeps the game from running too fast";
1099 static const char h_cfg_xa[] = "Disables XA sound, which can sometimes improve performance";
1100 static const char h_cfg_cdda[] = "Disable CD Audio for a performance boost\n"
1101 "(proper .cue/.bin dump is needed otherwise)";
1102 static const char h_cfg_sio[] = "This should be enabled for certain memcards/gamepads";
1103 static const char h_cfg_spuirq[] = "Compatibility tweak; should probably be left off";
1104 static const char h_cfg_rcnt1[] = "Parasite Eve 2, Vandal Hearts 1/2 Fix";
1105 static const char h_cfg_rcnt2[] = "InuYasha Sengoku Battle Fix";
1106 static const char h_cfg_nodrc[] = "Disable dynamic recompiler and use interpreter\n"
1107 "Might be useful to overcome some dynarec bugs";
1109 static menu_entry e_menu_adv_options[] =
1111 mee_onoff_h ("Show CPU load", 0, g_opts, OPT_SHOWCPU, h_cfg_cpul),
1112 mee_onoff_h ("Disable Frame Limiter", 0, g_opts, OPT_NO_FRAMELIM, h_cfg_fl),
1113 mee_onoff_h ("Disable XA Decoding", 0, Config.Xa, 1, h_cfg_xa),
1114 mee_onoff_h ("Disable CD Audio", 0, Config.Cdda, 1, h_cfg_cdda),
1115 mee_onoff_h ("SIO IRQ Always Enabled", 0, Config.Sio, 1, h_cfg_sio),
1116 mee_onoff_h ("SPU IRQ Always Enabled", 0, Config.SpuIrq, 1, h_cfg_spuirq),
1117 mee_onoff_h ("Rootcounter hack", 0, Config.RCntFix, 1, h_cfg_rcnt1),
1118 mee_onoff_h ("Rootcounter hack 2", 0, Config.VSyncWA, 1, h_cfg_rcnt2),
1119 mee_onoff_h ("Disable dynarec (slow!)",0, Config.Cpu, 1, h_cfg_nodrc),
1123 static int menu_loop_adv_options(int id, int keys)
1126 me_loop(e_menu_adv_options, &sel, NULL);
1130 // ------------ options menu ------------
1132 static int mh_restore_defaults(int id, int keys)
1134 menu_set_defconfig();
1135 me_update_msg("defaults restored");
1139 static const char *men_region[] = { "Auto", "NTSC", "PAL", NULL };
1141 static const char *men_confirm_save[] = { "OFF", "writes", "loads", "both", NULL };
1142 static const char h_confirm_save[] = "Ask for confirmation when overwriting save,\n"
1143 "loading state or both";
1145 static const char h_restore_def[] = "Switches back to default / recommended\n"
1148 static menu_entry e_menu_options[] =
1150 // mee_range ("Save slot", 0, state_slot, 0, 9),
1151 // mee_enum_h ("Confirm savestate", 0, dummy, men_confirm_save, h_confirm_save),
1152 mee_onoff ("Frameskip", 0, UseFrameSkip, 1),
1153 mee_onoff ("Show FPS", 0, g_opts, OPT_SHOWFPS),
1154 mee_enum ("Region", 0, region, men_region),
1155 mee_range ("CPU clock", MA_OPT_CPU_CLOCKS, cpu_clock, 20, 5000),
1156 mee_handler ("[Display]", menu_loop_gfx_options),
1157 mee_handler ("[BIOS/Plugins]", menu_loop_plugin_options),
1158 mee_handler ("[Advanced]", menu_loop_adv_options),
1159 mee_cust_nosave("Save global config", MA_OPT_SAVECFG, mh_savecfg, mgn_saveloadcfg),
1160 mee_cust_nosave("Save cfg for loaded game",MA_OPT_SAVECFG_GAME, mh_savecfg, mgn_saveloadcfg),
1161 mee_handler_h ("Restore default config", mh_restore_defaults, h_restore_def),
1165 static int menu_loop_options(int id, int keys)
1170 i = me_id2offset(e_menu_options, MA_OPT_CPU_CLOCKS);
1171 e_menu_options[i].enabled = cpu_clock != 0 ? 1 : 0;
1172 me_enable(e_menu_options, MA_OPT_SAVECFG_GAME, ready_to_go && CdromId[0]);
1174 me_loop(e_menu_options, &sel, NULL);
1179 // ------------ debug menu ------------
1181 static void draw_frame_debug(GPUFreeze_t *gpuf)
1183 int w = min(g_menuscreen_w, 1024);
1184 int h = min(g_menuscreen_h, 512);
1185 u16 *d = g_menuscreen_ptr;
1186 u16 *s = (u16 *)gpuf->psxVRam;
1190 gpuf->ulFreezeVersion = 1;
1191 if (GPU_freeze != NULL)
1192 GPU_freeze(1, gpuf);
1194 for (; h > 0; h--, d += g_menuscreen_w, s += 1024)
1195 bgr555_to_rgb565(d, s, w * 2);
1197 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1198 snprintf(buff, sizeof(buff), "GPU sr: %08x", gpuf->ulStatus);
1199 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1200 snprintf(buff, sizeof(buff), "PC/SP: %08x %08x", psxRegs.pc, psxRegs.GPR.n.sp);
1201 smalltext_out16(4, (ty += me_sfont_h), buff, 0xe7fc);
1204 static void debug_menu_loop(void)
1209 gpuf = malloc(sizeof(*gpuf));
1216 draw_frame_debug(gpuf);
1219 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK|PBTN_MA2|PBTN_MA3|PBTN_L|PBTN_R |
1220 PBTN_UP|PBTN_DOWN|PBTN_LEFT|PBTN_RIGHT, 70);
1221 if (inp & PBTN_MBACK)
1228 // ------------ main menu ------------
1230 static void menu_bios_warn(void)
1233 static const char msg[] =
1234 "You don't seem to have copied any BIOS files to\n"
1235 "<SD card>/pandora/appdata/pcsx_rearmed/bios/\n\n"
1236 "While many games work fine with fake (HLE) BIOS,\n"
1237 "others (like MGS and FF8) require BIOS to work.\n"
1238 "After copying the file, you'll also need to\n"
1239 "select it in the emu's options->[BIOS/Plugins]\n\n"
1240 "The file is usually named SCPH1001.BIN, but\n"
1241 "other not compressed files can be used too.\n\n"
1242 "Press (B) or (X) to continue";
1247 draw_menu_message(msg, NULL);
1250 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1251 if (inp & (PBTN_MBACK|PBTN_MOK))
1256 // ------------ main menu ------------
1260 static void draw_frame_main(void)
1262 if (CdromId[0] != 0) {
1264 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1265 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1266 Config.HLE ? "HLE" : "BIOS");
1267 smalltext_out16(4, 1, buff, 0x105f);
1271 static void draw_frame_credits(void)
1273 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1276 static const char credits_text[] =
1278 "(C) 1999-2003 PCSX Team\n"
1279 "(C) 2005-2009 PCSX-df Team\n"
1280 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1281 "GPU and SPU code by Pete Bernert\n"
1282 " and the P.E.Op.S. team\n"
1283 "ARM recompiler (C) 2009-2011 Ari64\n"
1284 "PCSX4ALL plugins by PCSX4ALL team\n"
1285 " Chui, Franxis, Unai\n\n"
1286 "integration, optimization and\n"
1287 " frontend (C) 2010-2011 notaz\n";
1289 static int reset_game(void)
1292 if (bios_sel == 0 && !Config.HLE)
1298 if (CheckCdrom() != -1) {
1304 static int run_bios(void)
1310 pl_fbdev_buf = NULL;
1315 pcnt_hook_plugins();
1317 if (OpenPlugins() == -1) {
1318 me_update_msg("failed to open plugins");
1321 plugin_call_rearmed_cbs();
1324 CdromLabel[0] = '\0';
1332 static int run_cd_image(const char *fname)
1335 pl_fbdev_buf = NULL;
1338 set_cd_image(fname);
1340 pcnt_hook_plugins();
1342 if (OpenPlugins() == -1) {
1343 me_update_msg("failed to open plugins");
1346 plugin_call_rearmed_cbs();
1348 if (CheckCdrom() == -1) {
1349 // Only check the CD if we are starting the console with a CD
1351 me_update_msg("unsupported/invalid CD image");
1357 // Read main executable directly from CDRom and start it
1358 if (LoadCdrom() == -1) {
1360 me_update_msg("failed to load CD image");
1368 static int romsel_run(void)
1370 int prev_gpu, prev_spu;
1373 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1377 printf("selected file: %s\n", fname);
1379 new_dynarec_clear_full();
1381 if (run_cd_image(fname) != 0)
1384 prev_gpu = gpu_plugsel;
1385 prev_spu = spu_plugsel;
1386 if (menu_load_config(1) != 0)
1387 menu_load_config(0);
1389 // check for plugin changes, have to repeat
1390 // loading if game config changed plugins to reload them
1391 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1392 printf("plugin change detected, reloading plugins..\n");
1393 if (run_cd_image(fname) != 0)
1397 strcpy(last_selected_fname, rom_fname_reload);
1401 static int swap_cd_image(void)
1405 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1409 printf("selected file: %s\n", fname);
1412 CdromLabel[0] = '\0';
1414 set_cd_image(fname);
1415 if (ReloadCdromPlugin() < 0) {
1416 me_update_msg("failed to load cdr plugin");
1419 if (CDR_open() < 0) {
1420 me_update_msg("failed to open cdr plugin");
1424 SetCdOpenCaseTime(time(NULL) + 2);
1427 strcpy(last_selected_fname, rom_fname_reload);
1431 static int main_menu_handler(int id, int keys)
1435 case MA_MAIN_RESUME_GAME:
1439 case MA_MAIN_SAVE_STATE:
1441 return menu_loop_savestate(0);
1443 case MA_MAIN_LOAD_STATE:
1445 return menu_loop_savestate(1);
1447 case MA_MAIN_RESET_GAME:
1448 if (ready_to_go && reset_game() == 0)
1451 case MA_MAIN_LOAD_ROM:
1452 if (romsel_run() == 0)
1455 case MA_MAIN_SWAP_CD:
1456 if (swap_cd_image() == 0)
1459 case MA_MAIN_RUN_BIOS:
1460 if (run_bios() == 0)
1463 case MA_MAIN_CREDITS:
1464 draw_menu_message(credits_text, draw_frame_credits);
1465 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1471 lprintf("%s: something unknown selected\n", __FUNCTION__);
1478 static menu_entry e_menu_main[] =
1482 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1483 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1484 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1485 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1486 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1487 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1488 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1489 mee_handler ("Options", menu_loop_options),
1490 mee_handler ("Controls", menu_loop_keyconfig),
1491 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1492 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1496 // ----------------------------
1498 static void menu_leave_emu(void);
1500 void menu_loop(void)
1506 if (bioses[1] == NULL && !warned_about_bios) {
1508 warned_about_bios = 1;
1511 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1512 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1513 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
1514 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1515 me_enable(e_menu_main, MA_MAIN_SWAP_CD, ready_to_go);
1516 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1518 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1521 me_loop(e_menu_main, &sel, draw_frame_main);
1522 } while (!ready_to_go);
1524 /* wait until menu, ok, back is released */
1525 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1528 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1533 static void scan_bios_plugins(void)
1535 char fname[MAXPATHLEN];
1537 int bios_i, gpu_i, spu_i;
1542 gpu_plugins[0] = "builtin_gpu";
1543 spu_plugins[0] = "builtin_spu";
1544 bios_i = gpu_i = spu_i = 1;
1546 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1547 dir = opendir(fname);
1549 perror("scan_bios_plugins bios opendir");
1564 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1567 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1568 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1569 printf("bad BIOS file: %s\n", ent->d_name);
1573 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1574 bioses[bios_i++] = strdup(ent->d_name);
1578 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1584 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1585 dir = opendir(fname);
1587 perror("scan_bios_plugins opendir");
1601 p = strstr(ent->d_name, ".so");
1605 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1606 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1608 fprintf(stderr, "%s\n", dlerror());
1612 // now what do we have here?
1613 tmp = dlsym(h, "GPUinit");
1616 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1617 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1621 tmp = dlsym(h, "SPUinit");
1624 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1625 spu_plugins[spu_i++] = strdup(ent->d_name);
1629 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1636 void menu_init(void)
1638 char buff[MAXPATHLEN];
1640 strcpy(last_selected_fname, "/media");
1642 scan_bios_plugins();
1646 menu_set_defconfig();
1647 menu_load_config(0);
1652 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1653 if (g_menubg_src_ptr == NULL)
1655 emu_make_path(buff, "skin/background.png", sizeof(buff));
1656 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1659 void menu_notify_mode_change(int w, int h, int bpp)
1665 if (scaling == SCALE_1_1) {
1666 g_layer_x = 800/2 - w/2; g_layer_y = 480/2 - h/2;
1667 g_layer_w = w; g_layer_h = h;
1671 static void menu_leave_emu(void)
1673 if (GPU_close != NULL) {
1674 int ret = GPU_close();
1676 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1679 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1680 if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
1681 int x = max(0, g_menuscreen_w - last_psx_w);
1682 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1683 int w = min(g_menuscreen_w, last_psx_w);
1684 int h = min(g_menuscreen_h, last_psx_h);
1685 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
1686 u16 *s = pl_fbdev_buf;
1688 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
1689 menu_darken_bg(d, s, w, 0);
1693 cpu_clock = get_cpu_clock();
1695 plat_video_menu_enter(ready_to_go);
1698 void menu_prepare_emu(void)
1700 R3000Acpu *prev_cpu = psxCpu;
1702 plat_video_menu_leave();
1706 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
1709 g_layer_x = 80; g_layer_y = 0;
1710 g_layer_w = 640; g_layer_h = 480;
1712 case SCALE_FULLSCREEN:
1713 g_layer_x = 0; g_layer_y = 0;
1714 g_layer_w = 800; g_layer_h = 480;
1719 apply_filter(filter);
1722 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1723 if (psxCpu != prev_cpu)
1724 // note that this does not really reset, just clears drc caches
1727 // core doesn't care about Config.Cdda changes,
1728 // so handle them manually here
1734 if (GPU_open != NULL) {
1735 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1737 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1740 dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
1743 void me_update_msg(const char *msg)
1745 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1746 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1748 menu_error_time = plat_get_ticks_ms();
1749 lprintf("msg: %s\n", menu_error_msg);