1 /**
2  * \file ui-prefs.c
3  * \brief Pref file handling code
4  *
5  * Copyright (c) 2003 Takeshi Mogami, Robert Ruehlmann
6  * Copyright (c) 2007 Pete Mack
7  * Copyright (c) 2010 Andi Sidwell
8  *
9  * This work is free software; you can redistribute it and/or modify it
10  * under the terms of either:
11  *
12  * a) the GNU General Public License as published by the Free Software
13  *    Foundation, version 2, or
14  *
15  * b) the "Angband licence":
16  *    This software may be copied and distributed for educational, research,
17  *    and not for profit purposes provided that this copyright and statement
18  *    are included in all such copies.  Other copyrights may also apply.
19  */
20 #include "angband.h"
21 #include "cave.h"
22 #include "game-input.h"
23 #include "grafmode.h"
24 #include "init.h"
25 #include "mon-util.h"
26 #include "monster.h"
27 #include "obj-ignore.h"
28 #include "obj-tval.h"
29 #include "obj-util.h"
30 #include "object.h"
31 #include "project.h"
32 #include "player.h"
33 #include "trap.h"
34 #include "ui-display.h"
35 #include "ui-entry-renderers.h"
36 #include "ui-keymap.h"
37 #include "ui-prefs.h"
38 #include "ui-term.h"
39 #include "sound.h"
40 
41 char arg_name[PLAYER_NAME_LEN];		/* Command arg -- request character name */
42 int arg_graphics;			/* Command arg -- Request graphics mode */
43 bool arg_graphics_nice;		/* Command arg -- Request nice graphics mode */
44 int use_graphics;			/* The "graphics" mode is enabled */
45 
46 byte *monster_x_attr;
47 wchar_t *monster_x_char;
48 byte *kind_x_attr;
49 wchar_t *kind_x_char;
50 byte *feat_x_attr[LIGHTING_MAX];
51 wchar_t *feat_x_char[LIGHTING_MAX];
52 byte *trap_x_attr[LIGHTING_MAX];
53 wchar_t *trap_x_char[LIGHTING_MAX];
54 byte *flavor_x_attr;
55 wchar_t *flavor_x_char;
56 static size_t flavor_max = 0;
57 
58 /**
59  * ------------------------------------------------------------------------
60  * Pref file saving code
61  * ------------------------------------------------------------------------ */
62 
63 /**
64  * Header and footer marker string for pref file dumps
65  */
66 static const char *dump_separator = "#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#";
67 
68 
69 /**
70  * Remove old lines from pref files
71  *
72  * If you are using setgid, make sure privileges were raised prior
73  * to calling this.
74  */
remove_old_dump(const char * cur_fname,const char * mark)75 static void remove_old_dump(const char *cur_fname, const char *mark)
76 {
77 	bool between_marks = false;
78 	bool changed = false;
79 	bool skip_one = false;
80 
81 	char buf[1024];
82 
83 	char start_line[1024];
84 	char end_line[1024];
85 
86 	char new_fname[1024];
87 
88 	ang_file *new_file;
89 	ang_file *cur_file;
90 
91 	/* Format up some filenames */
92 	strnfmt(new_fname, sizeof(new_fname), "%s.new", cur_fname);
93 
94 	/* Work out what we expect to find */
95 	strnfmt(start_line, sizeof(start_line), "%s begin %s",
96 			dump_separator, mark);
97 	strnfmt(end_line,   sizeof(end_line),   "%s end %s",
98 			dump_separator, mark);
99 
100 
101 	/* Open current file */
102 	cur_file = file_open(cur_fname, MODE_READ, FTYPE_TEXT);
103 	if (!cur_file) return;
104 
105 	/* Open new file */
106 	new_file = file_open(new_fname, MODE_WRITE, FTYPE_TEXT);
107 	if (!new_file) {
108 		file_close(cur_file);
109 		msg("Failed to create file %s", new_fname);
110 		return;
111 	}
112 
113 	/* Loop for every line */
114 	while (file_getl(cur_file, buf, sizeof(buf))) {
115 		/* Turn on at the start line, turn off at the finish line */
116 		if (!strcmp(buf, start_line))
117 			between_marks = true;
118 		else if (!strcmp(buf, end_line)) {
119 			between_marks = false;
120 			skip_one = true;
121 			changed = true;
122 		}
123 
124 		if (!between_marks && !skip_one)
125 			file_putf(new_file, "%s\n", buf);
126 
127 		skip_one = false;
128 	}
129 
130 	/* Close files */
131 	file_close(cur_file);
132 	file_close(new_file);
133 
134 	/* If there are changes use the new file. otherwise just destroy it */
135 	if (changed) {
136 		char old_fname[1024];
137 		strnfmt(old_fname, sizeof(old_fname), "%s.old", cur_fname);
138 
139 		if (file_move(cur_fname, old_fname)) {
140 			file_move(new_fname, cur_fname);
141 			file_delete(old_fname);
142 		}
143 	} else {
144 		file_delete(new_fname);
145 	}
146 }
147 
148 
149 /**
150  * Output the header of a pref-file dump
151  */
pref_header(ang_file * fff,const char * mark)152 static void pref_header(ang_file *fff, const char *mark)
153 {
154 	/* Start of dump */
155 	file_putf(fff, "%s begin %s\n", dump_separator, mark);
156 
157 	file_putf(fff, "# *Warning!*  The lines below are an automatic dump.\n");
158 	file_putf(fff, "# Don't edit them; changes will be deleted and replaced automatically.\n");
159 }
160 
161 /**
162  * Output the footer of a pref-file dump
163  */
pref_footer(ang_file * fff,const char * mark)164 static void pref_footer(ang_file *fff, const char *mark)
165 {
166 	file_putf(fff, "# *Warning!*  The lines above are an automatic dump.\n");
167 	file_putf(fff, "# Don't edit them; changes will be deleted and replaced automatically.\n");
168 
169 	/* End of dump */
170 	file_putf(fff, "%s end %s\n", dump_separator, mark);
171 }
172 
173 
174 /**
175  * Dump monsters
176  */
dump_monsters(ang_file * fff)177 void dump_monsters(ang_file *fff)
178 {
179 	int i;
180 
181 	for (i = 0; i < z_info->r_max; i++) {
182 		struct monster_race *race = &r_info[i];
183 		byte attr = monster_x_attr[i];
184 		wint_t chr = monster_x_char[i];
185 
186 		/* Skip non-entries */
187 		if (!race->name) continue;
188 
189 		file_putf(fff, "monster:%s:0x%02X:0x%02X\n", race->name, attr, chr);
190 	}
191 }
192 
193 /**
194  * Dump objects
195  */
dump_objects(ang_file * fff)196 void dump_objects(ang_file *fff)
197 {
198 	int i;
199 
200 	file_putf(fff, "# Objects\n");
201 
202 	for (i = 0; i < z_info->k_max; i++) {
203 		struct object_kind *kind = &k_info[i];
204 		char name[120] = "";
205 
206 		if (!kind->name || !kind->tval) continue;
207 
208 		object_short_name(name, sizeof name, kind->name);
209 		file_putf(fff, "object:%s:%s:%d:%d\n", tval_find_name(kind->tval),
210 				name, kind_x_attr[i], kind_x_char[i]);
211 	}
212 }
213 
214 /**
215  * Dump autoinscriptions
216  */
dump_autoinscriptions(ang_file * f)217 void dump_autoinscriptions(ang_file *f) {
218 	int i;
219 	for (i = 0; i < z_info->k_max; i++) {
220 		struct object_kind *k = &k_info[i];
221 		char name[120];
222 		const char *note;
223 
224 		if (!k->name || !k->tval) continue;
225 
226 		/* Only aware autoinscriptions go to the prefs file */
227 		note = get_autoinscription(k, true);
228 		if (note) {
229 			object_short_name(name, sizeof name, k->name);
230 			file_putf(f, "inscribe:%s:%s:%s\n", tval_find_name(k->tval), name, note);
231 		}
232 	}
233 }
234 
235 /**
236  * Dump features
237  */
dump_features(ang_file * fff)238 void dump_features(ang_file *fff)
239 {
240 	int i;
241 
242 	for (i = 0; i < z_info->f_max; i++) {
243 		struct feature *feat = &f_info[i];
244 		size_t j;
245 
246 		/* Skip non-entries */
247 		if (!feat->name) continue;
248 
249 		/* Skip mimic entries  */
250 		if (feat->mimic) continue;
251 
252 		file_putf(fff, "# Terrain: %s\n", feat->name);
253 		for (j = 0; j < LIGHTING_MAX; j++) {
254 			byte attr = feat_x_attr[j][i];
255 			wint_t chr = feat_x_char[j][i];
256 
257 			const char *light = NULL;
258 			if (j == LIGHTING_TORCH)
259 				light = "torch";
260 			if (j == LIGHTING_LOS)
261 				light = "los";
262 			else if (j == LIGHTING_LIT)
263 				light = "lit";
264 			else if (j == LIGHTING_DARK)
265 				light = "dark";
266 
267 			assert(light);
268 
269 			file_putf(fff, "feat:%s:%s:%d:%d\n", feat->name, light, attr, chr);
270 		}
271 	}
272 }
273 
274 /**
275  * Dump flavors
276  */
dump_flavors(ang_file * fff)277 void dump_flavors(ang_file *fff)
278 {
279 	struct flavor *f;
280 
281 	for (f = flavors; f; f = f->next) {
282 		byte attr = flavor_x_attr[f->fidx];
283 		wint_t chr = flavor_x_char[f->fidx];
284 
285 		file_putf(fff, "# Item flavor: %s\n", f->text);
286 		file_putf(fff, "flavor:%d:%d:%d\n\n", f->fidx, attr, chr);
287 	}
288 }
289 
290 /**
291  * Dump colors
292  */
dump_colors(ang_file * fff)293 void dump_colors(ang_file *fff)
294 {
295 	int i;
296 
297 	for (i = 0; i < MAX_COLORS; i++) {
298 		int kv = angband_color_table[i][0];
299 		int rv = angband_color_table[i][1];
300 		int gv = angband_color_table[i][2];
301 		int bv = angband_color_table[i][3];
302 
303 		const char *name = "unknown";
304 
305 		/* Skip non-entries */
306 		if (!kv && !rv && !gv && !bv) continue;
307 
308 		/* Extract the color name */
309 		if (i < BASIC_COLORS) name = color_table[i].name;
310 
311 		file_putf(fff, "# Color: %s\n", name);
312 		file_putf(fff, "color:%d:%d:%d:%d:%d\n\n", i, kv, rv, gv, bv);
313 	}
314 }
315 
316 
317 /**
318  * Dump the customizable attributes for renderers used in the character
319  * screen.
320  */
dump_ui_entry_renderers(ang_file * fff)321 void dump_ui_entry_renderers(ang_file *fff)
322 {
323 	int n = ui_entry_renderer_get_index_limit(), i;
324 
325 	file_putf(fff, "# Renderers for parts of the character screen.\n");
326 	file_putf(fff, "# Format entry-renderer:name:colors:label_colors:symbols\n");
327 	file_putf(fff, "# Use * for colors or label_colors to leave those unchanged\n");
328 	file_putf(fff, "# Leave off symbols and the colon before it to leave those unchanged\n");
329 	file_putf(fff, "# Look at lib/gamedata/ui_entry_renderers.txt for more information\n");
330 	file_putf(fff, "# about how colors, label_colors, and symbols are used\n");
331 
332 	for (i = ui_entry_renderer_get_min_index(); i < n; i++) {
333 		char* colors = ui_entry_renderer_get_colors(i);
334 		char* labcolors = ui_entry_renderer_get_label_colors(i);
335 		char* symbols = ui_entry_renderer_get_symbols(i);
336 
337 		file_putf(fff, "entry-renderer:%s:%s:%s:%s\n",
338 			ui_entry_renderer_get_name(i), colors, labcolors,
339 			symbols);
340 		mem_free(symbols);
341 		string_free(labcolors);
342 		string_free(colors);
343 	}
344 }
345 
346 
347 /**
348  * Write all current options to a user preference file.
349  */
option_dump(ang_file * fff)350 void option_dump(ang_file *fff)
351 {
352 	int i, j;
353 
354 	file_putf(fff, "# Options\n\n");
355 
356 	/* Dump window flags */
357 	for (i = 1; i < ANGBAND_TERM_MAX; i++) {
358 		/* Require a real window */
359 		if (!angband_term[i]) continue;
360 
361 		/* Check each flag */
362 		for (j = 0; j < (int)N_ELEMENTS(window_flag_desc); j++) {
363 			/* Require a real flag */
364 			if (!window_flag_desc[j]) continue;
365 
366 			/* Only dump the flag if true */
367 			if (window_flag[i] & (1L << j)) {
368 				file_putf(fff, "# Window '%s', Flag '%s'\n",
369 						  angband_term_name[i], window_flag_desc[j]);
370 				file_putf(fff, "window:%d:%d:1\n", i, j);
371 
372 				/* Skip a line */
373 				file_putf(fff, "\n");
374 			}
375 		}
376 	}
377 }
378 
379 /**
380  * Save a set of preferences to file, overwriting any old preferences with the
381  * same title.
382  *
383  * \param path is the filename to dump to
384  * \param dump is a pointer to the function that does the writing to file
385  * \param title is the name of this set of preferences
386  *
387  * \returns true on success, false otherwise.
388  */
prefs_save(const char * path,void (* dump)(ang_file *),const char * title)389 bool prefs_save(const char *path, void (*dump)(ang_file *), const char *title)
390 {
391 	ang_file *fff;
392 
393 	safe_setuid_grab();
394 
395 	/* Remove old keymaps */
396 	remove_old_dump(path, title);
397 
398 	fff = file_open(path, MODE_APPEND, FTYPE_TEXT);
399 	if (!fff) {
400 		safe_setuid_drop();
401 		return false;
402 	}
403 
404 	/* Append the header */
405 	pref_header(fff, title);
406 	file_putf(fff, "\n");
407 
408 	dump(fff);
409 
410 	file_putf(fff, "\n");
411 	pref_footer(fff, title);
412 	file_close(fff);
413 
414 	safe_setuid_drop();
415 
416 	return true;
417 }
418 
419 
420 
421 
422 
423 
424 /**
425  * ------------------------------------------------------------------------
426  * Pref file parser
427  * ------------------------------------------------------------------------ */
428 
429 /**
430  * Load another file.
431  */
parse_prefs_load(struct parser * p)432 static enum parser_error parse_prefs_load(struct parser *p)
433 {
434 	struct prefs_data *d = parser_priv(p);
435 	const char *file;
436 
437 	assert(d != NULL);
438 	if (d->bypass) return PARSE_ERROR_NONE;
439 
440 	file = parser_getstr(p, "file");
441 	(void)process_pref_file(file, true, d->user);
442 
443 	return PARSE_ERROR_NONE;
444 }
445 
446 /**
447  * Helper function for "process_pref_file()"
448  *
449  * Input:
450  *   v: output buffer array
451  *   f: final character
452  *
453  * Output:
454  *   result
455  */
process_pref_file_expr(char ** sp,char * fp)456 static const char *process_pref_file_expr(char **sp, char *fp)
457 {
458 	const char *v;
459 
460 	char *b;
461 	char *s;
462 
463 	char f = ' ';
464 
465 	/* Initial */
466 	s = (*sp);
467 
468 	/* Skip spaces */
469 	while (isspace((unsigned char)*s)) s++;
470 
471 	/* Save start */
472 	b = s;
473 
474 	/* Default */
475 	v = "?o?o?";
476 
477 	/* Either the string starts with a [ or it doesn't */
478 	if (*s == '[') {
479 		const char *p;
480 		const char *t;
481 
482 		/* Skip [ */
483 		s++;
484 
485 		/* First */
486 		t = process_pref_file_expr(&s, &f);
487 
488 		/* Check all the different types of connective */
489 		if (!*t) {
490 			/* Nothing */
491 		} else if (streq(t, "IOR")) {
492 			v = "0";
493 			while (*s && (f != ']')) {
494 				t = process_pref_file_expr(&s, &f);
495 				if (*t && !streq(t, "0")) v = "1";
496 			}
497 		} else if (streq(t, "AND")) {
498 			v = "1";
499 			while (*s && (f != ']')) {
500 				t = process_pref_file_expr(&s, &f);
501 				if (*t && streq(t, "0")) v = "0";
502 			}
503 		} else if (streq(t, "NOT")) {
504 			v = "1";
505 			while (*s && (f != ']')) {
506 				t = process_pref_file_expr(&s, &f);
507 				if (*t && !streq(t, "0")) v = "0";
508 			}
509 		} else if (streq(t, "EQU")) {
510 			v = "1";
511 			if (*s && (f != ']')) {
512 				t = process_pref_file_expr(&s, &f);
513 			}
514 			while (*s && (f != ']')) {
515 				p = t;
516 				t = process_pref_file_expr(&s, &f);
517 				if (*t && !streq(p, t)) v = "0";
518 			}
519 		} else if (streq(t, "LEQ")) {
520 			v = "1";
521 			if (*s && (f != ']')) {
522 				t = process_pref_file_expr(&s, &f);
523 			}
524 			while (*s && (f != ']')) {
525 				p = t;
526 				t = process_pref_file_expr(&s, &f);
527 				if (*t && (strcmp(p, t) >= 0)) v = "0";
528 			}
529 		} else if (streq(t, "GEQ")) {
530 			v = "1";
531 			if (*s && (f != ']')) {
532 				t = process_pref_file_expr(&s, &f);
533 			}
534 			while (*s && (f != ']')) {
535 				p = t;
536 				t = process_pref_file_expr(&s, &f);
537 				if (*t && (strcmp(p, t) <= 0)) v = "0";
538 			}
539 		} else {
540 			while (*s && (f != ']')) {
541 				(void) process_pref_file_expr(&s, &f);
542 			}
543 		}
544 
545 		/* Verify ending */
546 		if (f != ']') v = "?x?x?";
547 
548 		/* Extract final and Terminate */
549 		if ((f = *s) != '\0') *s++ = '\0';
550 	} else {
551 		/* Accept all printables except spaces and brackets */
552 		while (isprint((unsigned char)*s) && !strchr(" []", *s)) ++s;
553 
554 		/* Extract final and Terminate */
555 		if ((f = *s) != '\0') *s++ = '\0';
556 
557 		/* Variables start with $, otherwise it's a constant */
558 		if (*b == '$') {
559 			if (streq(b+1, "SYS"))
560 				v = ANGBAND_SYS;
561 			else if (streq(b+1, "RACE"))
562 				v = player->race->name;
563 			else if (streq(b+1, "CLASS"))
564 				v = player->class->name;
565 		} else {
566 			v = b;
567 		}
568 	}
569 
570 	/* Save */
571 	(*fp) = f;
572 	(*sp) = s;
573 
574 	return v;
575 }
576 
577 /**
578  * Parse one of the prefix-based logical expressions used in pref files
579  */
parse_prefs_expr(struct parser * p)580 static enum parser_error parse_prefs_expr(struct parser *p)
581 {
582 	struct prefs_data *d = parser_priv(p);
583 
584 	const char *v;
585 	char *str;
586 	char *expr;
587 	char f;
588 
589 	assert(d != NULL);
590 
591 	/* XXX this can be avoided with a rewrite of process_pref_file_expr */
592 	str = expr = string_make(parser_getstr(p, "expr"));
593 
594 	/* Parse the expr */
595 	v = process_pref_file_expr(&expr, &f);
596 
597 	/* Set flag */
598 	d->bypass = streq(v, "0");
599 
600 	string_free(str);
601 
602 	return PARSE_ERROR_NONE;
603 }
604 
parse_prefs_object(struct parser * p)605 static enum parser_error parse_prefs_object(struct parser *p)
606 {
607 	int tvi, svi;
608 	struct object_kind *kind;
609 	const char *tval, *sval;
610 
611 	struct prefs_data *d = parser_priv(p);
612 	assert(d != NULL);
613 	if (d->bypass) return PARSE_ERROR_NONE;
614 
615 	tval = parser_getsym(p, "tval");
616 	sval = parser_getsym(p, "sval");
617 	if (!strcmp(tval, "*")) {
618 		/* object:*:* means handle all objects and flavors */
619 		byte attr = parser_getint(p, "attr");
620 		wchar_t chr = parser_getint(p, "char");
621 		size_t i;
622 		struct flavor *flavor;
623 
624 		if (strcmp(sval, "*"))
625 			return PARSE_ERROR_UNRECOGNISED_SVAL;
626 
627 		for (i = 0; i < z_info->k_max; i++) {
628 			struct object_kind *kind_local = &k_info[i];
629 
630 			kind_x_attr[kind_local->kidx] = attr;
631 			kind_x_char[kind_local->kidx] = chr;
632 		}
633 
634 		for (flavor = flavors; flavor; flavor = flavor->next) {
635 			flavor_x_attr[flavor->fidx] = attr;
636 			flavor_x_char[flavor->fidx] = chr;
637 		}
638 	} else {
639 		tvi = tval_find_idx(tval);
640 		if (tvi < 0)
641 			return PARSE_ERROR_UNRECOGNISED_TVAL;
642 
643 		/* object:tval:* means handle all objects and flavors with this tval */
644 		if (!strcmp(sval, "*")) {
645 			byte attr = parser_getint(p, "attr");
646 			wchar_t chr = parser_getint(p, "char");
647 			size_t i;
648 			struct flavor *flavor;
649 
650 			for (i = 0; i < z_info->k_max; i++) {
651 				struct object_kind *kind_local = &k_info[i];
652 
653 				if (kind_local->tval != tvi)
654 					continue;
655 
656 				kind_x_attr[kind_local->kidx] = attr;
657 				kind_x_char[kind_local->kidx] = chr;
658 			}
659 
660 			for (flavor = flavors; flavor; flavor = flavor->next)
661 				if (flavor->tval == tvi) {
662 					flavor_x_attr[flavor->fidx] = attr;
663 					flavor_x_char[flavor->fidx] = chr;
664 				}
665 
666 		} else {
667 			/* No error at incorrect sval to stop failure due to outdated
668 			 * pref files and enable switching between old and new classes */
669 			svi = lookup_sval(tvi, sval);
670 			if (svi < 0)
671 				return PARSE_ERROR_NONE;
672 
673 			kind = lookup_kind(tvi, svi);
674 			if (!kind)
675 				return PARSE_ERROR_NONE;
676 
677 			kind_x_attr[kind->kidx] = (byte)parser_getint(p, "attr");
678 			kind_x_char[kind->kidx] = (wchar_t)parser_getint(p, "char");
679 		}
680 	}
681 
682 	return PARSE_ERROR_NONE;
683 }
684 
parse_prefs_monster(struct parser * p)685 static enum parser_error parse_prefs_monster(struct parser *p)
686 {
687 	const char *name;
688 	struct monster_race *monster;
689 
690 	struct prefs_data *d = parser_priv(p);
691 	assert(d != NULL);
692 	if (d->bypass) return PARSE_ERROR_NONE;
693 
694 	name = parser_getsym(p, "name");
695 	monster = lookup_monster(name);
696 	if (!monster)
697 		return PARSE_ERROR_NO_KIND_FOUND;
698 
699 	monster_x_attr[monster->ridx] = (byte)parser_getint(p, "attr");
700 	monster_x_char[monster->ridx] = (wchar_t)parser_getint(p, "char");
701 
702 	return PARSE_ERROR_NONE;
703 }
704 
parse_prefs_monster_base(struct parser * p)705 static enum parser_error parse_prefs_monster_base(struct parser *p)
706 {
707 	const char *name;
708 	struct monster_base *mb;
709 	size_t i;
710 	byte a;
711 	wchar_t c;
712 
713 	struct prefs_data *d = parser_priv(p);
714 	assert(d != NULL);
715 	if (d->bypass) return PARSE_ERROR_NONE;
716 
717 	name = parser_getsym(p, "name");
718 	mb = lookup_monster_base(name);
719 	if (!mb)
720 		return PARSE_ERROR_NO_KIND_FOUND;
721 	a = (byte)parser_getint(p, "attr");
722 	c = (wchar_t)parser_getint(p, "char");
723 
724 	for (i = 0; i < z_info->r_max; i++) {
725 		struct monster_race *race = &r_info[i];
726 
727 		if (race->base != mb) continue;
728 
729 		monster_x_attr[race->ridx] = a;
730 		monster_x_char[race->ridx] = c;
731 	}
732 
733 	return PARSE_ERROR_NONE;
734 }
735 
set_trap_graphic(int trap_idx,int light_idx,byte attr,char ch)736 static void set_trap_graphic(int trap_idx, int light_idx, byte attr, char ch) {
737 	if (light_idx < LIGHTING_MAX) {
738 		trap_x_attr[light_idx][trap_idx] = attr;
739 		trap_x_char[light_idx][trap_idx] = ch;
740 	} else {
741 		for (light_idx = 0; light_idx < LIGHTING_MAX; light_idx++) {
742 			trap_x_attr[light_idx][trap_idx] = attr;
743 			trap_x_char[light_idx][trap_idx] = ch;
744 		}
745 	}
746 }
747 
parse_prefs_trap(struct parser * p)748 static enum parser_error parse_prefs_trap(struct parser *p)
749 {
750 	const char *idx_sym;
751 	const char *lighting;
752 	int trap_idx;
753 	int light_idx;
754 
755 	struct prefs_data *d = parser_priv(p);
756 	assert(d != NULL);
757 	if (d->bypass) return PARSE_ERROR_NONE;
758 
759 	/* idx can be "*" or a name */
760 	idx_sym = parser_getsym(p, "idx");
761 
762 	if (!strcmp(idx_sym, "*")) {
763 		trap_idx = -1;
764 	} else {
765 		struct trap_kind *trap = lookup_trap(idx_sym);
766 		if (!trap) {
767 			return PARSE_ERROR_UNRECOGNISED_TRAP;
768 		} else {
769 			trap_idx = trap->tidx;
770 		}
771 	}
772 
773 	lighting = parser_getsym(p, "lighting");
774 	if (streq(lighting, "torch"))
775 		light_idx = LIGHTING_TORCH;
776 	else if (streq(lighting, "los"))
777 		light_idx = LIGHTING_LOS;
778 	else if (streq(lighting, "lit"))
779 		light_idx = LIGHTING_LIT;
780 	else if (streq(lighting, "dark"))
781 		light_idx = LIGHTING_DARK;
782 	else if (streq(lighting, "*"))
783 		light_idx = LIGHTING_MAX;
784 	else
785 		return PARSE_ERROR_INVALID_LIGHTING;
786 
787 	if (trap_idx == -1) {
788 		size_t i;
789 		for (i = 0; i < z_info->trap_max; i++) {
790 			set_trap_graphic(i, light_idx,
791 					parser_getint(p, "attr"), parser_getint(p, "char"));
792 		}
793 	} else {
794 		set_trap_graphic(trap_idx, light_idx,
795 				parser_getint(p, "attr"), parser_getint(p, "char"));
796 	}
797 
798 	return PARSE_ERROR_NONE;
799 }
800 
parse_prefs_feat(struct parser * p)801 static enum parser_error parse_prefs_feat(struct parser *p)
802 {
803 	int idx;
804 	const char *lighting;
805 	int light_idx;
806 
807 	struct prefs_data *d = parser_priv(p);
808 	assert(d != NULL);
809 	if (d->bypass) return PARSE_ERROR_NONE;
810 
811 	idx = lookup_feat(parser_getsym(p, "idx"));
812 	if (idx >= z_info->f_max)
813 		return PARSE_ERROR_OUT_OF_BOUNDS;
814 
815 	lighting = parser_getsym(p, "lighting");
816 	if (streq(lighting, "torch"))
817 		light_idx = LIGHTING_TORCH;
818 	else if (streq(lighting, "los"))
819 		light_idx = LIGHTING_LOS;
820 	else if (streq(lighting, "lit"))
821 		light_idx = LIGHTING_LIT;
822 	else if (streq(lighting, "dark"))
823 		light_idx = LIGHTING_DARK;
824 	else if (streq(lighting, "*"))
825 		light_idx = LIGHTING_MAX;
826 	else
827 		return PARSE_ERROR_INVALID_LIGHTING;
828 
829 	if (light_idx < LIGHTING_MAX) {
830 		feat_x_attr[light_idx][idx] = (byte)parser_getint(p, "attr");
831 		feat_x_char[light_idx][idx] = (wchar_t)parser_getint(p, "char");
832 	} else {
833 		for (light_idx = 0; light_idx < LIGHTING_MAX; light_idx++) {
834 			feat_x_attr[light_idx][idx] = (byte)parser_getint(p, "attr");
835 			feat_x_char[light_idx][idx] = (wchar_t)parser_getint(p, "char");
836 		}
837 	}
838 
839 	return PARSE_ERROR_NONE;
840 }
841 
parse_prefs_gf(struct parser * p)842 static enum parser_error parse_prefs_gf(struct parser *p)
843 {
844 	bool types[PROJ_MAX] = { 0 };
845 	const char *direction;
846 	int motion;
847 
848 	char *s, *t;
849 
850 	size_t i;
851 
852 	struct prefs_data *d = parser_priv(p);
853 	assert(d != NULL);
854 	if (d->bypass) return PARSE_ERROR_NONE;
855 
856 	/* Parse the type, which is a | seperated list of PROJ_ constants */
857 	s = string_make(parser_getsym(p, "type"));
858 	t = strtok(s, "| ");
859 	while (t) {
860 		if (streq(t, "*")) {
861 			memset(types, true, sizeof types);
862 		} else {
863 			int idx = proj_name_to_idx(t);
864 			if (idx == -1)
865 				return PARSE_ERROR_INVALID_VALUE;
866 
867 			types[idx] = true;
868 		}
869 
870 		t = strtok(NULL, "| ");
871 	}
872 
873 	string_free(s);
874 
875 	direction = parser_getsym(p, "direction");
876 	if (streq(direction, "static"))
877 		motion = BOLT_NO_MOTION;
878 	else if (streq(direction, "0"))
879 		motion = BOLT_0;
880 	else if (streq(direction, "45"))
881 		motion = BOLT_45;
882 	else if (streq(direction, "90"))
883 		motion = BOLT_90;
884 	else if (streq(direction, "135"))
885 		motion = BOLT_135;
886 	else
887 		return PARSE_ERROR_INVALID_VALUE;
888 
889 	for (i = 0; i < PROJ_MAX; i++) {
890 		if (!types[i]) continue;
891 
892 		proj_to_attr[i][motion] = (byte)parser_getuint(p, "attr");
893 		proj_to_char[i][motion] = (wchar_t)parser_getuint(p, "char");
894 	}
895 
896 	return PARSE_ERROR_NONE;
897 }
898 
parse_prefs_flavor(struct parser * p)899 static enum parser_error parse_prefs_flavor(struct parser *p)
900 {
901 	unsigned int idx;
902 	struct flavor *flavor;
903 
904 	struct prefs_data *d = parser_priv(p);
905 	assert(d != NULL);
906 	if (d->bypass) return PARSE_ERROR_NONE;
907 
908 	idx = parser_getuint(p, "idx");
909 	for (flavor = flavors; flavor; flavor = flavor->next)
910 		if (flavor->fidx == idx)
911 			break;
912 
913 	if (flavor) {
914 		flavor_x_attr[idx] = (byte)parser_getint(p, "attr");
915 		flavor_x_char[idx] = (wchar_t)parser_getint(p, "char");
916 	}
917 
918 	return PARSE_ERROR_NONE;
919 }
920 
parse_prefs_inscribe(struct parser * p)921 static enum parser_error parse_prefs_inscribe(struct parser *p)
922 {
923 	int tvi, svi;
924 	struct object_kind *kind;
925 
926 	struct prefs_data *d = parser_priv(p);
927 	assert(d != NULL);
928 	if (d->bypass) return PARSE_ERROR_NONE;
929 
930 	tvi = tval_find_idx(parser_getsym(p, "tval"));
931 	if (tvi < 0)
932 		return PARSE_ERROR_UNRECOGNISED_TVAL;
933 
934 	svi = lookup_sval(tvi, parser_getsym(p, "sval"));
935 	if (svi < 0)
936 		return PARSE_ERROR_UNRECOGNISED_SVAL;
937 
938 	kind = lookup_kind(tvi, svi);
939 	if (!kind)
940 		return PARSE_ERROR_UNRECOGNISED_SVAL;
941 
942 	add_autoinscription(kind->kidx, parser_getstr(p, "text"), true);
943 
944 	return PARSE_ERROR_NONE;
945 }
946 
parse_prefs_keymap_action(struct parser * p)947 static enum parser_error parse_prefs_keymap_action(struct parser *p)
948 {
949 	const char *act = "";
950 
951 	struct prefs_data *d = parser_priv(p);
952 	assert(d != NULL);
953 	if (d->bypass) return PARSE_ERROR_NONE;
954 
955 	if (parser_hasval(p, "act")) {
956 		act = parser_getstr(p, "act");
957 	}
958 	keypress_from_text(d->keymap_buffer, N_ELEMENTS(d->keymap_buffer), act);
959 
960 	return PARSE_ERROR_NONE;
961 }
962 
parse_prefs_keymap_input(struct parser * p)963 static enum parser_error parse_prefs_keymap_input(struct parser *p)
964 {
965 	int mode;
966 	struct keypress tmp[2];
967 
968 	struct prefs_data *d = parser_priv(p);
969 	assert(d != NULL);
970 	if (d->bypass) return PARSE_ERROR_NONE;
971 
972 	mode = parser_getint(p, "mode");
973 	if (mode < 0 || mode >= KEYMAP_MODE_MAX)
974 		return PARSE_ERROR_OUT_OF_BOUNDS;
975 
976 	keypress_from_text(tmp, N_ELEMENTS(tmp), parser_getstr(p, "key"));
977 	if (tmp[0].type != EVT_KBRD || tmp[1].type != EVT_NONE)
978 		return PARSE_ERROR_FIELD_TOO_LONG;
979 
980 	keymap_add(mode, tmp[0], d->keymap_buffer, d->user);
981 
982 	return PARSE_ERROR_NONE;
983 }
984 
parse_prefs_message(struct parser * p)985 static enum parser_error parse_prefs_message(struct parser *p)
986 {
987 	int a, msg_index;
988 	const char *attr;
989 	const char *type;
990 
991 	struct prefs_data *d = parser_priv(p);
992 	assert(d != NULL);
993 	if (d->bypass) return PARSE_ERROR_NONE;
994 
995 	type = parser_getsym(p, "type");
996 	attr = parser_getsym(p, "attr");
997 
998 	msg_index = message_lookup_by_name(type);
999 
1000 	if (msg_index < 0)
1001 		return PARSE_ERROR_INVALID_MESSAGE;
1002 
1003 	if (strlen(attr) > 1)
1004 		a = color_text_to_attr(attr);
1005 	else
1006 		a = color_char_to_attr(attr[0]);
1007 
1008 	if (a < 0)
1009 		return PARSE_ERROR_INVALID_COLOR;
1010 
1011 	message_color_define(msg_index, (byte)a);
1012 
1013 	return PARSE_ERROR_NONE;
1014 }
1015 
parse_prefs_color(struct parser * p)1016 static enum parser_error parse_prefs_color(struct parser *p)
1017 {
1018 	int idx;
1019 
1020 	struct prefs_data *d = parser_priv(p);
1021 	assert(d != NULL);
1022 	if (d->bypass) return PARSE_ERROR_NONE;
1023 
1024 	idx = parser_getuint(p, "idx");
1025 	if (idx > MAX_COLORS)
1026 		return PARSE_ERROR_OUT_OF_BOUNDS;
1027 
1028 	angband_color_table[idx][0] = parser_getint(p, "k");
1029 	angband_color_table[idx][1] = parser_getint(p, "r");
1030 	angband_color_table[idx][2] = parser_getint(p, "g");
1031 	angband_color_table[idx][3] = parser_getint(p, "b");
1032 
1033 	return PARSE_ERROR_NONE;
1034 }
1035 
parse_prefs_window(struct parser * p)1036 static enum parser_error parse_prefs_window(struct parser *p)
1037 {
1038 	int window;
1039 	size_t flag;
1040 
1041 	struct prefs_data *d = parser_priv(p);
1042 	assert(d != NULL);
1043 	if (d->bypass) return PARSE_ERROR_NONE;
1044 
1045 	window = parser_getint(p, "window");
1046 	if (window <= 0 || window >= ANGBAND_TERM_MAX)
1047 		return PARSE_ERROR_OUT_OF_BOUNDS;
1048 
1049 	flag = parser_getuint(p, "flag");
1050 	if (flag >= N_ELEMENTS(window_flag_desc))
1051 		return PARSE_ERROR_OUT_OF_BOUNDS;
1052 
1053 	if (window_flag_desc[flag])
1054 	{
1055 		int value = parser_getuint(p, "value");
1056 		if (value)
1057 			d->window_flags[window] |= (1L << flag);
1058 		else
1059 			d->window_flags[window] &= ~(1L << flag);
1060 	}
1061 
1062 	d->loaded_window_flag[window] = true;
1063 
1064 	return PARSE_ERROR_NONE;
1065 }
1066 
parse_prefs_entry_renderer(struct parser * p)1067 static enum parser_error parse_prefs_entry_renderer(struct parser *p)
1068 {
1069 	const char* name = parser_getsym(p, "name");
1070 	const char* colors;
1071 	const char* label_colors;
1072 	const char* symbols;
1073 	int index;
1074 
1075 	index = ui_entry_renderer_lookup(name);
1076 	if (index == 0) {
1077 		return PARSE_ERROR_INVALID_VALUE;
1078 	}
1079 	if (parser_hasval(p, "colors")) {
1080 		colors = parser_getsym(p, "colors");
1081 		if (streq(colors, "*")) {
1082 			colors = NULL;
1083 		}
1084 	} else {
1085 		colors = NULL;
1086 	}
1087 	if (parser_hasval(p, "labelcolors")) {
1088 		label_colors = parser_getsym(p, "labelcolors");
1089 		if (streq(label_colors, "*")) {
1090 			label_colors = NULL;
1091 		}
1092 	} else {
1093 		label_colors = NULL;
1094 	}
1095 	if (parser_hasval(p, "symbols")) {
1096 		symbols = parser_getsym(p, "symbols");
1097 	} else {
1098 		symbols = NULL;
1099 	}
1100 	if (ui_entry_renderer_customize(index, colors, label_colors, symbols)) {
1101 		return PARSE_ERROR_INVALID_VALUE;
1102 	}
1103 	return PARSE_ERROR_NONE;
1104 }
1105 
parse_prefs_dummy(struct parser * p)1106 enum parser_error parse_prefs_dummy(struct parser *p)
1107 {
1108 	return PARSE_ERROR_NONE;
1109 }
1110 
init_parse_prefs(bool user)1111 static struct parser *init_parse_prefs(bool user)
1112 {
1113 	struct parser *p = parser_new();
1114 	struct prefs_data *pd = mem_zalloc(sizeof *pd);
1115 	int i;
1116 
1117 	parser_setpriv(p, pd);
1118 	pd->user = user;
1119 	for (i = 0; i < ANGBAND_TERM_MAX; i++) {
1120 		pd->loaded_window_flag[i] = false;
1121 	}
1122 
1123 	parser_reg(p, "% str file", parse_prefs_load);
1124 	parser_reg(p, "? str expr", parse_prefs_expr);
1125 	parser_reg(p, "object sym tval sym sval int attr int char", parse_prefs_object);
1126 	parser_reg(p, "monster sym name int attr int char", parse_prefs_monster);
1127 	parser_reg(p, "monster-base sym name int attr int char", parse_prefs_monster_base);
1128 	parser_reg(p, "feat sym idx sym lighting int attr int char", parse_prefs_feat);
1129 	parser_reg(p, "trap sym idx sym lighting int attr int char", parse_prefs_trap);
1130 	parser_reg(p, "GF sym type sym direction uint attr uint char", parse_prefs_gf);
1131 	parser_reg(p, "flavor uint idx int attr int char", parse_prefs_flavor);
1132 	parser_reg(p, "inscribe sym tval sym sval str text", parse_prefs_inscribe);
1133 	parser_reg(p, "keymap-act ?str act", parse_prefs_keymap_action);
1134 	parser_reg(p, "keymap-input int mode str key", parse_prefs_keymap_input);
1135 	parser_reg(p, "message sym type sym attr", parse_prefs_message);
1136 	parser_reg(p, "color uint idx int k int r int g int b", parse_prefs_color);
1137 	parser_reg(p, "window int window uint flag uint value", parse_prefs_window);
1138 	parser_reg(p, "entry-renderer sym name ?sym colors ?sym labelcolors ?sym symbols", parse_prefs_entry_renderer);
1139 	register_sound_pref_parser(p);
1140 
1141 	return p;
1142 }
1143 
finish_parse_prefs(struct parser * p)1144 static errr finish_parse_prefs(struct parser *p)
1145 {
1146 	struct prefs_data *d = parser_priv(p);
1147 	int i;
1148 
1149 	/* Update sub-windows based on the newly read-in prefs.
1150 	 *
1151 	 * The window_flag[] array cannot be updated directly during
1152 	 * parsing since the changes between the existing flags and the new
1153 	 * are used to set/unset the event handlers that update the windows.
1154 	 *
1155 	 * Build a complete set to pass to subwindows_set_flags() by loading
1156 	 * any that weren't read in by the parser from the existing set.
1157 	 */
1158 	for (i = 0; i < ANGBAND_TERM_MAX; i++) {
1159 		if (!d->loaded_window_flag[i])
1160 			d->window_flags[i] = window_flag[i];
1161 	}
1162 	subwindows_set_flags(d->window_flags, ANGBAND_TERM_MAX);
1163 
1164 	return PARSE_ERROR_NONE;
1165 }
1166 
process_pref_file_command(const char * s)1167 errr process_pref_file_command(const char *s)
1168 {
1169 	struct parser *p = init_parse_prefs(true);
1170 	errr e = parser_parse(p, s);
1171 	mem_free(parser_priv(p));
1172 	parser_destroy(p);
1173 	return e;
1174 }
1175 
1176 
print_error(const char * name,struct parser * p)1177 static void print_error(const char *name, struct parser *p) {
1178 	struct parser_state s;
1179 	parser_getstate(p, &s);
1180 	msg("Parse error in %s line %d column %d: %s: %s", name,
1181 	           s.line, s.col, s.msg, parser_error_str[s.error]);
1182 	event_signal(EVENT_MESSAGE_FLUSH);
1183 }
1184 
1185 
1186 /**
1187  * Process the user pref file with a given path.
1188  *
1189  * \param path is the name of the pref file.
1190  * \param quiet means "don't complain about not finding the file".
1191  * \param user should be true if the pref file is user-specific and not a game
1192  * default.
1193  */
process_pref_file_named(const char * path,bool quiet,bool user)1194 static bool process_pref_file_named(const char *path, bool quiet, bool user) {
1195 	ang_file *f = file_open(path, MODE_READ, -1);
1196 	errr e = 0;
1197 
1198 	if (!f) {
1199 		if (!quiet)
1200 			msg("Cannot open '%s'.", path);
1201 
1202 		e = PARSE_ERROR_INTERNAL; /* signal failure to callers */
1203 	} else {
1204 		char line[1024];
1205 		int line_no = 0;
1206 
1207 		struct parser *p = init_parse_prefs(user);
1208 		while (file_getl(f, line, sizeof line)) {
1209 			line_no++;
1210 
1211 			e = parser_parse(p, line);
1212 			if (e != PARSE_ERROR_NONE) {
1213 				print_error(path, p);
1214 				break;
1215 			}
1216 		}
1217 		finish_parse_prefs(p);
1218 
1219 		file_close(f);
1220 		mem_free(parser_priv(p));
1221 		parser_destroy(p);
1222 	}
1223 
1224 	/* Result */
1225 	return e == PARSE_ERROR_NONE;
1226 }
1227 
1228 
1229 /**
1230  * Process the user pref file with a given name and search paths.
1231  *
1232  * \param name is the name of the pref file.
1233  * \param quiet means "don't complain about not finding the file".
1234  * \param user should be true if the pref file is user-specific and not a game
1235  * default.
1236  * \param base_search_path is the first path that should be checked for the file
1237  * \param fallback_search_path is the path that should be checked if the file
1238  * couldn't be found at the base path.
1239  * \param used_fallback will be set on return to true if the fallback path was
1240  * used, false otherwise.
1241  * \returns true if everything worked OK, false otherwise.
1242  */
process_pref_file_layered(const char * name,bool quiet,bool user,const char * base_search_path,const char * fallback_search_path,bool * used_fallback)1243 static bool process_pref_file_layered(const char *name, bool quiet, bool user,
1244 									  const char *base_search_path,
1245 									  const char *fallback_search_path,
1246 									  bool *used_fallback)
1247 {
1248 	char buf[1024];
1249 
1250 	assert(base_search_path != NULL);
1251 
1252 	/* Build the filename */
1253 	path_build(buf, sizeof(buf), base_search_path, name);
1254 
1255 	if (used_fallback != NULL)
1256 		*used_fallback = false;
1257 
1258 	if (!file_exists(buf) && fallback_search_path != NULL) {
1259 		path_build(buf, sizeof(buf), fallback_search_path, name);
1260 
1261 		if (used_fallback != NULL)
1262 			*used_fallback = true;
1263 	}
1264 
1265 	return process_pref_file_named(buf, quiet, user);
1266 }
1267 
1268 
1269 /**
1270  * Look for a pref file at its base location (falling back to another path if
1271  * needed) and then in the user location. This effectively will layer a user
1272  * pref file on top of a default pref file.
1273  *
1274  * Because of the way this function works, there might be some unexpected
1275  * effects when a pref file triggers another pref file to be loaded.
1276  * For example, pref/pref.prf causes message.prf to load. This means that the
1277  * game will load pref/pref.prf, then pref/message.prf, then user/message.prf,
1278  * and finally user/pref.prf.
1279  *
1280  * \param name is the name of the pref file.
1281  * \param quiet means "don't complain about not finding the file".
1282  * \param user should be true if the pref file is user-specific and not a game
1283  * default.
1284  * \returns true if everything worked OK, false otherwise.
1285  */
process_pref_file(const char * name,bool quiet,bool user)1286 bool process_pref_file(const char *name, bool quiet, bool user)
1287 {
1288 	bool root_success = false;
1289 	bool user_success = false;
1290 	bool used_fallback = false;
1291 
1292 	/* This supports the old behavior: look for a file first in 'pref/', and
1293 	 * if not found there, then 'user/'. */
1294 	root_success = process_pref_file_layered(name, quiet, user,
1295 											 ANGBAND_DIR_CUSTOMIZE,
1296 											 ANGBAND_DIR_USER,
1297 											 &used_fallback);
1298 
1299 	/* If not found, do a check of the current graphics directory */
1300 	if (!root_success && current_graphics_mode)
1301 		root_success = process_pref_file_layered(name, quiet, user,
1302 												 current_graphics_mode->path,
1303 												 NULL, NULL);
1304 
1305 	/* Next, we want to force a check for the file in the user/ directory.
1306 	 * However, since we used the user directory as a fallback in the previous
1307 	 * check, we only want to do this if the fallback wasn't used. This cuts
1308 	 * down on unnecessary parsing. */
1309 	if (!used_fallback) {
1310 		/* Force quiet (since this is an optional file) and force user
1311 		 * (since this should always be considered user-specific). */
1312 		user_success = process_pref_file_layered(name, true, true,
1313 												 ANGBAND_DIR_USER, NULL,
1314 												 &used_fallback);
1315 	}
1316 
1317 	/* If only one load was successful, that's okay; we loaded something. */
1318 	return root_success || user_success;
1319 }
1320 
1321 /**
1322  * Reset the "visual" lists
1323  *
1324  * This involves resetting various things to their "default" state.
1325  *
1326  * If the "prefs" flag is true, then we will also load the appropriate
1327  * "user pref file" based on the current setting of the "use_graphics"
1328  * flag.  This is useful for switching "graphics" on/off.
1329  */
reset_visuals(bool load_prefs)1330 void reset_visuals(bool load_prefs)
1331 {
1332 	int i, j;
1333 	struct flavor *f;
1334 
1335 	/* Extract default attr/char code for features */
1336 	for (i = 0; i < z_info->f_max; i++) {
1337 		struct feature *feat = &f_info[i];
1338 
1339 		/* Assume we will use the underlying values */
1340 		for (j = 0; j < LIGHTING_MAX; j++) {
1341 			feat_x_attr[j][i] = feat->d_attr;
1342 			feat_x_char[j][i] = feat->d_char;
1343 		}
1344 	}
1345 
1346 	/* Extract default attr/char code for objects */
1347 	for (i = 0; i < z_info->k_max; i++) {
1348 		struct object_kind *kind = &k_info[i];
1349 
1350 		/* Default attr/char */
1351 		kind_x_attr[i] = kind->d_attr;
1352 		kind_x_char[i] = kind->d_char;
1353 	}
1354 
1355 	/* Extract default attr/char code for monsters */
1356 	for (i = 0; i < z_info->r_max; i++) {
1357 		struct monster_race *race = &r_info[i];
1358 
1359 		/* Default attr/char */
1360 		monster_x_attr[i] = race->d_attr;
1361 		monster_x_char[i] = race->d_char;
1362 	}
1363 
1364 	/* Extract default attr/char code for traps */
1365 	for (i = 0; i < z_info->trap_max; i++) {
1366 		struct trap_kind *trap = &trap_info[i];
1367 
1368 		/* Default attr/char */
1369 		for (j = 0; j < LIGHTING_MAX; j++) {
1370 			trap_x_attr[j][i] = trap->d_attr;
1371 			trap_x_char[j][i] = trap->d_char;
1372 		}
1373 	}
1374 
1375 	/* Extract default attr/char code for flavors */
1376 	for (f = flavors; f; f = f->next) {
1377 		flavor_x_attr[f->fidx] = f->d_attr;
1378 		flavor_x_char[f->fidx] = f->d_char;
1379 	}
1380 
1381 	if (!load_prefs)
1382 		return;
1383 
1384 	/* Graphic symbols */
1385 	if (use_graphics) {
1386 		/* if we have a graphics mode, see if the mode has a pref file name */
1387 		graphics_mode *mode = get_graphics_mode(use_graphics);
1388 		char buf[2014];
1389 
1390 		assert(mode);
1391 
1392 		/* Build path to the pref file */
1393 		path_build(buf, sizeof buf, mode->path, mode->pref);
1394 
1395 		process_pref_file_named(buf, false, false);
1396 	} else {
1397 		/* Normal symbols */
1398 		process_pref_file("font.prf", false, false);
1399 	}
1400 }
1401 
1402 /**
1403  * Initialise the glyphs for monsters, objects, traps, flavors and terrain
1404  */
textui_prefs_init(void)1405 void textui_prefs_init(void)
1406 {
1407 	int i;
1408 	struct flavor *f;
1409 
1410 	monster_x_attr = mem_zalloc(z_info->r_max * sizeof(byte));
1411 	monster_x_char = mem_zalloc(z_info->r_max * sizeof(wchar_t));
1412 	kind_x_attr = mem_zalloc(z_info->k_max * sizeof(byte));
1413 	kind_x_char = mem_zalloc(z_info->k_max * sizeof(wchar_t));
1414 	for (i = 0; i < LIGHTING_MAX; i++) {
1415 		feat_x_attr[i] = mem_zalloc(z_info->f_max * sizeof(byte));
1416 		feat_x_char[i] = mem_zalloc(z_info->f_max * sizeof(wchar_t));
1417 	}
1418 	for (i = 0; i < LIGHTING_MAX; i++) {
1419 		trap_x_attr[i] = mem_zalloc(z_info->trap_max * sizeof(byte));
1420 		trap_x_char[i] = mem_zalloc(z_info->trap_max * sizeof(wchar_t));
1421 	}
1422 	for (f = flavors; f; f = f->next)
1423 		if (flavor_max < f->fidx)
1424 			flavor_max = f->fidx;
1425 	flavor_x_attr = mem_zalloc((flavor_max + 1) * sizeof(byte));
1426 	flavor_x_char = mem_zalloc((flavor_max + 1) * sizeof(wchar_t));
1427 
1428 	reset_visuals(false);
1429 }
1430 
1431 /**
1432  * Free the glyph arrays for monsters, objects, traps, flavors and terrain
1433  */
textui_prefs_free(void)1434 void textui_prefs_free(void)
1435 {
1436 	int i;
1437 
1438 	mem_free(monster_x_attr);
1439 	mem_free(monster_x_char);
1440 	mem_free(kind_x_attr);
1441 	mem_free(kind_x_char);
1442 	for (i = 0; i < LIGHTING_MAX; i++) {
1443 		mem_free(feat_x_attr[i]);
1444 		mem_free(feat_x_char[i]);
1445 	}
1446 	for (i = 0; i < LIGHTING_MAX; i++) {
1447 		mem_free(trap_x_attr[i]);
1448 		mem_free(trap_x_char[i]);
1449 	}
1450 	mem_free(flavor_x_attr);
1451 	mem_free(flavor_x_char);
1452 }
1453 
1454 /**
1455  * Ask for a "user pref line" and process it
1456  */
do_cmd_pref(void)1457 void do_cmd_pref(void)
1458 {
1459 	char tmp[80];
1460 
1461 	/* Default */
1462 	my_strcpy(tmp, "", sizeof(tmp));
1463 
1464 	/* Ask for a "user pref command" */
1465 	if (!get_string("Pref: ", tmp, 80)) return;
1466 
1467 	/* Process that pref command */
1468 	(void)process_pref_file_command(tmp);
1469 }
1470