1 /**
2  * \file ui-entry.c
3  * \brief Definitions to link object/player properties to 2nd character screen
4  *
5  * This work is free software; you can redistribute it and/or modify it
6  * under the terms of either:
7  *
8  * a) the GNU General Public License as published by the Free Software
9  *    Foundation, version 2, or
10  *
11  * b) the "Angband licence":
12  *    This software may be copied and distributed for educational, research,
13  *    and not for profit purposes provided that this copyright and statement
14  *    are included in all such copies.  Other copyrights may also apply.
15  */
16 
17 #include "init.h"
18 #include "object.h"
19 #include "obj-curse.h"
20 #include "obj-gear.h"
21 #include "obj-knowledge.h"
22 #include "obj-util.h"
23 #include "player.h"
24 #include "player-timed.h"
25 #include "ui-entry.h"
26 #include "ui-entry-combiner.h"
27 #include "ui-entry-init.h"
28 #include "ui-entry-renderers.h"
29 #include "z-util.h"
30 #include "z-virt.h"
31 
32 /*
33  * This is the maximum number of characters stored for a label.  Used to
34  * limit what's extracted from the configuration file.
35  */
36 #define MAX_ENTRY_LABEL (80)
37 
38 struct ui_entry_iterator {
39 	struct ui_entry **entries;
40 	int n, i;
41 };
42 
43 struct cached_object_data {
44 	bitflag f[OF_SIZE];
45 };
46 
47 struct cached_player_data {
48 	bitflag untimed[OF_SIZE];
49 	bitflag timed[OF_SIZE];
50 };
51 
52 struct category_reference {
53 	const char *name;
54 	int priority;
55 	bool priority_set;
56 };
57 
58 struct bound_object_property {
59 	int type;
60 	int index;
61 	int value;
62 	bool have_value;
63 	bool isaux;
64 };
65 
66 struct bound_player_ability {
67 	struct player_ability *ability;
68 	int value;
69 	bool have_value;
70 	bool isaux;
71 };
72 
73 
74 enum {
75 	ENTRY_FLAG_TIMED_AUX = 1,
76 	/* Used internally; not set from within the configuration files. */
77 	ENTRY_FLAG_TEMPLATE_ONLY = (1 << 20)
78 };
79 struct entry_flag {
80 	const char *name;
81 	int value;
82 };
83 static struct entry_flag entry_flags[] = {
84 	{ "TIMED_AS_AUX", ENTRY_FLAG_TIMED_AUX },
85 };
86 
87 /*
88  * Set the maximum number of shortened versions of the label that will be
89  * accepted.  The shortened versions will have lengths of one to MAX_SHORTENED
90  * characters, not including the terminating null.
91  */
92 #define MAX_SHORTENED (10)
93 struct ui_entry {
94 	char *name;
95 	struct category_reference *categories;
96 	struct bound_object_property *obj_props;
97 	struct bound_player_ability *p_abilities;
98 	wchar_t *label;
99 	wchar_t *shortened_labels[MAX_SHORTENED];
100 	wchar_t shortened_buffer[MAX_SHORTENED + (MAX_SHORTENED * (MAX_SHORTENED + 1)) / 2];
101 	int nshortened[MAX_SHORTENED];
102 	int nlabel;
103 	int renderer_index;
104 	int combiner_index;
105 	int default_priority;
106 	int param_index;
107 	int flags;
108 	int n_category;
109 	int nalloc_category;
110 	int n_obj_prop;
111 	int nalloc_obj_prop;
112 	int n_p_ability;
113 	int nalloc_p_ability;
114 };
115 
116 static int ui_entry_search(const char *name, int *ind);
117 static int ui_entry_search_categories(const struct ui_entry *entry,
118 	const char *name, int *ind);
119 static void modifier_to_skill(int modind, int *skillind, int *skill2mod_num,
120 	int *skill2mod_den
121 );
122 static int get_timed_element_effect(const struct player *p, int ind);
123 static int get_timed_modifier_effect(const struct player *p, int ind);
124 
125 struct ui_entry_name_parameter {
126 	const char *name;
127 	int (*count_func)(void);
128 	const char *(*ith_name_func)(int i);
129 };
130 
131 static int get_dummy_param_count(void);
132 static const char *get_dummy_param_name(int i);
133 static int get_element_count(void);
134 static const char *get_element_name(int i);
135 static int get_stat_count(void);
136 static const char *get_stat_name(int i);
137 
138 static struct ui_entry_name_parameter name_parameters[] = {
139 	{ "", get_dummy_param_count, get_dummy_param_name },
140 	{ "element", get_element_count, get_element_name },
141 	{ "stat", get_stat_count, get_stat_name },
142 };
143 
144 struct ui_entry_priority_scheme {
145 	const char *name;
146 	int (*priority)(int i);
147 };
148 
149 static int get_dummy_priority(int i);
150 static int get_priority_from_index(int i);
151 static int get_priority_from_negative_index(int i);
152 
153 static struct ui_entry_priority_scheme priority_schemes[] = {
154 	{ "", get_dummy_priority },
155 	{ "index", get_priority_from_index },
156 	{ "negative_index", get_priority_from_negative_index },
157 };
158 
159 struct embryonic_category_reference {
160 	const char *name;
161 	int psource_index;
162 	int priority;
163 	bool priority_set;
164 };
165 
166 struct embryonic_ui_entry {
167 	struct ui_entry *entry;
168 	struct embryonic_category_reference *categories;
169 	int param_index;
170 	int psource_index;
171 	int last_category_index;
172 	bool exists;
173 };
174 
175 static int n_category = 0;
176 static int nalloc_category = 0;
177 static char **categories = NULL;
178 
179 static int n_entry = 0;
180 static int nalloc_entry = 0;
181 static struct ui_entry **entries = NULL;
182 
183 
184 /**
185  * Binds an object property, given by type and index, to a user interface
186  * entry configured in ui_entry.txt.  If name isn't configured in that file
187  * returns a nonzero value.  Otherwise returns zero after binding the property.
188  * Currently, this is only used for some parts of the second character screen.
189  */
bind_object_property_to_ui_entry_by_name(const char * name,int type,int index,int value,bool have_value,bool isaux)190 int bind_object_property_to_ui_entry_by_name(const char *name, int type,
191 	int index, int value, bool have_value, bool isaux)
192 {
193 	int ind;
194 
195 	if (! ui_entry_search(name, &ind)) {
196 		return 1;
197 	}
198 	if (entries[ind]->n_obj_prop == entries[ind]->nalloc_obj_prop) {
199 		struct bound_object_property *nblk;
200 
201 		if (entries[ind]->nalloc_obj_prop > INT_MAX / 2) {
202 			return 2;
203 		}
204 		entries[ind]->nalloc_obj_prop =
205 			(entries[ind]->nalloc_obj_prop == 0) ?
206 			4 : 2 * entries[ind]->nalloc_obj_prop;
207 		nblk = mem_alloc(entries[ind]->nalloc_obj_prop *
208 			sizeof(*nblk));
209 		if (entries[ind]->n_obj_prop > 0) {
210 			(void) memcpy(nblk, entries[ind]->obj_props,
211 				entries[ind]->n_obj_prop * sizeof(*nblk));
212 		}
213 		mem_free(entries[ind]->obj_props);
214 		entries[ind]->obj_props = nblk;
215 	}
216 	entries[ind]->obj_props[entries[ind]->n_obj_prop].type = type;
217 	entries[ind]->obj_props[entries[ind]->n_obj_prop].index = index;
218 	entries[ind]->obj_props[entries[ind]->n_obj_prop].value = value;
219 	entries[ind]->obj_props[entries[ind]->n_obj_prop].have_value =
220 		have_value;
221 	entries[ind]->obj_props[entries[ind]->n_obj_prop].isaux = isaux;
222 	++entries[ind]->n_obj_prop;
223 	return 0;
224 }
225 
226 
227 /**
228  * Binds a player ability to to a user interface entry configured in ui_entry.
229  * If name isn't configured in that file returns a nonzero value.  Otherwise
230  * returns zero after binding the ability.  Currently, this is only used for
231  * some parts of the second character screen.
232  */
bind_player_ability_to_ui_entry_by_name(const char * name,struct player_ability * ability,int value,bool have_value,bool isaux)233 int bind_player_ability_to_ui_entry_by_name(const char *name,
234 	struct player_ability *ability, int value, bool have_value, bool isaux)
235 {
236 	int ind;
237 
238 	if (! ui_entry_search(name, &ind)) {
239 		return 1;
240 	}
241 	if (entries[ind]->n_p_ability ==
242 		entries[ind]->nalloc_p_ability) {
243 		struct bound_player_ability *abilities;
244 
245 		if (entries[ind]->nalloc_p_ability > INT_MAX / 2) {
246 			return 2;
247 		}
248 		entries[ind]->nalloc_p_ability =
249 			(entries[ind]->nalloc_p_ability == 0) ?
250 			4 : 2 * entries[ind]->nalloc_p_ability;
251 		abilities = mem_alloc(entries[ind]->nalloc_p_ability *
252 			sizeof(*abilities));
253 		if (entries[ind]->n_p_ability > 0) {
254 			(void) memcpy(abilities, entries[ind]->p_abilities,
255 				entries[ind]->n_p_ability *
256 				sizeof(*abilities));
257 		}
258 		mem_free(entries[ind]->p_abilities);
259 		entries[ind]->p_abilities = abilities;
260 	}
261 	entries[ind]->p_abilities[entries[ind]->n_p_ability].ability = ability;
262 	entries[ind]->p_abilities[entries[ind]->n_p_ability].value = value;
263 	entries[ind]->p_abilities[entries[ind]->n_p_ability].have_value =
264 		have_value;
265 	entries[ind]->p_abilities[entries[ind]->n_p_ability].isaux = isaux;
266 	++entries[ind]->n_p_ability;
267 	return 0;
268 }
269 
270 
271 /**
272  * Returns true if the given user interface entry was configured in
273  * ui_entry.txt to be part of the category with the given name.  Otherwise,
274  * returns false.
275  */
ui_entry_has_category(const struct ui_entry * entry,const char * name)276 bool ui_entry_has_category(const struct ui_entry *entry, const char *name)
277 {
278 	int ind;
279 
280 	return ui_entry_search_categories(entry, name, &ind) != 0;
281 }
282 
283 
284 /**
285  * Fills label with length characters, including a terminating null, of a
286  * label for a user interface entry configured in ui_entry.txt.  If the label
287  * is naturally shorter than the specified length, the label will be padded
288  * with spaces, either on the left if pad_left is true, or on the right if
289  * pad_left is false.
290  */
get_ui_entry_label(const struct ui_entry * entry,int length,bool pad_left,wchar_t * label)291 void get_ui_entry_label(const struct ui_entry *entry, int length,
292 	bool pad_left, wchar_t *label)
293 {
294 	static bool first_call = true;
295 	static wchar_t spc[2];
296 	const wchar_t *src;
297 	int n;
298 
299 	if (first_call) {
300 		size_t nw = text_mbstowcs(spc, " ", 2);
301 
302 		assert(nw != (size_t)-1);
303 		first_call = false;
304 	}
305 
306 	if (length <= 0) {
307 		return;
308 	}
309 	if (length == 1) {
310 		label[0] = spc[1];
311 		return;
312 	}
313 	if (length <= MAX_SHORTENED + 1) {
314 		src = entry->shortened_labels[length - 2];
315 		n = entry->nshortened[length - 2];
316 	} else {
317 		src = entry->label;
318 		n = entry->nlabel;
319 	}
320 	if (n < length - 1) {
321 		int i;
322 
323 		if (pad_left) {
324 			for (i = 0; i < length - 1 - n; ++i) {
325 				label[i] = spc[0];
326 			}
327 			(void) memcpy(label + length - 1 - n, src,
328 				n * sizeof(*label));
329 		} else {
330 			(void) memcpy(label, src, n * sizeof(*label));
331 			for (i = n; i < length - 1; ++i) {
332 				label[i] = spc[0];
333 			}
334 		}
335 	} else {
336 		(void) memcpy(label, src, (length - 1) * sizeof(*label));
337 	}
338 	label[length - 1] = spc[1];
339 }
340 
341 
342 static const char *category_for_cmp_desc_prio = NULL;
cmp_desc_prio(const void * left,const void * right)343 static int cmp_desc_prio(const void *left, const void *right)
344 {
345 	const struct ui_entry *eleft =
346 		*((const struct ui_entry* const *) left);
347 	const struct ui_entry *eright =
348 		*((const struct ui_entry* const *) right);
349 	int left_ind = -1, right_ind = -1;
350 	int result;
351 
352 	if (category_for_cmp_desc_prio) {
353 		int ind;
354 
355 		if (ui_entry_search_categories(eleft,
356 			category_for_cmp_desc_prio, &ind)) {
357 			left_ind = ind;
358 		}
359 		if (ui_entry_search_categories(eright,
360 			category_for_cmp_desc_prio, &ind)) {
361 			right_ind = ind;
362 		}
363 	}
364 	if (left_ind >= 0) {
365 		if (right_ind >= 0) {
366 			if (eleft->categories[left_ind].priority >
367 				eright->categories[right_ind].priority) {
368 				result = -1;
369 			} else if (eleft->categories[left_ind].priority <
370 				   eright->categories[right_ind].priority) {
371 				result = 1;
372 			} else {
373 				result = strcmp(eleft->name, eright->name);
374 			}
375 		} else {
376 			/*
377 			 * right is not in the sort category so it should be
378 			 * pushed toward the end.
379 			 */
380 			result = -1;
381 		}
382 	} else if (right_ind >= 0) {
383 		/*
384 		 * left is not in the sort category so it should be pushed
385 		 * towards the end.
386 		 */
387 		result = 1;
388 	} else {
389 		result = strcmp(eleft->name, eright->name);
390 	}
391 	return result;
392 }
393 
394 
395 /**
396  * Constructs an iterator to enumerate all the user interface elements for
397  * which the given predicate returns true.  The iterator will present those
398  * elements in descending order of priority where the priority is that
399  * configured for the element in the category named sortcategory.
400  */
initialize_ui_entry_iterator(ui_entry_predicate predicate,void * closure,const char * sortcategory)401 struct ui_entry_iterator *initialize_ui_entry_iterator(
402 	ui_entry_predicate predicate, void *closure, const char *sortcategory)
403 {
404 	struct ui_entry_iterator *result = mem_alloc(sizeof(*result));
405 	int i;
406 
407 	result->entries = mem_alloc(n_entry * sizeof(*result->entries));
408 	result->n = 0;
409 	result->i = 0;
410 	for (i = 0; i < n_entry; ++i) {
411 		if (! (entries[i]->flags & ENTRY_FLAG_TEMPLATE_ONLY) &&
412 			(*predicate)(entries[i], closure)) {
413 			result->entries[result->n] = entries[i];
414 			++result->n;
415 		}
416 	}
417 	category_for_cmp_desc_prio = sortcategory;
418 	sort(result->entries, result->n, sizeof(*result->entries),
419 		cmp_desc_prio);
420 	return result;
421 }
422 
423 
424 /**
425  * Releases the resources allocated by a prior call to
426  * initialize_ui_entry_iterator.
427  */
release_ui_entry_iterator(struct ui_entry_iterator * i)428 void release_ui_entry_iterator(struct ui_entry_iterator *i)
429 {
430 	mem_free(i->entries);
431 	mem_free(i);
432 }
433 
434 
435 /**
436  * Resets the given iterator to the position it ahd when returned by
437  * initialize_ui_entry_iterator.
438  */
reset_ui_entry_iterator(struct ui_entry_iterator * i)439 void reset_ui_entry_iterator(struct ui_entry_iterator *i)
440 {
441 	i->i = 0;
442 }
443 
444 
445 /**
446  * Returns the number of elements remaining to be iterated for the given
447  * iterator.
448  */
count_ui_entry_iterator(struct ui_entry_iterator * i)449 int count_ui_entry_iterator(struct ui_entry_iterator *i)
450 {
451 	return i->n - i->i;
452 }
453 
454 
455 /**
456  * Returns the user interface entry currently pointed to by the iterator and
457  * advances the iterator.
458  */
advance_ui_entry_iterator(struct ui_entry_iterator * i)459 struct ui_entry *advance_ui_entry_iterator(struct ui_entry_iterator *i)
460 {
461 	struct ui_entry *result = i->entries[i->i];
462 	++i->i;
463 	return result;
464 }
465 
466 
467 /**
468  * Returns the combiner index, suitable as the first argument to
469  * ui_entry_combiner_get_funcs(), for the given user interface element.
470  */
get_ui_entry_combiner_index(const struct ui_entry * entry)471 int get_ui_entry_combiner_index(const struct ui_entry *entry)
472 {
473 	return entry->combiner_index;
474 }
475 
476 
477 /**
478  * Returns the renderer index, suitable as the first argument to
479  * ui_entry_renderer_apply(), for the given user interface element.
480  */
get_ui_entry_renderer_index(const struct ui_entry * entry)481 int get_ui_entry_renderer_index(const struct ui_entry *entry)
482 {
483 	return entry->renderer_index;
484 }
485 
486 
487 /**
488  * Returns true if the properties/abilities bound to a user interface entry
489  * correspond to a known rune.  Otherwise, returns false.
490  */
is_ui_entry_for_known_rune(const struct ui_entry * entry,const struct player * p)491 bool is_ui_entry_for_known_rune(const struct ui_entry *entry,
492 	const struct player *p)
493 {
494 	bool result = true;
495 	int i;
496 
497 	/*
498 	 * Mark it as known if all of the properties/abilities bound to the
499 	 * entry are known.
500 	 */
501 	for (i = 0; i < entry->n_obj_prop && result; ++i) {
502 		int ind = entry->obj_props[i].index;
503 
504 		switch (entry->obj_props[i].type) {
505 		case OBJ_PROPERTY_STAT:
506 		case OBJ_PROPERTY_MOD:
507 			if (p->obj_k->modifiers[ind] == 0) {
508 				result = false;
509 			}
510 			break;
511 
512 		case OBJ_PROPERTY_FLAG:
513 			if (! of_has(p->obj_k->flags, ind)) {
514 				result = false;
515 			}
516 			break;
517 
518 		case OBJ_PROPERTY_IGNORE:
519 		case OBJ_PROPERTY_RESIST:
520 		case OBJ_PROPERTY_VULN:
521 		case OBJ_PROPERTY_IMM:
522 			if (p->obj_k->el_info[ind].res_level == 0) {
523 				result = false;
524 			}
525 			break;
526 
527 		default:
528 			result = false;
529 			break;
530 		}
531 	}
532 	for (i = 0; i < entry->n_p_ability && result; ++i) {
533 		int ind = entry->p_abilities[i].ability->index;
534 
535 		if (streq(entry->p_abilities[i].ability->type, "player")) {
536 			/*
537 			 * Not so easy to associate with a rune so don't let
538 			 * it change the result.
539 			 */
540 			continue;
541 		} else if (streq(entry->p_abilities[i].ability->type,
542 			"object")) {
543 			if (! of_has(p->obj_k->flags, ind)) {
544 				result = false;
545 			}
546 		} else if (streq(entry->p_abilities[i].ability->type,
547 			"element")) {
548 			if (p->obj_k->el_info[ind].res_level == 0) {
549 				result = false;
550 			}
551 		} else {
552 			result = false;
553 		}
554 	}
555 	return result;
556 }
557 
558 
compute_ui_entry_values_for_object(const struct ui_entry * entry,const struct object * obj,const struct player * p,struct cached_object_data ** cache,int * val,int * auxval)559 void compute_ui_entry_values_for_object(const struct ui_entry *entry,
560 	const struct object *obj, const struct player *p,
561 	struct cached_object_data **cache, int *val, int *auxval)
562 {
563 	struct ui_entry_combiner_state cst = { 0, 0, 0 };
564 	struct ui_entry_combiner_funcs combiner;
565 	const struct curse_data *curse;
566 	struct cached_object_data *cache2;
567 	bool first, all_unknown, all_aux_unknown, any_aux, all_aux;
568 	int curse_ind;
569 
570 	if (!obj || !entry->n_obj_prop) {
571 		*val = UI_ENTRY_VALUE_NOT_PRESENT;
572 		*auxval = UI_ENTRY_VALUE_NOT_PRESENT;
573 		return;
574 	}
575 	if (*cache == NULL) {
576 		*cache = mem_alloc(sizeof(**cache));
577 		of_wipe((*cache)->f);
578 		object_flags_known(obj, (*cache)->f);
579 	}
580 	first = true;
581 	all_unknown = true;
582 	all_aux_unknown = true;
583 	any_aux = false;
584 	all_aux = true;
585 	if (ui_entry_combiner_get_funcs(entry->combiner_index, &combiner)) {
586 		assert(0);
587 	}
588 	cache2 = *cache;
589 	curse = obj->curses;
590 	curse_ind = 0;
591 	while (obj) {
592 		int i;
593 
594 		for (i = 0; i < entry->n_obj_prop; ++i) {
595 			int ind = entry->obj_props[i].index;
596 
597 			if (entry->obj_props[i].isaux) {
598 				if (entry->flags & ENTRY_FLAG_TIMED_AUX) {
599 					continue;
600 				}
601 				any_aux = true;
602 			} else {
603 				all_aux = false;
604 			}
605 
606 			switch (entry->obj_props[i].type) {
607 			case OBJ_PROPERTY_STAT:
608 			case OBJ_PROPERTY_MOD:
609 				if (p->obj_k->modifiers[ind] != 0 ||
610 					obj->modifiers[ind] == 0) {
611 					int v = obj->modifiers[ind];
612 					int a = 0;
613 
614 					if (v && entry->obj_props[i].have_value) {
615 						v = entry->obj_props[i].value;
616 					}
617 					if (entry->obj_props[i].isaux) {
618 						int t = a;
619 
620 						a = v;
621 						v = t;
622 						all_aux_unknown = false;
623 					} else {
624 						all_unknown = false;
625 					}
626 					if (first) {
627 						(*combiner.init_func)(v, a, &cst);
628 						first = false;
629 					} else {
630 						(*combiner.accum_func)(v, a, &cst);
631 					}
632 				}
633 				break;
634 
635 			case OBJ_PROPERTY_FLAG:
636 				if (object_flag_is_known(obj, ind)) {
637 					int v = of_has(cache2->f, ind) ? 1 : 0;
638 					int a = 0;
639 
640 					if (v && entry->obj_props[i].have_value) {
641 						v = entry->obj_props[i].value;
642 					}
643 					if (entry->obj_props[i].isaux) {
644 						int t = a;
645 
646 						a = v;
647 						v = t;
648 						all_aux_unknown = false;
649 					} else {
650 						all_unknown = false;
651 					}
652 					if (first) {
653 						(*combiner.init_func)(v, a, &cst);
654 						first = false;
655 					} else {
656 						(*combiner.accum_func)(v, a, &cst);
657 					}
658 				}
659 				break;
660 
661 			case OBJ_PROPERTY_IGNORE:
662 				if (object_element_is_known(obj, ind)) {
663 					int v = (obj->el_info[ind].flags &
664 						EL_INFO_IGNORE) ? 1 : 0;
665 					int a = 0;
666 
667 					if (v && entry->obj_props[i].have_value) {
668 						v = entry->obj_props[i].value;
669 					}
670 					if (entry->obj_props[i].isaux) {
671 						int t = a;
672 
673 						a = v;
674 						v = t;
675 						all_aux_unknown = false;
676 					} else {
677 						all_unknown = false;
678 					}
679 					if (first) {
680 						(*combiner.init_func)(v, a, &cst);
681 						first = false;
682 					} else {
683 						(*combiner.accum_func)(v, a, &cst);
684 					}
685 				}
686 				break;
687 
688 			case OBJ_PROPERTY_RESIST:
689 			case OBJ_PROPERTY_VULN:
690 			case OBJ_PROPERTY_IMM:
691 				if (object_element_is_known(obj, ind)) {
692 					int v = obj->el_info[ind].res_level;
693 					int a = 0;
694 
695 					if (v && entry->obj_props[i].have_value) {
696 						v = entry->obj_props[i].value;
697 					}
698 					if (entry->obj_props[i].isaux) {
699 						int t = a;
700 
701 						a = v;
702 						v = t;
703 						all_aux_unknown = false;
704 					} else {
705 						all_unknown = false;
706 					}
707 					if (first) {
708 						(*combiner.init_func)(v, a, &cst);
709 						first = false;
710 					} else {
711 						(*combiner.accum_func)(v, a, &cst);
712 					}
713 				}
714 				break;
715 
716 			default:
717 				break;
718 			}
719 		}
720 
721 		if (curse) {
722 			/*
723 			 * Proceed to the next unprocessed curse object.
724 			 * Don't overwrite the cached data for the base.
725 			 */
726 			obj = NULL;
727 			if (curse_ind == 0) {
728 				cache2 = mem_alloc(sizeof(*cache2));
729 			}
730 			++curse_ind;
731 			while (1) {
732 				if (curse_ind >= z_info->curse_max) {
733 					mem_free(cache2);
734 					break;
735 				}
736 				if (curse[curse_ind].power) {
737 					obj = curses[curse_ind].obj;
738 					of_wipe(cache2->f);
739 					object_flags_known(obj, cache2->f);
740 					break;
741 				}
742 				++curse_ind;
743 			}
744 		} else {
745 			obj = NULL;
746 		}
747 	}
748 	if (all_unknown && all_aux_unknown) {
749 		*val = (all_aux) ? 0 : UI_ENTRY_UNKNOWN_VALUE;
750 		*auxval = (any_aux) ? UI_ENTRY_UNKNOWN_VALUE : 0;
751 	} else {
752 		(*combiner.finish_func)(&cst);
753 		if (all_unknown) {
754 			*val = (all_aux) ? 0 : UI_ENTRY_UNKNOWN_VALUE;
755 		} else {
756 			*val = cst.accum;
757 		}
758 		if (all_aux_unknown) {
759 			*auxval = (any_aux) ? UI_ENTRY_UNKNOWN_VALUE : 0;
760 		} else {
761 			*auxval = cst.accum_aux;
762 		}
763 	}
764 }
765 
766 
compute_ui_entry_values_for_player(const struct ui_entry * entry,struct player * p,struct cached_player_data ** cache,int * val,int * auxval)767 void compute_ui_entry_values_for_player(const struct ui_entry *entry,
768 	struct player *p, struct cached_player_data **cache, int *val,
769 	int *auxval)
770 {
771 	struct ui_entry_combiner_state cst = { 0, 0, 0 };
772 	struct ui_entry_combiner_funcs combiner;
773 	bool first;
774 	int i;
775 
776 	if (!p) {
777 		*val = UI_ENTRY_VALUE_NOT_PRESENT;
778 		*auxval = UI_ENTRY_VALUE_NOT_PRESENT;
779 		return;
780 	}
781 	if (*cache == NULL) {
782 		*cache = mem_alloc(sizeof(**cache));
783 		player_flags(p, (*cache)->untimed);
784 		of_wipe((*cache)->timed);
785 		player_flags_timed(p, (*cache)->timed);
786 		if (p->timed[TMD_TRAPSAFE]) {
787 			of_on((*cache)->timed, OF_TRAP_IMMUNE);
788 		}
789 	}
790 	first = true;
791 	if (ui_entry_combiner_get_funcs(entry->combiner_index, &combiner)) {
792 		assert(0);
793 	}
794 	for (i = 0; i < entry->n_p_ability; ++i) {
795 		int ind = entry->p_abilities[i].ability->index;
796 
797 		if ((entry->flags & ENTRY_FLAG_TIMED_AUX) &&
798 			entry->p_abilities[i].isaux) {
799 			continue;
800 		}
801 		if (streq(entry->p_abilities[i].ability->type, "player")) {
802 			if (! player_has(p, ind)) {
803 				continue;
804 			}
805 			if (entry->p_abilities[i].have_value) {
806 				int v = entry->p_abilities[i].value;
807 				int a = UI_ENTRY_VALUE_NOT_PRESENT;
808 
809 				if (entry->p_abilities[i].isaux) {
810 					int t = v;
811 
812 					v = a;
813 					a = t;
814 				}
815 				if (first) {
816 					(*combiner.init_func)(v, a, &cst);
817 					first = false;
818 				} else {
819 					(*combiner.accum_func)(v, a, &cst);
820 				}
821 			} else {
822 				int v, a;
823 				struct object *launcher;
824 
825 				/*
826 				 * Handle player abilities that did not bind a
827 				 * value to the user interface element as
828 				 * special cases.
829 				 */
830 				switch (ind) {
831 				case PF_FAST_SHOT:
832 					launcher = equipped_item_by_slot_name(
833 						p, "shooting");
834 					if (launcher && kf_has(launcher->kind->kind_flags,
835 						KF_SHOOTS_ARROWS)) {
836 						v = p->lev / 3;
837 						a = 0;
838 					} else {
839 						v = 0;
840 						a = 0;
841 					}
842 					if (entry->p_abilities[i].isaux) {
843 						int t = v;
844 
845 						v = a;
846 						a = t;
847 					}
848 					if (first) {
849 						(*combiner.init_func)(v, a, &cst);
850 						first = false;
851 					} else {
852 						(*combiner.accum_func)(v, a, &cst);
853 					}
854 					break;
855 
856 				case PF_BRAVERY_30:
857 					/*
858 					 * player_flags() accounts for
859 					 * PF_BRAVERY_30 so this is only
860 					 * necessary in cases where
861 					 * OF_PROT_FEAR isn't also bound to
862 					 * the element.
863 					 */
864 					v = (p->lev >= 30) ? 1 : 0;
865 					a = 0;
866 					if (entry->p_abilities[i].isaux) {
867 						int t = v;
868 
869 						v = a;
870 						a = t;
871 					}
872 					if (first) {
873 						(*combiner.init_func)(v, a, &cst);
874 						first = false;
875 					} else {
876 						(*combiner.accum_func)(v, a, &cst);
877 					}
878 					break;
879 				}
880 			}
881 		} else if (streq(entry->p_abilities[i].ability->type,
882 			"object")) {
883 			int v = of_has((*cache)->untimed, ind) ? 1 : 0;
884 			int a;
885 
886 			if (entry->flags & ENTRY_FLAG_TIMED_AUX) {
887 				a = of_has((*cache)->timed, ind) ? 1 : 0;
888 			} else {
889 				a = 0;
890 			}
891 			if (entry->p_abilities[i].isaux) {
892 				int t = v;
893 
894 				v = a;
895 				a = t;
896 			}
897 			if (first) {
898 				(*combiner.init_func)(v, a, &cst);
899 				first = false;
900 			} else {
901 				(*combiner.accum_func)(v, a, &cst);
902 			}
903 
904 			v = of_has(p->shape->flags, ind) ? 1 : 0;
905 			a = 0;
906 			if (v && of_has(p->obj_k->flags, ind)) {
907 				if (entry->p_abilities[i].isaux) {
908 					int t = v;
909 
910 					v = a;
911 					a = t;
912 				}
913 				(*combiner.accum_func)(v, a, &cst);
914 			}
915 		} else if (streq(entry->p_abilities[i].ability->type,
916 			"element")) {
917 			int v = p->race->el_info[ind].res_level;
918 			int a;
919 
920 			if (entry->flags & ENTRY_FLAG_TIMED_AUX) {
921 				a = get_timed_element_effect(p, ind);
922 			} else {
923 				a = 0;
924 			}
925 			if (entry->p_abilities[i].isaux) {
926 				int t = v;
927 
928 				v = a;
929 				a = t;
930 			}
931 			if (first) {
932 				(*combiner.init_func)(v, a, &cst);
933 				first = false;
934 			} else {
935 				(*combiner.accum_func)(v, a, &cst);
936 			}
937 			v = p->shape->el_info[ind].res_level;
938 			a = 0;
939 			if (v != 0 && p->obj_k->el_info[ind].res_level) {
940 				if (entry->p_abilities[i].isaux) {
941 					int t = v;
942 
943 					v = a;
944 					a = t;
945 				}
946 				(*combiner.accum_func)(v, a, &cst);
947 			}
948 		}
949 	}
950 	/*
951 	 * Since stats and modifiers aren't stored in the ability list, check
952 	 * if any object properties for those are bound to this element.
953 	 * Then lookup the player's intrinsic values for those.
954 	 */
955 	for (i = 0; i < entry->n_obj_prop; ++i) {
956 		int ind = entry->obj_props[i].index;
957 		int skill_ind, skill_cnv_num, skill_cnv_den;
958 		int v, a;
959 
960 		if (entry->obj_props[i].isaux &&
961 			(entry->flags & ENTRY_FLAG_TIMED_AUX)) {
962 			continue;
963 		}
964 		switch (entry->obj_props[i].type) {
965 		case OBJ_PROPERTY_STAT:
966 		case OBJ_PROPERTY_MOD:
967 			v = p->shape->modifiers[ind];
968 			if (entry->flags & ENTRY_FLAG_TIMED_AUX) {
969 				a = get_timed_modifier_effect(p, ind);
970 			} else {
971 				a = 0;
972 			}
973 			if (entry->obj_props[i].isaux) {
974 				int t = v;
975 
976 				v = a;
977 				a = t;
978 			}
979 			if (first) {
980 				(*combiner.init_func)(v, a, &cst);
981 				first = false;
982 			} else {
983 				(*combiner.accum_func)(v, a, &cst);
984 			}
985 			/*
986 			 * Racial information doesn't store modifiers but does
987 			 * store skills.  If applicable, extract the relevant
988 			 * value and convert.
989 			 */
990 			modifier_to_skill(ind, &skill_ind, &skill_cnv_num,
991 				&skill_cnv_den);
992 			if (skill_ind >= 0) {
993 				v = (p->race->r_skills[skill_ind] *
994 					skill_cnv_num) / skill_cnv_den;
995 				a = 0;
996 				if (entry->obj_props[i].isaux) {
997 					int t = v;
998 
999 					v = a;
1000 					a = t;
1001 				}
1002 				(*combiner.accum_func)(v, a, &cst);
1003 			}
1004 			/*
1005 			 * Uggh, the player race handles infravision
1006 			 * separately.
1007 			 */
1008 			if (ind == OBJ_MOD_INFRA) {
1009 				v = p->race->infra;
1010 				a = 0;
1011 				if (entry->obj_props[i].isaux) {
1012 					int t = v;
1013 
1014 					v = a;
1015 					a = t;
1016 				}
1017 				(*combiner.accum_func)(v, a, &cst);
1018 			}
1019 			break;
1020 		}
1021 	}
1022 	if (first) {
1023 		*val = UI_ENTRY_VALUE_NOT_PRESENT;
1024 		*auxval = UI_ENTRY_VALUE_NOT_PRESENT;
1025 	} else {
1026 		(*combiner.finish_func)(&cst);
1027 		*val = cst.accum;
1028 		*auxval = cst.accum_aux;
1029 	}
1030 }
1031 
1032 
release_cached_object_data(struct cached_object_data * cache)1033 void release_cached_object_data(struct cached_object_data *cache)
1034 {
1035 	mem_free(cache);
1036 }
1037 
1038 
release_cached_player_data(struct cached_player_data * cache)1039 void release_cached_player_data(struct cached_player_data *cache)
1040 {
1041 	mem_free(cache);
1042 }
1043 
1044 
1045 /**
1046  * Lookup the entry by name.  Return a nonzero value if present and set *ind to
1047  * its index.  Otherwise return zero and set *ind to where it should be
1048  * inserted.
1049  */
ui_entry_search(const char * name,int * ind)1050 static int ui_entry_search(const char *name, int *ind)
1051 {
1052 	/* They're sorted by name so use a binary search. */
1053 	int ilow = 0, ihigh = n_entry;
1054 
1055 	while (1) {
1056 		int imid, cmp;
1057 
1058 		if (ilow == ihigh) {
1059 			*ind = ilow;
1060 			return 0;
1061 		}
1062 		imid = (ilow + ihigh) / 2;
1063 		cmp = strcmp(entries[imid]->name, name);
1064 		if (cmp == 0) {
1065 			*ind = imid;
1066 			return 1;
1067 		}
1068 		if (cmp < 0) {
1069 			ilow = imid + 1;
1070 		} else {
1071 			ihigh = imid;
1072 		}
1073 	}
1074 }
1075 
1076 
1077 /**
1078  * Lookup the entry by name.  If present return its index + 1.  Otherwise,
1079  * return 0.
1080  */
ui_entry_lookup(const char * name)1081 static int ui_entry_lookup(const char *name)
1082 {
1083 	int ind;
1084 
1085 	return ui_entry_search(name, &ind) ? ind + 1 : 0;
1086 }
1087 
1088 
1089 /**
1090  * Insert the entry.
1091  */
ui_entry_insert(struct ui_entry * entry)1092 static void ui_entry_insert(struct ui_entry *entry)
1093 {
1094 	int ind;
1095 
1096 	if (ui_entry_search(entry->name, &ind)) {
1097 		quit("Attempted to insert ui_entry with same name");
1098 	}
1099 
1100 	if (n_entry == nalloc_entry) {
1101 		struct ui_entry **extended;
1102 
1103 		if (nalloc_entry > INT_MAX / 2) {
1104 			quit("Too many ui_entry");
1105 		}
1106 		nalloc_entry = (nalloc_entry == 0) ? 8 : 2 * nalloc_entry;
1107 		extended = mem_alloc(nalloc_entry * sizeof(*extended));
1108 		if (ind > 0) {
1109 			(void) memcpy(extended, entries,
1110 				ind * sizeof(*entries));
1111 		}
1112 		extended[ind] = entry;
1113 		if (ind < n_entry) {
1114 			(void) memcpy(extended + ind + 1, entries + ind,
1115 				(n_entry - ind) * sizeof(*entries));
1116 		}
1117 		mem_free(entries);
1118 		entries = extended;
1119 	} else {
1120 		int i;
1121 
1122 		for (i = n_entry; i > ind; --i) {
1123 			entries[i] = entries[i - 1];
1124 		}
1125 		entries[ind] = entry;
1126 	}
1127 	++n_entry;
1128 }
1129 
1130 
1131 /**
1132  * Search for name in the categories associated with ui_entry.  Return a
1133  * nonzero value if present and set *ind to its index.  Otherwise, return zero
1134  * and set *ind to where it should be inserted.
1135  */
ui_entry_search_categories(const struct ui_entry * entry,const char * name,int * ind)1136 static int ui_entry_search_categories(const struct ui_entry *entry,
1137 	const char *name, int *ind)
1138 {
1139 	/* They're sorted by name so use a binary search. */
1140 	int ilow = 0, ihigh = entry->n_category;
1141 
1142 	while (1) {
1143 		int imid, cmp;
1144 
1145 		if (ilow == ihigh) {
1146 			*ind = ilow;
1147 			return 0;
1148 		}
1149 		imid = (ilow + ihigh) / 2;
1150 		cmp = strcmp(entry->categories[imid].name, name);
1151 		if (cmp == 0) {
1152 			*ind = imid;
1153 			return 1;
1154 		}
1155 		if (cmp < 0) {
1156 			ilow = imid + 1;
1157 		} else {
1158 			ihigh = imid;
1159 		}
1160 	}
1161 }
1162 
1163 
modifier_to_skill(int modind,int * skillind,int * skill2mod_num,int * skill2mod_den)1164 static void modifier_to_skill(int modind, int *skillind, int *skill2mod_num,
1165 	int *skill2mod_den
1166 )
1167 {
1168 	switch (modind) {
1169 	case OBJ_MOD_TUNNEL:
1170 		*skillind = SKILL_DIGGING;
1171 		*skill2mod_num = 1;
1172 		*skill2mod_den = 20;
1173 		break;
1174 
1175 	/*
1176 	 * Stealth and searching also have skills, but the calculations
1177 	 * formerly in ui-player.c didn't include the racial contribution from
1178 	 * those in what was shown in the second player screen.  If that's
1179 	 * desirable, these are the conversion factors used in player-calcs.c
1180 	 */
1181 #if 0
1182 	case OBJ_MOD_STEALTH:
1183 		*skillind = SKILL_STEALTH;
1184 		*skill2mod_num = 1;
1185 		*skill2mod_den = 1;
1186 		break;
1187 
1188 	case OBJ_MOD_SEARCH:
1189 		*skillind = SKILL_SEARCH;
1190 		*skill2mod_num = 1;
1191 		*skill2mod_den = 5;
1192 		break;
1193 #endif
1194 
1195 	default:
1196 		*skillind = -1;
1197 		*skill2mod_num = 1;
1198 		*skill2mod_den = 1;
1199 		break;
1200 	}
1201 }
1202 
1203 
get_timed_element_effect(const struct player * p,int ind)1204 static int get_timed_element_effect(const struct player *p, int ind)
1205 {
1206 	int result;
1207 
1208 	switch (ind) {
1209 	case ELEM_ACID:
1210 		result = p->timed[TMD_OPP_ACID] ? 1 : 0;
1211 		break;
1212 
1213 	case ELEM_ELEC:
1214 		result = p->timed[TMD_OPP_ELEC] ? 1 : 0;
1215 		break;
1216 
1217 	case ELEM_FIRE:
1218 		result = p->timed[TMD_OPP_FIRE] ? 1 : 0;
1219 		break;
1220 
1221 	case ELEM_COLD:
1222 		result = p->timed[TMD_OPP_COLD] ? 1 : 0;
1223 		break;
1224 
1225 	case ELEM_POIS:
1226 		result = p->timed[TMD_OPP_POIS] ? 1 : 0;
1227 		break;
1228 
1229 	default:
1230 		result = 0;
1231 		break;
1232 	}
1233 	return result;
1234 }
1235 
1236 
get_timed_modifier_effect(const struct player * p,int ind)1237 static int get_timed_modifier_effect(const struct player *p, int ind)
1238 {
1239 	int result;
1240 
1241 	/* Mimics calculations made in player-calcs.c. */
1242 	switch (ind) {
1243 	case OBJ_MOD_BLOWS:
1244 		result = (p->timed[TMD_BLOODLUST]) ?
1245 			p->timed[TMD_BLOODLUST] / 20 : 0;
1246 		break;
1247 
1248 	case OBJ_MOD_INFRA:
1249 		result = (p->timed[TMD_SINFRA]) ? 5 : 0;
1250 		break;
1251 
1252 	case OBJ_MOD_SPEED:
1253 		result = (p->timed[TMD_FAST] || p->timed[TMD_SPRINT]) ? 10 : 0;
1254 		if (p->timed[TMD_STONESKIN]) {
1255 			result -= 5;
1256 		}
1257 		if (p->timed[TMD_SLOW]) {
1258 			result -= 10;
1259 		}
1260 		if (p->timed[TMD_TERROR]) {
1261 			result += 10;
1262 		}
1263 		break;
1264 
1265 	case OBJ_MOD_STEALTH:
1266 		result = (p->timed[TMD_STEALTH]) ? 10 : 0;
1267 		break;
1268 
1269 	default:
1270 		result = 0;
1271 		break;
1272 	}
1273 	return result;
1274 }
1275 
1276 
1277 /**
1278  * Search for name in the categories associated with ui_entry being parsed.
1279  * Return a nonzero value if present and set *ind to its index.  Otherwise,
1280  * return zero and set *ind to where it should be inserted.
1281  */
search_embryo_categories(const struct embryonic_ui_entry * embryo,const char * name,int * ind)1282 static int search_embryo_categories(const struct embryonic_ui_entry *embryo,
1283 	const char *name, int *ind)
1284 {
1285 	int ilow, ihigh;
1286 
1287 	if (embryo->exists) {
1288 		return ui_entry_search_categories(embryo->entry, name, ind);
1289 	}
1290 
1291 	/* They're sorted by name so use a binary search. */
1292 	ilow = 0;
1293 	ihigh = embryo->entry->n_category;
1294 	while (1) {
1295 		int imid, cmp;
1296 
1297 		if (ilow == ihigh) {
1298 			*ind = ilow;
1299 			return 0;
1300 		}
1301 		imid = (ilow + ihigh) / 2;
1302 		cmp = strcmp(embryo->categories[imid].name, name);
1303 		if (cmp == 0) {
1304 			*ind = imid;
1305 			return 1;
1306 		}
1307 		if (cmp < 0) {
1308 			ilow = imid + 1;
1309 		} else {
1310 			ihigh = imid;
1311 		}
1312 	}
1313 }
1314 
1315 
1316 /**
1317  * Search the list of all categories seen so far for a name.  Return a
1318  * nonzero value if found and set *ind to its index.  Otherwise, return zero
1319  * and set *ind to where it should be inserted.
1320  */
search_categories(const char * name,int * ind)1321 static int search_categories(const char *name, int *ind)
1322 {
1323 	int ilow = 0, ihigh = n_category;
1324 
1325 	while (1) {
1326 		int imid, cmp;
1327 
1328 		if (ilow == ihigh) {
1329 			*ind = ilow;
1330 			return 0;
1331 		}
1332 		imid = (ilow + ihigh) / 2;
1333 		cmp = strcmp(categories[imid], name);
1334 		if (cmp == 0) {
1335 			*ind = imid;
1336 			return 1;
1337 		}
1338 		if (cmp < 0) {
1339 			ilow = imid + 1;
1340 		} else {
1341 			ihigh = imid;
1342 		}
1343 	}
1344 }
1345 
1346 
1347 /**
1348  * Insert a category with (name, priority) into the ui_entry being parsed.
1349  * Use ind as the insertion point.
1350  */
insert_embryo_category(struct embryonic_ui_entry * embryo,const char * name,int psource_index,int priority,bool priority_set,int ind)1351 static void insert_embryo_category(struct embryonic_ui_entry *embryo,
1352 	const char *name, int psource_index, int priority, bool priority_set,
1353 	int ind)
1354 {
1355 	int cind;
1356 
1357 	if (! search_categories(name, &cind)) {
1358 		if (n_category == nalloc_category) {
1359 			char **extended;
1360 
1361 			if (n_category > INT_MAX / 2) {
1362 				quit("Too many categories");
1363 			}
1364 			nalloc_category = (nalloc_category == 0) ?
1365 				8 : nalloc_category * 2;
1366 			extended = mem_alloc(nalloc_category *
1367 				sizeof(*extended));
1368 			if (cind > 0) {
1369 				memcpy(extended, categories,
1370 					cind * sizeof(*extended));
1371 			}
1372 			extended[cind] = string_make(name);
1373 			if (cind < n_category) {
1374 				memcpy(extended + cind + 1, categories + cind,
1375 					(n_category - cind) *
1376 					sizeof(*extended));
1377 			}
1378 			mem_free(categories);
1379 			categories = extended;
1380 		} else {
1381 			int i;
1382 
1383 			for (i = n_category; i > cind; --i) {
1384 				categories[i] = categories[i - 1];
1385 			}
1386 			categories[cind] = string_make(name);
1387 		}
1388 		++n_category;
1389 	}
1390 	if (embryo->entry->n_category == embryo->entry->nalloc_category) {
1391 		if (embryo->entry->nalloc_category > INT_MAX / 2) {
1392 			quit("Too many categories for an ui_entry");
1393 		}
1394 		embryo->entry->nalloc_category =
1395 			(embryo->entry->nalloc_category == 0) ?
1396 			4 : 2 * embryo->entry->nalloc_category;
1397 		if (embryo->exists) {
1398 			struct category_reference *extended =
1399 				mem_alloc(embryo->entry->nalloc_category *
1400 				sizeof(*extended));
1401 
1402 			if (ind > 0) {
1403 				(void) memcpy(extended,
1404 					embryo->entry->categories,
1405 					ind * sizeof(*extended));
1406 			}
1407 			extended[ind].name = categories[cind];
1408 			extended[ind].priority = priority;
1409 			extended[ind].priority_set = priority_set;
1410 			if (ind < embryo->entry->n_category) {
1411 				(void) memcpy(extended + ind + 1,
1412 					embryo->entry->categories + ind,
1413 					(embryo->entry->n_category - ind) *
1414 					sizeof(*extended));
1415 			}
1416 			mem_free(embryo->entry->categories);
1417 			embryo->entry->categories = extended;
1418 		} else {
1419 			struct embryonic_category_reference *extended =
1420 				mem_alloc(embryo->entry->nalloc_category *
1421 				sizeof(*extended));
1422 
1423 			if (ind > 0) {
1424 				(void) memcpy(extended,
1425 					embryo->categories,
1426 					ind * sizeof(*extended));
1427 			}
1428 			extended[ind].name = categories[cind];
1429 			extended[ind].psource_index = psource_index;
1430 			extended[ind].priority = priority;
1431 			extended[ind].priority_set = priority_set;
1432 			if (ind < embryo->entry->n_category) {
1433 				(void) memcpy(extended + ind + 1,
1434 					embryo->categories + ind,
1435 					(embryo->entry->n_category - ind) *
1436 					sizeof(*extended));
1437 			}
1438 			mem_free(embryo->categories);
1439 			embryo->categories = extended;
1440 		}
1441 	} else {
1442 		int i;
1443 
1444 		if (embryo->exists) {
1445 			for (i = embryo->entry->n_category; i > ind; --i) {
1446 				embryo->entry->categories[i] =
1447 					embryo->entry->categories[i - 1];
1448 			}
1449 			embryo->entry->categories[ind].name = categories[cind];
1450 			embryo->entry->categories[ind].priority = priority;
1451 			embryo->entry->categories[ind].priority_set =
1452 				priority_set;
1453 		} else {
1454 			for (i = embryo->entry->n_category; i > ind; --i) {
1455 				embryo->categories[i] =
1456 					embryo->categories[i - 1];
1457 			}
1458 			embryo->categories[ind].name = categories[cind];
1459 			embryo->categories[ind].psource_index = psource_index;
1460 			embryo->categories[ind].priority = priority;
1461 			embryo->categories[ind].priority_set = priority_set;
1462 		}
1463 	}
1464 	++embryo->entry->n_category;
1465 }
1466 
1467 
1468 /* These are for handling unparameterized entry names. */
get_dummy_param_count(void)1469 static int get_dummy_param_count(void)
1470 {
1471 	return 1;
1472 }
1473 
1474 
get_dummy_param_name(int i)1475 static const char *get_dummy_param_name(int i)
1476 {
1477 	assert(i == 0);
1478 	return "";
1479 }
1480 
1481 
1482 /* These are for handling of entries parameterized by the element name. */
1483 static const char *element_names[] = {
1484 	#define ELEM(x) #x,
1485 	#include "list-elements.h"
1486 	#undef ELEM
1487 };
1488 
1489 
get_element_count(void)1490 static int get_element_count(void)
1491 {
1492 	return N_ELEMENTS(element_names);
1493 }
1494 
1495 
get_element_name(int i)1496 static const char *get_element_name(int i)
1497 {
1498 	assert(i >= 0 && i < get_element_count());
1499 	return element_names[i];
1500 }
1501 
1502 
1503 /* These are for handling of entries parameterized by the stat name. */
1504 static const char *stat_names[] = {
1505 	#define STAT(x) #x,
1506 	#include "list-stats.h"
1507 	#undef STAT
1508 };
1509 
1510 
get_stat_count(void)1511 static int get_stat_count(void)
1512 {
1513 	return N_ELEMENTS(stat_names);
1514 }
1515 
1516 
get_stat_name(int i)1517 static const char *get_stat_name(int i)
1518 {
1519 	assert(i >= 0 && i < get_stat_count());
1520 	return stat_names[i];
1521 }
1522 
1523 
1524 /*
1525  * For handling of automatically setting the priority based on the parameter
1526  * indexing.
1527  */
get_dummy_priority(int i)1528 static int get_dummy_priority(int i)
1529 {
1530 	return 0;
1531 }
1532 
1533 
get_priority_from_index(int i)1534 static int get_priority_from_index(int i)
1535 {
1536 	return i;
1537 }
1538 
1539 
get_priority_from_negative_index(int i)1540 static int get_priority_from_negative_index(int i)
1541 {
1542 	return -i;
1543 }
1544 
1545 
1546 /* Convert list of categories in the embryo to its final form. */
parameterize_category_list(const struct embryonic_category_reference * categories,int n,int ind,struct ui_entry * entry)1547 static void parameterize_category_list(
1548 	const struct embryonic_category_reference *categories, int n, int ind,
1549 	struct ui_entry *entry)
1550 {
1551 	int i;
1552 
1553 	entry->categories = mem_alloc(n * sizeof(*entry->categories));
1554 	entry->n_category = n;
1555 	entry->nalloc_category = n;
1556 
1557 	for (i = 0; i < n; ++i) {
1558 		entry->categories[i].name = categories[i].name;
1559 		if (categories[i].priority_set) {
1560 			entry->categories[i].priority =
1561 				(categories[i].psource_index > 0) ?
1562 				(*priority_schemes[categories[i].psource_index].priority)(ind) :
1563 				categories[i].priority;
1564 			entry->categories[i].priority_set = true;
1565 		} else {
1566 			entry->categories[i].priority = 0;
1567 			entry->categories[i].priority_set = false;
1568 		}
1569 	}
1570 }
1571 
1572 
initialize_shortened(struct ui_entry * entry)1573 static void initialize_shortened(struct ui_entry *entry)
1574 {
1575 	int i;
1576 
1577 	assert(MAX_SHORTENED > 0);
1578 	entry->shortened_labels[0] = entry->shortened_buffer;
1579 	for (i = 1; i < MAX_SHORTENED; ++i) {
1580 		entry->shortened_labels[i] =
1581 			entry->shortened_labels[i - 1] + i + 1;
1582 	}
1583 	for (i = 0; i < MAX_SHORTENED; ++i) {
1584 		entry->nshortened[i] = 0;
1585 	}
1586 }
1587 
1588 
copy_shortened_labels(struct ui_entry * dest,const struct ui_entry * src)1589 static void copy_shortened_labels(struct ui_entry *dest,
1590 	const struct ui_entry *src)
1591 {
1592 	int i;
1593 
1594 	for (i = 0; i < MAX_SHORTENED; ++i) {
1595 		dest->shortened_labels[i] = (i == 0) ? dest->shortened_buffer :
1596 			dest->shortened_labels[i - 1] + i + 1;
1597 		dest->nshortened[i] = src->nshortened[i];
1598 		if (src->nshortened[i] > 0) {
1599 			(void) memcpy(dest->shortened_labels[i],
1600 				src->shortened_labels[i],
1601 				(src->nshortened[i] + 1) *
1602 				sizeof(*dest->shortened_labels[i]));
1603 		}
1604 	}
1605 }
1606 
1607 
fill_out_shortened(struct ui_entry * entry)1608 static void fill_out_shortened(struct ui_entry *entry)
1609 {
1610 	int i;
1611 
1612 	for (i = 0; i < MAX_SHORTENED; ++i) {
1613 		int j;
1614 		int n;
1615 		const wchar_t *src;
1616 
1617 		if (entry->nshortened[i] != 0) {
1618 			continue;
1619 		}
1620 		/*
1621 		 * Is there a longer abbreviated label already set?  Use it
1622 		 * as the basis for this one.  Otherwise, use the full label.
1623 		 */
1624 		j = i + 1;
1625 		while (1) {
1626 			if (j >= MAX_SHORTENED) {
1627 				n = entry->nlabel;
1628 				src = entry->label;
1629 				break;
1630 			}
1631 			if (entry->nshortened[j] != 0) {
1632 				n = entry->nshortened[j];
1633 				src = entry->shortened_labels[j];
1634 				break;
1635 			}
1636 			++j;
1637 		}
1638 		entry->nshortened[i] = (n < i + 1) ? n : i + 1;
1639 		(void) memcpy(entry->shortened_labels[i], src,
1640 			(entry->nshortened[i] + 1) *
1641 			sizeof(*entry->shortened_labels[i]));
1642 	}
1643 }
1644 
1645 
hatch_embryo(struct embryonic_ui_entry * embryo)1646 static int hatch_embryo(struct embryonic_ui_entry *embryo)
1647 {
1648 	/* If was editing an existing entry, do not have to recommit it. */
1649 	if (! embryo->exists) {
1650 		int n, i;
1651 
1652 		/* Check that required fields are valid. */
1653 		if (embryo->entry->combiner_index == 0) {
1654 			string_free(embryo->entry->name);
1655 			mem_free(embryo->entry->label);
1656 			mem_free(embryo->entry);
1657 			mem_free(embryo->categories);
1658 			return 1;
1659 		}
1660 
1661 		/* Parameterize the name generating multiple entries. */
1662 		n = (*name_parameters[embryo->param_index].count_func)();
1663 		for (i = 0; i < n - 1; ++i) {
1664 			struct ui_entry *entry = mem_alloc(sizeof(*entry));
1665 			const char *name = (*name_parameters[embryo->param_index].ith_name_func)(i);
1666 			entry->name = string_make(
1667 				format("%s<%s>", embryo->entry->name, name));
1668 			if (embryo->psource_index != 0) {
1669 				entry->default_priority =
1670 					(*priority_schemes[embryo->psource_index].priority)(i);
1671 			} else {
1672 				entry->default_priority =
1673 					embryo->entry->default_priority;
1674 			}
1675 			parameterize_category_list(
1676 				embryo->categories, embryo->entry->n_category,
1677 				i, entry);
1678 			entry->obj_props = NULL;
1679 			entry->p_abilities = NULL;
1680 			if (embryo->entry->nlabel > 0) {
1681 				size_t sz = (embryo->entry->nlabel + 1) *
1682 					sizeof(*entry->label);
1683 
1684 				entry->label = mem_alloc(sz);
1685 				(void) memcpy(entry->label,
1686 					embryo->entry->label, sz);
1687 				entry->nlabel = embryo->entry->nlabel;
1688 			} else {
1689 				size_t nw;
1690 
1691 				entry->label = mem_alloc(
1692 					(MAX_ENTRY_LABEL + 1) *
1693 					sizeof(*entry->label));
1694 				nw = text_mbstowcs(entry->label, name,
1695 					MAX_ENTRY_LABEL);
1696 				/* Ensure null termination. */
1697 				if (nw != (size_t)-1 &&
1698 					text_mbstowcs(entry->label + nw, "", 1) != (size_t)-1) {
1699 					entry->nlabel = nw;
1700 				}
1701 			}
1702 			copy_shortened_labels(entry, embryo->entry);
1703 			entry->renderer_index = embryo->entry->renderer_index;
1704 			entry->combiner_index = embryo->entry->combiner_index;
1705 			entry->param_index = i;
1706 			entry->flags = embryo->entry->flags;
1707 			entry->n_obj_prop = 0;
1708 			entry->nalloc_obj_prop = 0;
1709 			entry->n_p_ability = 0;
1710 			entry->nalloc_p_ability = 0;
1711 			ui_entry_insert(entry);
1712 		}
1713 		/*
1714 		 * For the last one, reuse the structure stored in the embryo
1715 		 * to save a bit of effort.
1716 		 */
1717 		if (embryo->param_index == 0) {
1718 			/* Not parameterized */
1719 			embryo->entry->param_index = -1;
1720 		} else {
1721 			const char *name = (*name_parameters[embryo->param_index].ith_name_func)(n - 1);
1722 			char *entnm = string_make(
1723 				format("%s<%s>", embryo->entry->name, name));
1724 
1725 			string_free(embryo->entry->name);
1726 			embryo->entry->name = entnm;
1727 			if (embryo->entry->nlabel == 0) {
1728 				size_t nw;
1729 
1730 				embryo->entry->label = mem_alloc(
1731 					(MAX_ENTRY_LABEL + 1) *
1732 					sizeof(*embryo->entry->label));
1733 				nw = text_mbstowcs(embryo->entry->label, name,
1734 					MAX_ENTRY_LABEL);
1735 				/* Ensure null termination. */
1736 				if (nw != (size_t)-1 &&
1737 					text_mbstowcs(embryo->entry->label + nw, "", 1) != (size_t)-1) {
1738 					embryo->entry->nlabel = nw;
1739 				}
1740 			}
1741 			embryo->entry->param_index = n - 1;
1742 			if (embryo->psource_index != 0) {
1743 				embryo->entry->default_priority =
1744 					(*priority_schemes[embryo->psource_index].priority)(n - 1);
1745 			}
1746 		}
1747 		parameterize_category_list(embryo->categories,
1748 			embryo->entry->n_category, n - 1, embryo->entry);
1749 		ui_entry_insert(embryo->entry);
1750 	}
1751 	mem_free(embryo->categories);
1752 	return 0;
1753 }
1754 
1755 
hatch_last_embryo(struct parser * p)1756 static int hatch_last_embryo(struct parser *p)
1757 {
1758 	struct embryonic_ui_entry *embryo = parser_priv(p);
1759 	int result = 0;
1760 
1761 	if (embryo) {
1762 		if (hatch_embryo(embryo)) {
1763 			result = 1;
1764 		}
1765 		mem_free(embryo);
1766 		parser_setpriv(p, NULL);
1767 	}
1768 	return result;
1769 }
1770 
1771 
parse_entry_name(struct parser * p)1772 static enum parser_error parse_entry_name(struct parser *p)
1773 {
1774 	const char *name = parser_getstr(p, "name");
1775 	struct embryonic_ui_entry *embryo = parser_priv(p);
1776 	int ind;
1777 
1778 	if (embryo) {
1779 		if (streq(name, embryo->entry->name)) {
1780 			/*
1781 			 * Strange case since the last record is the same as
1782 			 * the this one.  Simply proceeed to modify the
1783 			 * previous record without a warning.
1784 			 */
1785 			return PARSE_ERROR_NONE;
1786 		}
1787 		if (hatch_embryo(embryo)) {
1788 			mem_free(embryo);
1789 			parser_setpriv(p, NULL);
1790 			return PARSE_ERROR_INVALID_VALUE;
1791 		}
1792 	} else {
1793 		embryo = mem_alloc(sizeof(*embryo));
1794 	}
1795 
1796 	parser_setpriv(p, embryo);
1797 	ind = ui_entry_lookup(name);
1798 	if (ind > 0) {
1799 		/*
1800 		 * Modify an already existing record.  Recall it, convert it
1801 		 * back to embryonic form, and mark it as already existing.
1802 		 */
1803 		embryo->entry = entries[ind - 1];
1804 		embryo->exists = true;
1805 	} else {
1806 		/* Make a completely new entry. */
1807 		embryo->entry = mem_alloc(sizeof(*embryo->entry));
1808 		embryo->entry->name = string_make(name);
1809 		embryo->entry->categories = NULL;
1810 		embryo->entry->obj_props = NULL;
1811 		embryo->entry->p_abilities = NULL;
1812 		embryo->entry->label = NULL;
1813 		initialize_shortened(embryo->entry);
1814 		embryo->entry->nlabel = 0;
1815 		embryo->entry->renderer_index = 0;
1816 		embryo->entry->combiner_index = 0;
1817 		embryo->entry->default_priority = 0;
1818 		embryo->entry->flags = 0;
1819 		embryo->entry->n_category = 0;
1820 		embryo->entry->nalloc_category = 0;
1821 		embryo->entry->n_obj_prop = 0;
1822 		embryo->entry->nalloc_obj_prop = 0;
1823 		embryo->entry->n_p_ability = 0;
1824 		embryo->entry->nalloc_p_ability = 0;
1825 		embryo->exists = false;
1826 	}
1827 	embryo->categories = NULL;
1828 	embryo->param_index = 0;
1829 	embryo->psource_index = 0;
1830 	embryo->last_category_index = -1;
1831 
1832 	return PARSE_ERROR_NONE;
1833 }
1834 
1835 
parse_entry_template(struct parser * p)1836 static enum parser_error parse_entry_template(struct parser *p)
1837 {
1838 	struct embryonic_ui_entry *embryo = parser_priv(p);
1839 	const char *name;
1840 	const struct ui_entry *tentry;
1841 	int template_ind, i;
1842 
1843 	if (!embryo) {
1844 		return PARSE_ERROR_MISSING_RECORD_HEADER;
1845 	}
1846 	name = parser_getstr(p, "template");
1847 	template_ind = ui_entry_lookup(name);
1848 	if (template_ind == 0) {
1849 		return PARSE_ERROR_INVALID_VALUE;
1850 	}
1851 	tentry = entries[template_ind - 1];
1852 	/* Replace simple fields with what's in the template. */
1853 	embryo->entry->renderer_index = tentry->renderer_index;
1854 	embryo->entry->combiner_index = tentry->combiner_index;
1855 	embryo->entry->default_priority = tentry->default_priority;
1856 	embryo->entry->flags = tentry->flags & ~ENTRY_FLAG_TEMPLATE_ONLY;
1857 	/* Add in categories not already present. */
1858 	for (i = 0; i < tentry->n_category; ++i) {
1859 		int ind;
1860 
1861 		if (! search_embryo_categories(embryo,
1862 			tentry->categories[i].name, &ind)) {
1863 			insert_embryo_category(embryo,
1864 				tentry->categories[i].name, 0,
1865 				tentry->categories[i].priority,
1866 				tentry->categories[i].priority_set, ind);
1867 		}
1868 	}
1869 	return PARSE_ERROR_NONE;
1870 }
1871 
1872 
parse_entry_parameter(struct parser * p)1873 static enum parser_error parse_entry_parameter(struct parser *p)
1874 {
1875 	struct embryonic_ui_entry *embryo = parser_priv(p);
1876 	const char *name;
1877 	int n = N_ELEMENTS(name_parameters);
1878 	int i;
1879 
1880 	if (!embryo) {
1881 		return PARSE_ERROR_MISSING_RECORD_HEADER;
1882 	}
1883 	/*
1884 	 * Don't allow parameterization when editing an already existing entry.
1885 	 */
1886 	if (embryo->exists) {
1887 		return PARSE_ERROR_INVALID_OPTION;
1888 	}
1889 	name = parser_getstr(p, "parameter");
1890 	i = 0;
1891 	while (1) {
1892 		if (i >= n) {
1893 			return PARSE_ERROR_INVALID_VALUE;
1894 		}
1895 		if (streq(name, name_parameters[i].name)) {
1896 			embryo->param_index = i;
1897 			break;
1898 		}
1899 		++i;
1900 	}
1901 	return PARSE_ERROR_NONE;
1902 }
1903 
1904 
parse_entry_renderer(struct parser * p)1905 static enum parser_error parse_entry_renderer(struct parser *p)
1906 {
1907 	struct embryonic_ui_entry *embryo = parser_priv(p);
1908 	const char *name;
1909 
1910 	if (!embryo) {
1911 		return PARSE_ERROR_MISSING_RECORD_HEADER;
1912 	}
1913 	name = parser_getstr(p, "renderer");
1914 	embryo->entry->renderer_index = ui_entry_renderer_lookup(name);
1915 	return (embryo->entry->renderer_index) ?
1916 		PARSE_ERROR_NONE : PARSE_ERROR_INVALID_VALUE;
1917 }
1918 
1919 
parse_entry_combine(struct parser * p)1920 static enum parser_error parse_entry_combine(struct parser *p)
1921 {
1922 	struct embryonic_ui_entry *embryo = parser_priv(p);
1923 	const char *name;
1924 
1925 	if (!embryo) {
1926 		return PARSE_ERROR_MISSING_RECORD_HEADER;
1927 	}
1928 	name = parser_getstr(p, "combine");
1929 	embryo->entry->combiner_index = ui_entry_combiner_lookup(name);
1930 	return (embryo->entry->combiner_index) ?
1931 		PARSE_ERROR_NONE : PARSE_ERROR_INVALID_VALUE;
1932 }
1933 
1934 
parse_entry_label(struct parser * p)1935 static enum parser_error parse_entry_label(struct parser *p)
1936 {
1937 	struct embryonic_ui_entry *embryo = parser_priv(p);
1938 	const char *name;
1939 	size_t nw;
1940 
1941 	if (!embryo) {
1942 		return PARSE_ERROR_MISSING_RECORD_HEADER;
1943 	}
1944 	name = parser_getstr(p, "label");
1945 	mem_free(embryo->entry->label);
1946 	embryo->entry->label = mem_alloc((MAX_ENTRY_LABEL + 1) *
1947 		sizeof(*embryo->entry->label));
1948 	nw = text_mbstowcs(embryo->entry->label, name, MAX_ENTRY_LABEL);
1949 	if (nw != (size_t)-1) {
1950 		/* Ensure null termination. */
1951 		size_t nw2 = text_mbstowcs(embryo->entry->label + nw, "", 1);
1952 
1953 		if (nw2 != (size_t)-1) {
1954 			embryo->entry->nlabel = nw;
1955 		} else {
1956 			return PARSE_ERROR_INVALID_VALUE;
1957 		}
1958 	} else {
1959 		return PARSE_ERROR_INVALID_VALUE;
1960 	}
1961 	return PARSE_ERROR_NONE;
1962 }
1963 
1964 
parse_entry_shortened_label(struct parser * p)1965 static enum parser_error parse_entry_shortened_label(struct parser *p)
1966 {
1967 	struct embryonic_ui_entry *embryo = parser_priv(p);
1968 	int n = 1;
1969 	const char *name;
1970 	size_t nw;
1971 
1972 	if (!embryo) {
1973 		return PARSE_ERROR_MISSING_RECORD_HEADER;
1974 	}
1975 	while (1) {
1976 		if (n > MAX_SHORTENED) {
1977 			return PARSE_ERROR_INTERNAL;
1978 		}
1979 		if (parser_hasval(p, format("label%d", n))) {
1980 			break;
1981 		}
1982 		++n;
1983 	}
1984 	name = parser_getstr(p, format("label%d", n));
1985 	nw = text_mbstowcs(embryo->entry->shortened_labels[n - 1], name, n);
1986 	if (nw != (size_t) -1) {
1987 		/* Ensure null termination. */
1988 		size_t nw2 = text_mbstowcs(
1989 			embryo->entry->shortened_labels[n - 1] +
1990 			((int)nw < n ? (int)nw : n), "", 1);
1991 
1992 		if (nw2 != (size_t)-1) {
1993 			embryo->entry->nshortened[n - 1] = nw;
1994 		} else {
1995 			return PARSE_ERROR_INVALID_VALUE;
1996 		}
1997 	} else {
1998 		return PARSE_ERROR_INVALID_VALUE;
1999 	}
2000 	return PARSE_ERROR_NONE;
2001 }
2002 
2003 
parse_entry_category(struct parser * p)2004 static enum parser_error parse_entry_category(struct parser *p)
2005 {
2006 	struct embryonic_ui_entry *embryo = parser_priv(p);
2007 	const char *name;
2008 	int ind;
2009 
2010 	if (!embryo) {
2011 		return PARSE_ERROR_MISSING_RECORD_HEADER;
2012 	}
2013 	name = parser_getstr(p, "category");
2014 	if (! search_embryo_categories(embryo, name, &ind)) {
2015 		insert_embryo_category(embryo, name, embryo->psource_index,
2016 			embryo->entry->default_priority, false, ind);
2017 	}
2018 	embryo->last_category_index = ind;
2019 	return PARSE_ERROR_NONE;
2020 }
2021 
2022 
parse_entry_priority(struct parser * p)2023 static enum parser_error parse_entry_priority(struct parser *p)
2024 {
2025 	struct embryonic_ui_entry *embryo = parser_priv(p);
2026 	const char *name;
2027 	int psource_index = 0;
2028 	int priority = 0;
2029 	int n = N_ELEMENTS(priority_schemes);
2030 	int i;
2031 
2032 	if (!embryo) {
2033 		return PARSE_ERROR_MISSING_RECORD_HEADER;
2034 	}
2035 	name = parser_getstr(p, "priority");
2036 	i = 1;
2037 	while (1) {
2038 		if (i >= n) {
2039 			char *end;
2040 			long v;
2041 
2042 			v = strtol(name, &end, 10);
2043 			if (! *name || *end) {
2044 				return PARSE_ERROR_NOT_NUMBER;
2045 			}
2046 			priority = (v <= INT_MAX) ?
2047 				((v >= INT_MIN) ? v : INT_MIN) : INT_MAX;
2048 			break;
2049 		}
2050 		if (streq(name, priority_schemes[i].name)) {
2051 			if (embryo->exists) {
2052 				priority = (*priority_schemes[i].priority)(
2053 					embryo->entry->param_index);
2054 			} else {
2055 				psource_index = i;
2056 			}
2057 			break;
2058 		}
2059 		++i;
2060 	}
2061 	if (embryo->last_category_index == -1) {
2062 		embryo->psource_index = psource_index;
2063 		embryo->entry->default_priority = priority;
2064 	} else {
2065 		if (embryo->exists) {
2066 			embryo->entry->categories[embryo->last_category_index].priority = priority;
2067 			embryo->entry->categories[embryo->last_category_index].priority_set = true;
2068 		} else {
2069 			embryo->categories[embryo->last_category_index].psource_index = psource_index;
2070 			embryo->categories[embryo->last_category_index].priority = priority;
2071 			embryo->categories[embryo->last_category_index].priority_set = true;
2072 		}
2073 	}
2074 	return PARSE_ERROR_NONE;
2075 }
2076 
2077 
parse_entry_flags(struct parser * p)2078 static enum parser_error parse_entry_flags(struct parser *p)
2079 {
2080 	struct embryonic_ui_entry *embryo = parser_priv(p);
2081 	char *flags;
2082 	char *s;
2083 	int n;
2084 
2085 	if (!embryo) {
2086 		return PARSE_ERROR_MISSING_RECORD_HEADER;
2087 	}
2088 	if (!parser_hasval(p, "flags")) {
2089 		return PARSE_ERROR_NONE;
2090 	}
2091 	flags = string_make(parser_getstr(p, "flags"));
2092 	n = N_ELEMENTS(entry_flags);
2093 	s = strtok(flags, " |");
2094 	while (s) {
2095 		int i = 0;
2096 
2097 		while (1) {
2098 			if (i >= n) {
2099 				string_free(flags);
2100 				return PARSE_ERROR_INVALID_FLAG;
2101 			}
2102 			if (streq(s, entry_flags[i].name)) {
2103 				embryo->entry->flags |= entry_flags[i].value;
2104 				break;
2105 			}
2106 			++i;
2107 		}
2108 		s = strtok(NULL, " |");
2109 	}
2110 	string_free(flags);
2111 	return PARSE_ERROR_NONE;
2112 }
2113 
2114 
parse_entry_desc(struct parser * p)2115 static enum parser_error parse_entry_desc(struct parser *p)
2116 {
2117 	struct embryonic_ui_entry *embryo = parser_priv(p);
2118 
2119 	if (!embryo) {
2120 		return PARSE_ERROR_MISSING_RECORD_HEADER;
2121 	}
2122 	/* Don't bother to store the description. */
2123 	return PARSE_ERROR_NONE;
2124 }
2125 
2126 
init_parse_ui_entry(void)2127 static struct parser *init_parse_ui_entry(void)
2128 {
2129 	struct parser *p = parser_new();
2130 	int i;
2131 
2132 	parser_setpriv(p, NULL);
2133 	parser_reg(p, "name str name", parse_entry_name);
2134 	parser_reg(p, "template str template", parse_entry_template);
2135 	parser_reg(p, "parameter str parameter", parse_entry_parameter);
2136 	parser_reg(p, "renderer str renderer", parse_entry_renderer);
2137 	parser_reg(p, "combine str combine", parse_entry_combine);
2138 	parser_reg(p, "label str label", parse_entry_label);
2139 	for (i = 1; i <= MAX_SHORTENED; ++i) {
2140 		parser_reg(p, format("label%d str label%d", i, i),
2141 			parse_entry_shortened_label);
2142 	}
2143 	parser_reg(p, "category str category", parse_entry_category);
2144 	parser_reg(p, "priority str priority", parse_entry_priority);
2145 	parser_reg(p, "flags ?str flags", parse_entry_flags);
2146 	parser_reg(p, "desc str desc", parse_entry_desc);
2147 	return p;
2148 }
2149 
2150 
run_parse_ui_entry(struct parser * p)2151 static errr run_parse_ui_entry(struct parser *p)
2152 {
2153 	errr result = parse_file(p, "ui_entry_base");
2154 	int i;
2155 
2156 	if (result != 0) {
2157 		return result;
2158 	}
2159 	if (hatch_last_embryo(p)) {
2160 		return 1;
2161 	}
2162 	/*
2163 	 * Mark those as templates only so they'll never be directly displayed.
2164 	 */
2165 	for (i = 0; i < n_entry; ++i) {
2166 		entries[i]->flags |= ENTRY_FLAG_TEMPLATE_ONLY;
2167 	}
2168 	return parse_file(p, "ui_entry");
2169 }
2170 
2171 
finish_parse_ui_entry(struct parser * p)2172 static errr finish_parse_ui_entry(struct parser *p)
2173 {
2174 	errr result = 0;
2175 	int i;
2176 
2177 	if (hatch_last_embryo(p)) {
2178 		result = 1;
2179 	}
2180 	for (i = 0; i < n_entry; ++i) {
2181 		int j;
2182 
2183 		/* Set labels not already configured. */
2184 		if (entries[i]->nlabel == 0) {
2185 			size_t n;
2186 
2187 			entries[i]->label = mem_alloc((MAX_ENTRY_LABEL + 1) *
2188 				sizeof(*entries[i]->label));
2189 			n = text_mbstowcs(entries[i]->label, entries[i]->name,
2190 				MAX_ENTRY_LABEL);
2191 			if (n != (size_t)-1) {
2192 			 	/* Ensure null termination. */
2193 				size_t n2 = text_mbstowcs(
2194 					entries[i]->label + n, "", 1);
2195 
2196 				if (n2 != (size_t)-1) {
2197 					entries[i]->nlabel = n;
2198 				} else {
2199 					result = 1;
2200 				}
2201 			} else {
2202 				result = 1;
2203 			}
2204 		}
2205 		fill_out_shortened(entries[i]);
2206 
2207 		/* Set priorities not already configured. */
2208 		for (j = 0; j < entries[i]->n_category; ++j) {
2209 			if (! entries[i]->categories[j].priority_set) {
2210 				entries[i]->categories[j].priority =
2211 					entries[i]->default_priority;
2212 				entries[i]->categories[j].priority_set = true;
2213 			}
2214 		}
2215 	}
2216 
2217 	parser_destroy(p);
2218 	return result;
2219 }
2220 
2221 
cleanup_parse_ui_entry(void)2222 static void cleanup_parse_ui_entry(void)
2223 {
2224 	int i;
2225 
2226 	for (i = 0; i < n_entry; ++i) {
2227 		string_free(entries[i]->name);
2228 		mem_free(entries[i]->categories);
2229 		mem_free(entries[i]->obj_props);
2230 		mem_free(entries[i]->p_abilities);
2231 		mem_free(entries[i]->label);
2232 		mem_free(entries[i]);
2233 	}
2234 	mem_free(entries);
2235 	n_entry = 0;
2236 	nalloc_entry = 0;
2237 	entries = NULL;
2238 
2239 	for (i = 0; i < n_category; ++i) {
2240 		string_free(categories[i]);
2241 	}
2242 	mem_free(categories);
2243 	n_category = 0;
2244 	nalloc_category = 0;
2245 	categories = NULL;
2246 }
2247 
2248 
2249 struct file_parser ui_entry_parser = {
2250 	"ui_entry",
2251 	init_parse_ui_entry,
2252 	run_parse_ui_entry,
2253 	finish_parse_ui_entry,
2254 	cleanup_parse_ui_entry
2255 };
2256