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";
1246 draw_menu_message(msg, NULL);
1248 inp = in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1249 if (inp & (PBTN_MBACK|PBTN_MOK))
1254 // ------------ main menu ------------
1258 static void draw_frame_main(void)
1260 if (CdromId[0] != 0) {
1262 snprintf(buff, sizeof(buff), "%.32s/%.9s (running as %s, with %s)",
1263 get_cd_label(), CdromId, Config.PsxType ? "PAL" : "NTSC",
1264 Config.HLE ? "HLE" : "BIOS");
1265 smalltext_out16(4, 1, buff, 0x105f);
1269 static void draw_frame_credits(void)
1271 smalltext_out16(4, 1, "build: "__DATE__ " " __TIME__ " " REV, 0xe7fc);
1274 static const char credits_text[] =
1276 "(C) 1999-2003 PCSX Team\n"
1277 "(C) 2005-2009 PCSX-df Team\n"
1278 "(C) 2009-2011 PCSX-Reloaded Team\n\n"
1279 "GPU and SPU code by Pete Bernert\n"
1280 " and the P.E.Op.S. team\n"
1281 "ARM recompiler (C) 2009-2011 Ari64\n"
1282 "PCSX4ALL plugins by PCSX4ALL team\n"
1283 " Chui, Franxis, Unai\n\n"
1284 "integration, optimization and\n"
1285 " frontend (C) 2010-2011 notaz\n";
1287 static int reset_game(void)
1290 if (bios_sel == 0 && !Config.HLE)
1296 if (CheckCdrom() != -1) {
1302 static int run_bios(void)
1308 pl_fbdev_buf = NULL;
1313 pcnt_hook_plugins();
1315 if (OpenPlugins() == -1) {
1316 me_update_msg("failed to open plugins");
1319 plugin_call_rearmed_cbs();
1322 CdromLabel[0] = '\0';
1330 static int run_cd_image(const char *fname)
1333 pl_fbdev_buf = NULL;
1336 set_cd_image(fname);
1338 pcnt_hook_plugins();
1340 if (OpenPlugins() == -1) {
1341 me_update_msg("failed to open plugins");
1344 plugin_call_rearmed_cbs();
1346 if (CheckCdrom() == -1) {
1347 // Only check the CD if we are starting the console with a CD
1349 me_update_msg("unsupported/invalid CD image");
1355 // Read main executable directly from CDRom and start it
1356 if (LoadCdrom() == -1) {
1358 me_update_msg("failed to load CD image");
1366 static int romsel_run(void)
1368 int prev_gpu, prev_spu;
1371 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1375 printf("selected file: %s\n", fname);
1377 new_dynarec_clear_full();
1379 if (run_cd_image(fname) != 0)
1382 prev_gpu = gpu_plugsel;
1383 prev_spu = spu_plugsel;
1384 if (menu_load_config(1) != 0)
1385 menu_load_config(0);
1387 // check for plugin changes, have to repeat
1388 // loading if game config changed plugins to reload them
1389 if (prev_gpu != gpu_plugsel || prev_spu != spu_plugsel) {
1390 printf("plugin change detected, reloading plugins..\n");
1391 if (run_cd_image(fname) != 0)
1395 strcpy(last_selected_fname, rom_fname_reload);
1399 static int swap_cd_image(void)
1403 fname = menu_loop_romsel(last_selected_fname, sizeof(last_selected_fname));
1407 printf("selected file: %s\n", fname);
1410 CdromLabel[0] = '\0';
1412 set_cd_image(fname);
1413 if (ReloadCdromPlugin() < 0) {
1414 me_update_msg("failed to load cdr plugin");
1417 if (CDR_open() < 0) {
1418 me_update_msg("failed to open cdr plugin");
1422 SetCdOpenCaseTime(time(NULL) + 2);
1425 strcpy(last_selected_fname, rom_fname_reload);
1429 static int main_menu_handler(int id, int keys)
1433 case MA_MAIN_RESUME_GAME:
1437 case MA_MAIN_SAVE_STATE:
1439 return menu_loop_savestate(0);
1441 case MA_MAIN_LOAD_STATE:
1443 return menu_loop_savestate(1);
1445 case MA_MAIN_RESET_GAME:
1446 if (ready_to_go && reset_game() == 0)
1449 case MA_MAIN_LOAD_ROM:
1450 if (romsel_run() == 0)
1453 case MA_MAIN_SWAP_CD:
1454 if (swap_cd_image() == 0)
1457 case MA_MAIN_RUN_BIOS:
1458 if (run_bios() == 0)
1461 case MA_MAIN_CREDITS:
1462 draw_menu_message(credits_text, draw_frame_credits);
1463 in_menu_wait(PBTN_MOK|PBTN_MBACK, 70);
1469 lprintf("%s: something unknown selected\n", __FUNCTION__);
1476 static menu_entry e_menu_main[] =
1480 mee_handler_id("Resume game", MA_MAIN_RESUME_GAME, main_menu_handler),
1481 mee_handler_id("Save State", MA_MAIN_SAVE_STATE, main_menu_handler),
1482 mee_handler_id("Load State", MA_MAIN_LOAD_STATE, main_menu_handler),
1483 mee_handler_id("Reset game", MA_MAIN_RESET_GAME, main_menu_handler),
1484 mee_handler_id("Load CD image", MA_MAIN_LOAD_ROM, main_menu_handler),
1485 mee_handler_id("Change CD image", MA_MAIN_SWAP_CD, main_menu_handler),
1486 mee_handler_id("Run BIOS", MA_MAIN_RUN_BIOS, main_menu_handler),
1487 mee_handler ("Options", menu_loop_options),
1488 mee_handler ("Controls", menu_loop_keyconfig),
1489 mee_handler_id("Credits", MA_MAIN_CREDITS, main_menu_handler),
1490 mee_handler_id("Exit", MA_MAIN_EXIT, main_menu_handler),
1494 // ----------------------------
1496 static void menu_leave_emu(void);
1498 void menu_loop(void)
1504 if (bioses[1] == NULL && !warned_about_bios) {
1506 warned_about_bios = 1;
1509 me_enable(e_menu_main, MA_MAIN_RESUME_GAME, ready_to_go);
1510 me_enable(e_menu_main, MA_MAIN_SAVE_STATE, ready_to_go && CdromId[0]);
1511 me_enable(e_menu_main, MA_MAIN_LOAD_STATE, ready_to_go && CdromId[0]);
1512 me_enable(e_menu_main, MA_MAIN_RESET_GAME, ready_to_go);
1513 me_enable(e_menu_main, MA_MAIN_SWAP_CD, ready_to_go);
1514 me_enable(e_menu_main, MA_MAIN_RUN_BIOS, bios_sel != 0);
1516 in_set_config_int(0, IN_CFG_BLOCKING, 1);
1519 me_loop(e_menu_main, &sel, draw_frame_main);
1520 } while (!ready_to_go);
1522 /* wait until menu, ok, back is released */
1523 while (in_menu_wait_any(50) & (PBTN_MENU|PBTN_MOK|PBTN_MBACK))
1526 in_set_config_int(0, IN_CFG_BLOCKING, 0);
1531 static void scan_bios_plugins(void)
1533 char fname[MAXPATHLEN];
1535 int bios_i, gpu_i, spu_i;
1540 gpu_plugins[0] = "builtin_gpu";
1541 spu_plugins[0] = "builtin_spu";
1542 bios_i = gpu_i = spu_i = 1;
1544 snprintf(fname, sizeof(fname), "%s/", Config.BiosDir);
1545 dir = opendir(fname);
1547 perror("scan_bios_plugins bios opendir");
1562 if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1565 snprintf(fname, sizeof(fname), "%s/%s", Config.BiosDir, ent->d_name);
1566 if (stat(fname, &st) != 0 || st.st_size != 512*1024) {
1567 printf("bad BIOS file: %s\n", ent->d_name);
1571 if (bios_i < ARRAY_SIZE(bioses) - 1) {
1572 bioses[bios_i++] = strdup(ent->d_name);
1576 printf("too many BIOSes, dropping \"%s\"\n", ent->d_name);
1582 snprintf(fname, sizeof(fname), "%s/", Config.PluginsDir);
1583 dir = opendir(fname);
1585 perror("scan_bios_plugins opendir");
1599 p = strstr(ent->d_name, ".so");
1603 snprintf(fname, sizeof(fname), "%s/%s", Config.PluginsDir, ent->d_name);
1604 h = dlopen(fname, RTLD_LAZY | RTLD_LOCAL);
1606 fprintf(stderr, "%s\n", dlerror());
1610 // now what do we have here?
1611 tmp = dlsym(h, "GPUinit");
1614 if (gpu_i < ARRAY_SIZE(gpu_plugins) - 1)
1615 gpu_plugins[gpu_i++] = strdup(ent->d_name);
1619 tmp = dlsym(h, "SPUinit");
1622 if (spu_i < ARRAY_SIZE(spu_plugins) - 1)
1623 spu_plugins[spu_i++] = strdup(ent->d_name);
1627 fprintf(stderr, "ignoring unidentified plugin: %s\n", fname);
1634 void menu_init(void)
1636 char buff[MAXPATHLEN];
1638 strcpy(last_selected_fname, "/media");
1640 scan_bios_plugins();
1644 menu_set_defconfig();
1645 menu_load_config(0);
1650 g_menubg_src_ptr = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);
1651 if (g_menubg_src_ptr == NULL)
1653 emu_make_path(buff, "skin/background.png", sizeof(buff));
1654 readpng(g_menubg_src_ptr, buff, READPNG_BG, g_menuscreen_w, g_menuscreen_h);
1657 void menu_notify_mode_change(int w, int h, int bpp)
1663 if (scaling == SCALE_1_1) {
1664 g_layer_x = 800/2 - w/2; g_layer_y = 480/2 - h/2;
1665 g_layer_w = w; g_layer_h = h;
1669 static void menu_leave_emu(void)
1671 if (GPU_close != NULL) {
1672 int ret = GPU_close();
1674 fprintf(stderr, "Warning: GPU_close returned %d\n", ret);
1677 memcpy(g_menubg_ptr, g_menubg_src_ptr, g_menuscreen_w * g_menuscreen_h * 2);
1678 if (pl_fbdev_buf != NULL && ready_to_go && last_psx_bpp == 16) {
1679 int x = max(0, g_menuscreen_w - last_psx_w);
1680 int y = max(0, g_menuscreen_h / 2 - last_psx_h / 2);
1681 int w = min(g_menuscreen_w, last_psx_w);
1682 int h = min(g_menuscreen_h, last_psx_h);
1683 u16 *d = (u16 *)g_menubg_ptr + g_menuscreen_w * y + x;
1684 u16 *s = pl_fbdev_buf;
1686 for (; h > 0; h--, d += g_menuscreen_w, s += last_psx_w)
1687 menu_darken_bg(d, s, w, 0);
1691 cpu_clock = get_cpu_clock();
1693 plat_video_menu_enter(ready_to_go);
1696 void menu_prepare_emu(void)
1698 R3000Acpu *prev_cpu = psxCpu;
1700 plat_video_menu_leave();
1704 menu_notify_mode_change(last_psx_w, last_psx_h, last_psx_bpp);
1707 g_layer_x = 80; g_layer_y = 0;
1708 g_layer_w = 640; g_layer_h = 480;
1710 case SCALE_FULLSCREEN:
1711 g_layer_x = 0; g_layer_y = 0;
1712 g_layer_w = 800; g_layer_h = 480;
1717 apply_filter(filter);
1720 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1721 if (psxCpu != prev_cpu)
1722 // note that this does not really reset, just clears drc caches
1725 // core doesn't care about Config.Cdda changes,
1726 // so handle them manually here
1732 if (GPU_open != NULL) {
1733 int ret = GPU_open(&gpuDisp, "PCSX", NULL);
1735 fprintf(stderr, "Warning: GPU_open returned %d\n", ret);
1738 dfinput_activate(in_type == PSE_PAD_TYPE_ANALOGPAD);
1741 void me_update_msg(const char *msg)
1743 strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
1744 menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
1746 menu_error_time = plat_get_ticks_ms();
1747 lprintf("msg: %s\n", menu_error_msg);