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