boolean set_option(const char *name, union nh_optvalue value, struct newgame_options *ngo) { struct nh_option_desc *option = NULL, *options = new_opt_struct(); boolean ret = FALSE; /* can't change options for other players */ if (program_state.followmode != FM_PLAY) goto free; if (options) option = nhlib_find_option(options, name); if (!option || (option->birth_option && program_state.game_running)) goto free; if (!nhlib_option_value_ok(option, value)) goto free; nhlib_copy_option_value(option, value); if (option->type == OPTTYPE_BOOL) { boolean *bvar = nhlib_find_boolopt(boolopt_map, option->name); if (!bvar) { impossible("no boolean for option '%s'", option->name); goto free; } *bvar = option->value.b; ret = TRUE; goto free; } else if (!strcmp("disclose", option->name)) { flags.end_disclose = option->value.e; } else if (!strcmp("fruit", option->name)) { strncpy(gamestate.fruits.curname, option->value.s, PL_FSIZ-1); gamestate.fruits.curname[PL_FSIZ - 1] = '\0'; if (objects) /* don't do fruitadd before the game is running */ fruitadd(gamestate.fruits.curname); } else if (!strcmp("menustyle", option->name)) { flags.menu_style = option->value.e; } else if (!strcmp("movecommand", option->name)) { flags.interaction_mode = option->value.e; } else if (!strcmp("packorder", option->name)) { if (!change_inv_order(option->value.s)) goto free; } else if (!strcmp("pickup_burden", option->name)) { flags.pickup_burden = option->value.e; } else if (!strcmp("autopickup_rules", option->name)) { if (flags.ap_rules) { free(flags.ap_rules->rules); free(flags.ap_rules); flags.ap_rules = NULL; } flags.ap_rules = nhlib_copy_autopickup_rules(option->value.ar); } /* birth options */ else if (!strcmp("mode", option->name)) { flags.debug = (option->value.e == MODE_WIZARD); flags.explore = (option->value.e == MODE_EXPLORE); flags.challenge = (option->value.e == MODE_CHALLENGE); } else if (!strcmp("align", option->name)) { u.initalign = option->value.e; } else if (!strcmp("gender", option->name)) { u.initgend = option->value.e; } else if (!strcmp("race", option->name)) { u.initrace = option->value.e; } else if (!strcmp("role", option->name)) { u.initrole = option->value.e; } else if (!strcmp("name", option->name)) { strncpy(u.uplname, option->value.s, PL_NSIZ-1); (u.uplname)[PL_NSIZ - 1] = '\0'; } else if (!strcmp("seed", option->name)) { /* note: does not NUL-terminate a max-length string, this is intentional */ strncpy(flags.setseed, option->value.s, RNG_SEED_SIZE_BASE64); } else if (!strcmp("catname", option->name)) { if (!ngo) panic("catname set outside newgame sequence"); strncpy(ngo->catname, option->value.s, PL_PSIZ-1); ngo->catname[PL_PSIZ - 1] = '\0'; } else if (!strcmp("dogname", option->name)) { if (!ngo) panic("dogname set outside newgame sequence"); strncpy(ngo->dogname, option->value.s, PL_PSIZ-1); ngo->dogname[PL_PSIZ - 1] = '\0'; } else if (!strcmp("horsename", option->name)) { if (!ngo) panic("horsename set outside newgame sequence"); strncpy(ngo->horsename, option->value.s, PL_PSIZ-1); ngo->horsename[PL_PSIZ - 1] = '\0'; } else if (!strcmp("pettype", option->name)) { if (!ngo) panic("preferred_pet set outside newgame sequence"); ngo->preferred_pet = (char)option->value.e; } else if (!strcmp("timezone", option->name)) { flags.timezone = option->value.e; } else if (!strcmp("polyinit", option->name)) { flags.polyinit_mnum = option->value.e; } else /* unrecognized option */ goto free; /* assume that any recognized option has been handled. */ ret = TRUE; free: nhlib_free_optlist(options); return ret; }
struct nh_option_desc * nh_get_options(void) { struct nh_option_desc *options = new_opt_struct(); /* Outside of a game, don't attempt to recover options from state; all options should have default values. */ if (!program_state.game_running) return options; struct nh_option_desc *option; for (option = options; option->name; ++option) { if (option->type == OPTTYPE_BOOL) { boolean *bvar = nhlib_find_boolopt(boolopt_map, option->name); if (!bvar) { impossible("no boolean for option '%s'", option->name); continue; } option->value.b = *bvar; } else if (!strcmp("disclose", option->name)) { option->value.e = flags.end_disclose; } else if (!strcmp("fruit", option->name)) { if (option->value.s) free(option->value.s); option->value.s = malloc(PL_FSIZ); strncpy(option->value.s, gamestate.fruits.curname, PL_FSIZ-1); option->value.s[PL_FSIZ - 1] = '\0'; } else if (!strcmp("menustyle", option->name)) { option->value.e = flags.menu_style; } else if (!strcmp("movecommand", option->name)) { option->value.e = flags.interaction_mode; } else if (!strcmp("packorder", option->name)) { int i; if (option->value.s) free(option->value.s); option->value.s = malloc(MAXOCLASSES + 1); for (i = 0; i < MAXOCLASSES; ++i) option->value.s[i] = def_oc_syms[(int)flags.inv_order[i]]; option->value.s[MAXOCLASSES] = '\0'; } else if (!strcmp("pickup_burden", option->name)) { option->value.e = flags.pickup_burden; } else if (!strcmp("autopickup_rules", option->name)) { if (option->value.ar) { free(option->value.ar->rules); free(option->value.ar); } option->value.ar = nhlib_copy_autopickup_rules(flags.ap_rules); } else if (!strcmp("mode", option->name)) { option->value.e = flags.debug ? MODE_WIZARD : flags.explore ? MODE_EXPLORE : flags.challenge ? MODE_CHALLENGE : MODE_NORMAL; } else if (!strcmp("timezone", option->name)) { option->value.e = flags.timezone; } else if (!strcmp("polyinit", option->name)) { option->value.e = flags.polyinit_mnum; } else if (!strcmp("align", option->name)) { option->value.e = u.initalign; } else if (!strcmp("gender", option->name)) { option->value.e = u.initgend; } else if (!strcmp("race", option->name)) { option->value.e = u.initrace; } else if (!strcmp("role", option->name)) { option->value.e = u.initrole; } else if (!strcmp("name", option->name)) { if (option->value.s) free(option->value.s); option->value.s = malloc(PL_NSIZ); strncpy(option->value.s, u.uplname, PL_NSIZ-1); option->value.s[PL_NSIZ - 1] = '\0'; } else if (!strcmp("seed", option->name)) { if (option->value.s) free(option->value.s); option->value.s = malloc(RNG_SEED_SIZE_BASE64 + 1); strncpy(option->value.s, flags.setseed, RNG_SEED_SIZE_BASE64); option->value.s[RNG_SEED_SIZE_BASE64] = '\0'; } else if (!strcmp("catname", option->name) || !strcmp("dogname", option->name) || !strcmp("horsename", option->name)) { /* The client shouldn't be doing this, but we can't crash in response because that would be exploitable. Send it a null string instead. */ if (option->value.s) free(option->value.s); option->value.s = malloc(1); option->value.s[0] = '\0'; } else if (!strcmp("pettype", option->name)) { /* As the previous case, just we want an enum not a string. */ option->value.e = 0; } else { impossible("unknown option '%s'", option->name); memset(&option->value, 0, sizeof option->value); } } return options; }
nh_bool curses_set_option(const char *name, union nh_optvalue value) { nh_bool game_option = FALSE; struct nh_option_desc *option = nhlib_find_option(curses_options, name); if (!option) { if (game_is_running) return nh_set_option(name, value); /* If the game is not running, update our local copy of options. */ if (!nh_options || !(option = nhlib_find_option(nh_options, name))) { return FALSE; } game_option = TRUE; } if ((int)option->type == OPTTYPE_KEYMAP) { return FALSE; } if (!nhlib_option_value_ok(option, value)) return FALSE; nhlib_copy_option_value(option, value); if (game_option) return TRUE; /* In case the option affects graphics; this is pretty cheap if we don't do it every turn */ mark_mapwin_for_full_refresh(); if (option->type == OPTTYPE_BOOL) { nh_bool *var = nhlib_find_boolopt(boolopt_map, option->name); if (!var) { curses_impossible("missing boolean option"); return FALSE; } *var = value.b; if (!strcmp(option->name, "status3")) { rebuild_ui(); } else if (!strcmp(option->name, "darkgray")) { set_darkgray(); draw_map(player.x, player.y); } else if (!strcmp(option->name, "mouse")) { uncursed_enable_mouse(option->value.b); } } else if (!strcmp(option->name, "comment")) { /* do nothing */ } else if (!strcmp(option->name, "tileset")) { if (settings.tileset) free(settings.tileset); settings.tileset = malloc(strlen(option->value.s) + 1); strcpy(settings.tileset, option->value.s); rebuild_ui(); } else if (!strcmp(option->name, "border")) { settings.whichframes = option->value.e; rebuild_ui(); } else if (!strcmp(option->name, "menu_headings")) { settings.menu_headings = option->value.e; } else if (!strcmp(option->name, "palette")) { settings.palette = option->value.e; setup_palette(); if (ui_flags.initialized) { /* * - We don't want to install a palette as a result of the default * setting of "palette", because some terminals cannot handle a * palette reset, and thus we need to ensure that we've loaded * the user's palette setting before palette initialization. * * - Besides, clear() will crash with an uninitialized libuncursed. * So we have to delay this anyway. */ clear(); refresh(); rebuild_ui(); } } else if (!strcmp(option->name, "animation")) { settings.animation = option->value.e; } else if (!strcmp(option->name, "sidebar")) { settings.sidebar = option->value.e; rebuild_ui(); } else if (!strcmp(option->name, "scores_top")) { settings.end_top = option->value.i; } else if (!strcmp(option->name, "scores_around")) { settings.end_around = option->value.i; } else if (!strcmp(option->name, "networkmotd")) { settings.show_motd = option->value.e; } else if (!strcmp(option->name, "menupaging")) { settings.menupaging = option->value.e; } else if (!strcmp(option->name, "optstyle")) { settings.optstyle = option->value.e; } else if (!strcmp(option->name, "msgheight")) { settings.msgheight = option->value.i; rebuild_ui(); } else if (!strcmp(option->name, "msghistory")) { settings.msghistory = option->value.i; alloc_hist_array(); } else return FALSE; return TRUE; }