File: | shared/entitiesdef.cpp |
Location: | line 561, column 2 |
Description: | Memory is never released; potential leak of memory pointed to by 'newRanges' |
1 | /** | ||||
2 | * @file | ||||
3 | * @brief Handles definition of entities, parsing them from entities.ufo | ||||
4 | */ | ||||
5 | |||||
6 | /* | ||||
7 | All original material Copyright (C) 2002-2011 UFO: Alien Invasion. | ||||
8 | |||||
9 | Copyright (C) 1997-2001 Id Software, Inc. | ||||
10 | |||||
11 | This program is free software; you can redistribute it and/or | ||||
12 | modify it under the terms of the GNU General Public License | ||||
13 | as published by the Free Software Foundation; either version 2 | ||||
14 | of the License, or (at your option) any later version. | ||||
15 | |||||
16 | This program is distributed in the hope that it will be useful, | ||||
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||
19 | |||||
20 | See the GNU General Public License for more details. | ||||
21 | |||||
22 | You should have received a copy of the GNU General Public License | ||||
23 | along with this program; if not, write to the Free Software | ||||
24 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||||
25 | |||||
26 | */ | ||||
27 | |||||
28 | #include <string.h> | ||||
29 | #include <stdlib.h> | ||||
30 | #include <assert.h> | ||||
31 | #include <stdio.h> | ||||
32 | #include <math.h> | ||||
33 | |||||
34 | #include "shared.h" /* needed for strdup() */ | ||||
35 | #include "parse.h" | ||||
36 | #include "entitiesdef.h" | ||||
37 | |||||
38 | #define ED_MAX_KEYS_PER_ENT32 32 | ||||
39 | #define ED_MAX_TOKEN_LEN512 512 | ||||
40 | #define ED_MAX_ERR_LEN512 512 | ||||
41 | |||||
42 | static char lastErr[ED_MAX_ERR_LEN512]; /**< for storing last error message */ | ||||
43 | static char lastErrExtra[ED_MAX_ERR_LEN512]; /**< temporary storage for extra information to be added to lastErr */ | ||||
44 | |||||
45 | int numEntityDefs; | ||||
46 | entityDef_t entityDefs[ED_MAX_DEFS64 + 1]; | ||||
47 | |||||
48 | /** | ||||
49 | * @brief write an error message and exit the current function returning ED_ERROR | ||||
50 | * @sa ED_TEST_RETURN_ERROR | ||||
51 | */ | ||||
52 | #define ED_RETURN_ERROR(...){ snprintf(lastErr, sizeof(lastErr), ...); return -1; } \ | ||||
53 | { \ | ||||
54 | snprintf(lastErr, sizeof(lastErr), __VA_ARGS__); \ | ||||
55 | return ED_ERROR-1; \ | ||||
56 | } | ||||
57 | |||||
58 | /** | ||||
59 | * @brief test a condition, write an error | ||||
60 | * message and exit the current function with ED_ERROR | ||||
61 | */ | ||||
62 | #define ED_TEST_RETURN_ERROR(condition,...)if (condition) { snprintf(lastErr, sizeof(lastErr), ...); return -1; } \ | ||||
63 | if (condition) { \ | ||||
64 | snprintf(lastErr, sizeof(lastErr), __VA_ARGS__); \ | ||||
65 | return ED_ERROR-1; \ | ||||
66 | } | ||||
67 | |||||
68 | /** | ||||
69 | * @brief if the function returns ED_ERROR, then the function that the | ||||
70 | * macro is in also returns ED_ERROR. Note that the called function is expected | ||||
71 | * to set lastErr, probably by using the ED_TEST_RETURN_ERROR macro | ||||
72 | */ | ||||
73 | #define ED_PASS_ERROR(function_call)if ((function_call) == -1) { return -1; } \ | ||||
74 | if ((function_call) == ED_ERROR-1) { \ | ||||
75 | return ED_ERROR-1; \ | ||||
76 | } | ||||
77 | |||||
78 | /** | ||||
79 | * @brief if the function returns ED_ERROR, then the function that the | ||||
80 | * macro is in also returns ED_ERROR. Note that the called function is expected | ||||
81 | * to set lastErr, probably by using the ED_TEST_RETURN_ERROR macro. this macro then | ||||
82 | * appends extra detail to the message to give context | ||||
83 | */ | ||||
84 | #define ED_PASS_ERROR_EXTRAMSG(function_call,...)if ((function_call) == -1) { snprintf(lastErrExtra, sizeof(lastErr ), ...); strncat(lastErr, lastErrExtra, sizeof(lastErr) - strlen (lastErr) -1); return -1; } \ | ||||
85 | if ((function_call) == ED_ERROR-1) { \ | ||||
86 | snprintf(lastErrExtra, sizeof(lastErr), __VA_ARGS__); \ | ||||
87 | strncat(lastErr, lastErrExtra, sizeof(lastErr) - strlen(lastErr) -1); \ | ||||
88 | return ED_ERROR-1; \ | ||||
89 | } | ||||
90 | |||||
91 | /** | ||||
92 | * @brief allocate space for key defs etc, pointers for which are stored in the entityDef_t | ||||
93 | * @return ED_ERROR or ED_OK. | ||||
94 | */ | ||||
95 | static int ED_AllocEntityDef (entityKeyDef_t *newKeyDefs, int numKeyDefs, int entityIndex) | ||||
96 | { | ||||
97 | entityDef_t *eDef = &entityDefs[entityIndex]; | ||||
98 | |||||
99 | /* now we know how many there are in this entity, malloc */ | ||||
100 | eDef->keyDefs = (entityKeyDef_t *) malloc((numKeyDefs + 1) * sizeof(entityKeyDef_t)); | ||||
101 | ED_TEST_RETURN_ERROR(!eDef->keyDefs, "ED_AllocEntityDef: out of memory")if (!eDef->keyDefs) { snprintf(lastErr, sizeof(lastErr), "ED_AllocEntityDef: out of memory" ); return -1; }; | ||||
102 | eDef->numKeyDefs = numKeyDefs; | ||||
103 | |||||
104 | /* and copy from temp buffer */ | ||||
105 | memcpy(eDef->keyDefs, newKeyDefs, numKeyDefs * sizeof(entityKeyDef_t)); | ||||
106 | |||||
107 | /* set NULLs at the end, to enable looping through using a pointer */ | ||||
108 | OBJZERO(eDef->keyDefs[numKeyDefs])(memset(&((eDef->keyDefs[numKeyDefs])), (0), sizeof((eDef ->keyDefs[numKeyDefs])))); | ||||
109 | |||||
110 | /* classname is commonly used, put it in its own field, copied from keyDefs[0] */ | ||||
111 | eDef->classname = strdup(eDef->keyDefs->desc); | ||||
112 | ED_TEST_RETURN_ERROR(!eDef->classname, "ED_AllocEntityDef: out of memory")if (!eDef->classname) { snprintf(lastErr, sizeof(lastErr), "ED_AllocEntityDef: out of memory"); return -1; }; | ||||
113 | |||||
114 | return ED_OK1; | ||||
115 | } | ||||
116 | |||||
117 | /** | ||||
118 | * @brief search for an existing keyDef to add a new parsed pair info to. | ||||
119 | * @return a pointer to the entity def or NULL if it is not found | ||||
120 | */ | ||||
121 | static entityKeyDef_t *ED_FindKeyDefInArray (entityKeyDef_t keyDefs[], int numDefs, const char *name, int parseMode) | ||||
122 | { | ||||
123 | int i; | ||||
124 | for (i = 0; i < numDefs; i++) { | ||||
125 | const entityKeyDef_t *keyDef = &keyDefs[i]; | ||||
126 | /* names equal. both abstract or both not abstract */ | ||||
127 | if (Q_streq(keyDef->name, name)(strcmp(keyDef->name, name) == 0) && !((keyDef->flags ^ parseMode) & ED_ABSTRACT(1<<2))) { | ||||
128 | return &keyDefs[i]; | ||||
129 | } | ||||
130 | } | ||||
131 | return NULL__null; | ||||
132 | } | ||||
133 | |||||
134 | /** | ||||
135 | * @brief converts a string representation of a type (eg V_FLOAT) to | ||||
136 | * the appropriate internal constant integer | ||||
137 | * @return the constant, or ED_ERROR if strType is not recognised | ||||
138 | */ | ||||
139 | static int ED_Type2Constant (const char *strType) | ||||
140 | { | ||||
141 | if (Q_streq(strType, "V_FLOAT")(strcmp(strType, "V_FLOAT") == 0)) | ||||
142 | return ED_TYPE_FLOAT(1<<3); | ||||
143 | else if (Q_streq(strType, "V_INT")(strcmp(strType, "V_INT") == 0)) | ||||
144 | return ED_TYPE_INT(1<<4); | ||||
145 | else if (Q_streq(strType, "V_STRING")(strcmp(strType, "V_STRING") == 0)) | ||||
146 | return ED_TYPE_STRING(1<<5); | ||||
147 | |||||
148 | ED_RETURN_ERROR("ED_Type2Constant: type string not recognised: \"%s\"", strType){ snprintf(lastErr, sizeof(lastErr), "ED_Type2Constant: type string not recognised: \"%s\"" , strType); return -1; }; | ||||
149 | } | ||||
150 | |||||
151 | /** | ||||
152 | * @brief converts an internal constant integer to a string | ||||
153 | * representation of a type (eg V_FLOAT) | ||||
154 | * @return the string, or NULL if the integer is not recognised. | ||||
155 | */ | ||||
156 | static const char *ED_Constant2Type (int constInt) | ||||
157 | { | ||||
158 | switch (constInt) { | ||||
159 | case ED_TYPE_FLOAT(1<<3): | ||||
160 | return "V_FLOAT"; | ||||
161 | case ED_TYPE_INT(1<<4): | ||||
162 | return "V_INT"; | ||||
163 | case ED_TYPE_STRING(1<<5): | ||||
164 | return "V_STRING"; | ||||
165 | default: | ||||
166 | snprintf(lastErr, sizeof(lastErr), "ED_Constant2Type: constant not recognised"); | ||||
167 | return NULL__null; | ||||
168 | } | ||||
169 | } | ||||
170 | |||||
171 | /** | ||||
172 | * @brief parses an int array from a string | ||||
173 | * @param str the string to parse | ||||
174 | * @param[out] v the array of int to put the answer in | ||||
175 | * it must have enough space for n elements. | ||||
176 | * @param n the number of elements expected in the vector | ||||
177 | * @return ED_ERROR or ED_OK | ||||
178 | * @sa ED_GetLastError. | ||||
179 | */ | ||||
180 | static int ED_GetIntVectorFromString (const char *str, int v[], const int n) | ||||
181 | { | ||||
182 | int i; | ||||
183 | const char *buf_p = str; | ||||
184 | |||||
185 | for (i = 0; buf_p; i++) { | ||||
186 | const char *tok = Com_Parse(&buf_p); | ||||
187 | if (tok[0] == '\0') | ||||
188 | break; /* previous tok was the last real one, don't waste time */ | ||||
189 | ED_TEST_RETURN_ERROR(i >= n, "ED_GetIntVectorFromString: v[%i] too small for ints from string \"%s\"", n, str)if (i >= n) { snprintf(lastErr, sizeof(lastErr), "ED_GetIntVectorFromString: v[%i] too small for ints from string \"%s\"" , n, str); return -1; }; | ||||
190 | v[i] = atoi(tok); | ||||
191 | } | ||||
192 | ED_TEST_RETURN_ERROR(i != n, "ED_GetIntVectorFromString: v[%i] wrong size for ints from string \"%s\"", n, str)if (i != n) { snprintf(lastErr, sizeof(lastErr), "ED_GetIntVectorFromString: v[%i] wrong size for ints from string \"%s\"" , n, str); return -1; }; | ||||
193 | return ED_OK1; | ||||
194 | } | ||||
195 | |||||
196 | /** | ||||
197 | * @brief parses an float array from a string | ||||
198 | * @param str the string to parse | ||||
199 | * @param[out] v the array of float to put the answer in | ||||
200 | * it must have enough space for n elements. | ||||
201 | * @param n the number of elements expected in the vector | ||||
202 | * @return ED_ERROR or ED_OK | ||||
203 | * @sa ED_GetLastError. | ||||
204 | */ | ||||
205 | static int ED_GetFloatVectorFromString (const char *str, float v[], const int n) | ||||
206 | { | ||||
207 | int i; | ||||
208 | const char *buf_p = str; | ||||
209 | |||||
210 | for (i = 0; buf_p; i++) { | ||||
211 | const char *tok = Com_Parse(&buf_p); | ||||
212 | if (tok[0] == '\0') | ||||
213 | break; /* previous tok was the last real one, don't waste time */ | ||||
214 | ED_TEST_RETURN_ERROR(i >= n, "ED_GetFloatVectorFromString: v[%i] too small for floats from string \"%s\"", n, str)if (i >= n) { snprintf(lastErr, sizeof(lastErr), "ED_GetFloatVectorFromString: v[%i] too small for floats from string \"%s\"" , n, str); return -1; }; | ||||
215 | v[i] = atof(tok); | ||||
216 | } | ||||
217 | ED_TEST_RETURN_ERROR(i != n, "ED_GetFloatVectorFromString: v[%i] wrong size for floats from string \"%s\"", n, str)if (i != n) { snprintf(lastErr, sizeof(lastErr), "ED_GetFloatVectorFromString: v[%i] wrong size for floats from string \"%s\"" , n, str); return -1; }; | ||||
218 | return ED_OK1; | ||||
219 | } | ||||
220 | |||||
221 | /** | ||||
222 | * @brief gets the default value for the key | ||||
223 | * @param[in] defaultBuf the calling function is responsible for ensuring this buffer | ||||
224 | * has enough space | ||||
225 | * @param n the number of floats the defaultBuf can hold | ||||
226 | * @param kd the key definition to get the default for. | ||||
227 | * @sa ED_GetKeyDef can be used to find kd. | ||||
228 | * @return ED_OK or ED_ERROR (no default, or n is too small). | ||||
229 | * @note the defualt int array is tested on parsing | ||||
230 | */ | ||||
231 | int ED_GetDefaultFloat (float *defaultBuf, const int n, const entityKeyDef_t *kd) | ||||
232 | { | ||||
233 | ED_TEST_RETURN_ERROR(!kd->defaultVal, "ED_GetDefaultFloat: no default available")if (!kd->defaultVal) { snprintf(lastErr, sizeof(lastErr), "ED_GetDefaultFloat: no default available" ); return -1; }; | ||||
234 | ED_TEST_RETURN_ERROR(!(kd->flags & ED_TYPE_FLOAT), "ED_GetDefaultFloat: key does not define V_FLOAT")if (!(kd->flags & (1<<3))) { snprintf(lastErr, sizeof (lastErr), "ED_GetDefaultFloat: key does not define V_FLOAT") ; return -1; }; | ||||
235 | ED_PASS_ERROR(ED_GetFloatVectorFromString(kd->defaultVal, defaultBuf, n))if ((ED_GetFloatVectorFromString(kd->defaultVal, defaultBuf , n)) == -1) { return -1; }; | ||||
236 | return ED_OK1; | ||||
237 | } | ||||
238 | |||||
239 | /** | ||||
240 | * @brief gets the default value for the key | ||||
241 | * @param[in] defaultBuf the calling function is responsible for ensuring this buffer | ||||
242 | * has enough space | ||||
243 | * @param n the number of ints the defaultBuf can hold | ||||
244 | * @param kd the key definition to get the default for. | ||||
245 | * @sa ED_GetKeyDef can be used to find kd. | ||||
246 | * @return ED_OK or ED_ERROR (no default, or n is too small). | ||||
247 | * @note the defualt int array is tested on parsing | ||||
248 | */ | ||||
249 | int ED_GetDefaultInt (int *defaultBuf, const int n, const entityKeyDef_t *kd) | ||||
250 | { | ||||
251 | ED_TEST_RETURN_ERROR(!kd->defaultVal, "ED_GetDefaultInt: no default available")if (!kd->defaultVal) { snprintf(lastErr, sizeof(lastErr), "ED_GetDefaultInt: no default available" ); return -1; }; | ||||
252 | ED_TEST_RETURN_ERROR(!(kd->flags & ED_TYPE_INT), "ED_GetDefaultInt: key does not define V_INT")if (!(kd->flags & (1<<4))) { snprintf(lastErr, sizeof (lastErr), "ED_GetDefaultInt: key does not define V_INT"); return -1; }; | ||||
253 | ED_PASS_ERROR(ED_GetIntVectorFromString(kd->defaultVal, defaultBuf, n))if ((ED_GetIntVectorFromString(kd->defaultVal, defaultBuf, n)) == -1) { return -1; }; | ||||
254 | return ED_OK1; | ||||
255 | } | ||||
256 | |||||
257 | /** | ||||
258 | * @brief parses a value from the definition | ||||
259 | * @param kd the key definition to parse from | ||||
260 | * @param[out] v the array of int to put the answer in | ||||
261 | * it must have enough space for n elements. | ||||
262 | * @param n the number of elements expected in the vector | ||||
263 | * @return ED_ERROR or ED_OK | ||||
264 | * @sa ED_GetLastError. | ||||
265 | */ | ||||
266 | int ED_GetIntVector (const entityKeyDef_t *kd, int v[], const int n) | ||||
267 | { | ||||
268 | ED_PASS_ERROR(ED_GetIntVectorFromString(kd->desc, v, n))if ((ED_GetIntVectorFromString(kd->desc, v, n)) == -1) { return -1; }; | ||||
269 | return ED_OK1; | ||||
270 | } | ||||
271 | |||||
272 | /** | ||||
273 | * @brief checks that a string represents a single number | ||||
274 | * @param floatOrInt one of ED_TYPE_FLOAT or ED_TYPE_INT | ||||
275 | * @param insistPositive if 1, then tests for the number being greater than or equal to zero. | ||||
276 | * @sa ED_CheckNumericType | ||||
277 | * @note disallows hex, inf, NaN, numbers with junk on the end (eg -0123junk) | ||||
278 | * @return ED_OK or ED_ERROR | ||||
279 | * @sa ED_GetLastError | ||||
280 | * @note the parsed numbers are stored for later use in lastCheckedInt and lastCheckedFloat | ||||
281 | */ | ||||
282 | static int ED_CheckNumber (const char *value, const int floatOrInt, const int insistPositive, int_float_tu *parsedNumber) | ||||
283 | { | ||||
284 | char *end_p; | ||||
285 | /* V_INTs are protected from octal and hex as strtol with base 10 is used. | ||||
286 | * this test is useful for V_INT, as it gives a specific error message. | ||||
287 | * V_FLOATs are not protected from hex, inf, nan, so this check is here. | ||||
288 | * strstr is used for hex, as 0x may not be the start of the string. | ||||
289 | * eg -0x0.2 is a negative hex float */ | ||||
290 | ED_TEST_RETURN_ERROR(value[0] == 'i' || value[0] == 'I' || value[0] == 'n' || value[0] == 'N'if (value[0] == 'i' || value[0] == 'I' || value[0] == 'n' || value [0] == 'N' || strstr(value, "0x") || strstr(value, "0X")) { snprintf (lastErr, sizeof(lastErr), "infinity, NaN, hex (0x...) not allowed. found \"%s\"" , value); return -1; } | ||||
291 | || strstr(value, "0x") || strstr(value, "0X"),if (value[0] == 'i' || value[0] == 'I' || value[0] == 'n' || value [0] == 'N' || strstr(value, "0x") || strstr(value, "0X")) { snprintf (lastErr, sizeof(lastErr), "infinity, NaN, hex (0x...) not allowed. found \"%s\"" , value); return -1; } | ||||
292 | "infinity, NaN, hex (0x...) not allowed. found \"%s\"", value)if (value[0] == 'i' || value[0] == 'I' || value[0] == 'n' || value [0] == 'N' || strstr(value, "0x") || strstr(value, "0X")) { snprintf (lastErr, sizeof(lastErr), "infinity, NaN, hex (0x...) not allowed. found \"%s\"" , value); return -1; }; | ||||
293 | switch (floatOrInt) { | ||||
294 | case ED_TYPE_FLOAT(1<<3): | ||||
295 | parsedNumber->f = strtof(value, &end_p); | ||||
296 | ED_TEST_RETURN_ERROR(insistPositive && parsedNumber->f < 0.0f, "ED_CheckNumber: not positive %s", value)if (insistPositive && parsedNumber->f < 0.0f) { snprintf(lastErr, sizeof(lastErr), "ED_CheckNumber: not positive %s" , value); return -1; }; | ||||
297 | break; | ||||
298 | case ED_TYPE_INT(1<<4): | ||||
299 | parsedNumber->i = (int)strtol(value, &end_p, 10); | ||||
300 | ED_TEST_RETURN_ERROR(insistPositive && parsedNumber->i < 0, "ED_CheckNumber: not positive %s", value)if (insistPositive && parsedNumber->i < 0) { snprintf (lastErr, sizeof(lastErr), "ED_CheckNumber: not positive %s", value); return -1; }; | ||||
301 | break; | ||||
302 | default: | ||||
303 | ED_RETURN_ERROR("ED_CheckNumber: type to test against not recognised"){ snprintf(lastErr, sizeof(lastErr), "ED_CheckNumber: type to test against not recognised" ); return -1; }; | ||||
304 | } | ||||
305 | /* if strto* did not use the whole token, then there is some non-number part to it */ | ||||
306 | ED_TEST_RETURN_ERROR(strlen(value) != (unsigned int)(end_p-value),if (strlen(value) != (unsigned int)(end_p-value)) { snprintf( lastErr, sizeof(lastErr), "problem with numeric value: \"%s\" declared as %s. (might be relevant: only use whitespace to delimit values)" , value, ED_Constant2Type(floatOrInt)); return -1; } | ||||
307 | "problem with numeric value: \"%s\" declared as %s. (might be relevant: only use whitespace to delimit values)",if (strlen(value) != (unsigned int)(end_p-value)) { snprintf( lastErr, sizeof(lastErr), "problem with numeric value: \"%s\" declared as %s. (might be relevant: only use whitespace to delimit values)" , value, ED_Constant2Type(floatOrInt)); return -1; } | ||||
308 | value, ED_Constant2Type(floatOrInt))if (strlen(value) != (unsigned int)(end_p-value)) { snprintf( lastErr, sizeof(lastErr), "problem with numeric value: \"%s\" declared as %s. (might be relevant: only use whitespace to delimit values)" , value, ED_Constant2Type(floatOrInt)); return -1; }; | ||||
309 | return ED_OK1; | ||||
310 | } | ||||
311 | |||||
312 | /** | ||||
313 | * @brief check a value against the range for the key | ||||
314 | * @param floatOrInt either ED_TYPE_FLOAT or ED_TYPE_INT | ||||
315 | * @param index the index of the number being checked in the value. eg angles "90 180", 90 is at 0, 180 is at 1. | ||||
316 | * @note checks lastCheckedInt or lastCheckedFloat against the range in the supplied keyDef. | ||||
317 | * @return ED_ERROR or ED_OK | ||||
318 | */ | ||||
319 | static int ED_CheckRange (const entityKeyDef_t *keyDef, const int floatOrInt, const int index, int_float_tu parsedNumber) | ||||
320 | { | ||||
321 | /* there may be a range for each element of the value, or there may only be one */ | ||||
322 | const int useRangeIndex = (keyDef->numRanges == 1) ? 0 : index; | ||||
323 | entityKeyRange_t *kr; | ||||
324 | const float discreteFloatEpsilon = 0.0001f; | ||||
325 | if (0 == keyDef->numRanges) | ||||
326 | return ED_OK1; /* if no range defined, that is OK */ | ||||
327 | assert(useRangeIndex < keyDef->numRanges)(__builtin_expect(!(useRangeIndex < keyDef->numRanges), 0) ? __assert_rtn(__func__, "src/shared/entitiesdef.cpp", 327 , "useRangeIndex < keyDef->numRanges") : (void)0); | ||||
328 | kr = keyDef->ranges[useRangeIndex]; | ||||
329 | switch (floatOrInt) { | ||||
330 | case ED_TYPE_FLOAT(1<<3): | ||||
331 | if (kr->continuous) { | ||||
332 | ED_TEST_RETURN_ERROR(parsedNumber.f < kr->fArr[0] || parsedNumber.f > kr->fArr[1],if (parsedNumber.f < kr->fArr[0] || parsedNumber.f > kr->fArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %.1f out of range, \"%s\" in %s" , parsedNumber.f, kr->str, keyDef->name); return -1; } | ||||
333 | "ED_CheckRange: %.1f out of range, \"%s\" in %s",if (parsedNumber.f < kr->fArr[0] || parsedNumber.f > kr->fArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %.1f out of range, \"%s\" in %s" , parsedNumber.f, kr->str, keyDef->name); return -1; } | ||||
334 | parsedNumber.f, kr->str, keyDef->name)if (parsedNumber.f < kr->fArr[0] || parsedNumber.f > kr->fArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %.1f out of range, \"%s\" in %s" , parsedNumber.f, kr->str, keyDef->name); return -1; }; | ||||
335 | return ED_OK1; | ||||
336 | } else { | ||||
337 | int j; | ||||
338 | for (j = 0; j < kr->numElements; j++) | ||||
339 | if (fabs(parsedNumber.f - kr->fArr[j]) < discreteFloatEpsilon) | ||||
340 | return ED_OK1; | ||||
341 | } | ||||
342 | break; | ||||
343 | case ED_TYPE_INT(1<<4): | ||||
344 | if (kr->continuous) { | ||||
345 | ED_TEST_RETURN_ERROR(parsedNumber.i < kr->iArr[0] || parsedNumber.i > kr->iArr[1],if (parsedNumber.i < kr->iArr[0] || parsedNumber.i > kr->iArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %i out of range, \"%s\" in %s" , parsedNumber.i, kr->str, keyDef->name); return -1; } | ||||
346 | "ED_CheckRange: %i out of range, \"%s\" in %s",if (parsedNumber.i < kr->iArr[0] || parsedNumber.i > kr->iArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %i out of range, \"%s\" in %s" , parsedNumber.i, kr->str, keyDef->name); return -1; } | ||||
347 | parsedNumber.i, kr->str, keyDef->name)if (parsedNumber.i < kr->iArr[0] || parsedNumber.i > kr->iArr[1]) { snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: %i out of range, \"%s\" in %s" , parsedNumber.i, kr->str, keyDef->name); return -1; }; | ||||
348 | return ED_OK1; | ||||
349 | } else { | ||||
350 | int j; | ||||
351 | for (j = 0; j < kr->numElements; j++) | ||||
352 | if (kr->iArr[j] == parsedNumber.i) | ||||
353 | return ED_OK1; | ||||
354 | } | ||||
355 | break; | ||||
356 | default: | ||||
357 | ED_RETURN_ERROR( "ED_CheckRange: type to test against not recognised in %s", keyDef->name){ snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: type to test against not recognised in %s" , keyDef->name); return -1; }; | ||||
358 | } | ||||
359 | ED_RETURN_ERROR("ED_CheckRange: value not specified in range definition, \"%s\" in %s",{ snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: value not specified in range definition, \"%s\" in %s" , kr->str, keyDef->name); return -1; } | ||||
360 | kr->str, keyDef->name){ snprintf(lastErr, sizeof(lastErr), "ED_CheckRange: value not specified in range definition, \"%s\" in %s" , kr->str, keyDef->name); return -1; }; | ||||
361 | } | ||||
362 | |||||
363 | /** | ||||
364 | * @brief tests if a value string matches the type for this key. this includes | ||||
365 | * each element of a numeric array. Also checks value against range def, if one exists. | ||||
366 | * @param floatOrInt one of ED_TYPE_FLOAT or ED_TYPE_INT | ||||
367 | * @return ED_OK or ED_ERROR (call ED_GetLastError) | ||||
368 | */ | ||||
369 | static int ED_CheckNumericType (const entityKeyDef_t *keyDef, const char *value, const int floatOrInt) | ||||
370 | { | ||||
371 | int i = 0; | ||||
372 | static char tokBuf[64]; | ||||
373 | const char *buf_p = tokBuf; | ||||
374 | |||||
375 | strncpy(tokBuf, value, sizeof(tokBuf)); | ||||
376 | assert(!(floatOrInt & ED_TYPE_INT) != !(floatOrInt & ED_TYPE_FLOAT))(__builtin_expect(!(!(floatOrInt & (1<<4)) != !(floatOrInt & (1<<3))), 0) ? __assert_rtn(__func__, "src/shared/entitiesdef.cpp" , 376, "!(floatOrInt & ED_TYPE_INT) != !(floatOrInt & ED_TYPE_FLOAT)" ) : (void)0);/* logical exclusive or */ | ||||
377 | while (buf_p) { | ||||
378 | const char *tok = Com_Parse(&buf_p); | ||||
379 | int_float_tu parsedNumber; | ||||
380 | if (tok[0] == '\0') | ||||
381 | break; /* previous tok was the last real one, don't waste time */ | ||||
382 | |||||
383 | ED_PASS_ERROR_EXTRAMSG(ED_CheckNumber(tok, floatOrInt, keyDef->flags & ED_INSIST_POSITIVE, &parsedNumber),if ((ED_CheckNumber(tok, floatOrInt, keyDef->flags & ( 1<<9), &parsedNumber)) == -1) { snprintf(lastErrExtra , sizeof(lastErr), " in key \"%s\"", keyDef->name); strncat (lastErr, lastErrExtra, sizeof(lastErr) - strlen(lastErr) -1) ; return -1; } | ||||
384 | " in key \"%s\"", keyDef->name)if ((ED_CheckNumber(tok, floatOrInt, keyDef->flags & ( 1<<9), &parsedNumber)) == -1) { snprintf(lastErrExtra , sizeof(lastErr), " in key \"%s\"", keyDef->name); strncat (lastErr, lastErrExtra, sizeof(lastErr) - strlen(lastErr) -1) ; return -1; }; | ||||
385 | |||||
386 | ED_PASS_ERROR(ED_CheckRange(keyDef, floatOrInt, i, parsedNumber))if ((ED_CheckRange(keyDef, floatOrInt, i, parsedNumber)) == - 1) { return -1; }; | ||||
387 | |||||
388 | i++; | ||||
389 | } | ||||
390 | |||||
391 | ED_TEST_RETURN_ERROR(i != keyDef->vLen, "ED_CheckNumericType: %i elements in vector that should have %i for \"%s\" key",if (i != keyDef->vLen) { snprintf(lastErr, sizeof(lastErr) , "ED_CheckNumericType: %i elements in vector that should have %i for \"%s\" key" , i, keyDef->vLen, keyDef->name); return -1; } | ||||
392 | i, keyDef->vLen, keyDef->name)if (i != keyDef->vLen) { snprintf(lastErr, sizeof(lastErr) , "ED_CheckNumericType: %i elements in vector that should have %i for \"%s\" key" , i, keyDef->vLen, keyDef->name); return -1; }; | ||||
393 | |||||
394 | return ED_OK1; | ||||
395 | } | ||||
396 | |||||
397 | /** | ||||
398 | * @brief tests if a value string matches the type for this key. Also checks the value against the | ||||
399 | * range, if one was defined. | ||||
400 | * @return ED_OK or ED_ERROR | ||||
401 | * @sa ED_GetLastError | ||||
402 | * @note abstract (radiant) keys may not have types. keys used here must be declared in entities.ufo in | ||||
403 | * an optional or mandatory block. | ||||
404 | */ | ||||
405 | int ED_Check (const char *classname, const char *key, const char *value) | ||||
406 | { | ||||
407 | const entityKeyDef_t *kd = ED_GetKeyDef(classname, key, 0); | ||||
408 | if (!kd) | ||||
409 | return ED_ERROR-1; | ||||
410 | |||||
411 | return ED_CheckKey(kd, value); | ||||
412 | } | ||||
413 | |||||
414 | /** | ||||
415 | * @brief as ED_Check, but where the entity and key are known, so takes | ||||
416 | * different arguments. | ||||
417 | * @return ED_ERROR or ED_OK | ||||
418 | * @sa ED_GetLastError | ||||
419 | */ | ||||
420 | int ED_CheckKey (const entityKeyDef_t *kd, const char *value) | ||||
421 | { | ||||
422 | ED_TEST_RETURN_ERROR(!kd, "ED_CheckTypeEntityKey: null key def")if (!kd) { snprintf(lastErr, sizeof(lastErr), "ED_CheckTypeEntityKey: null key def" ); return -1; }; | ||||
423 | switch (kd->flags & ED_KEY_TYPE((1<<3) | (1<<4) | (1<<5))) { | ||||
424 | case ED_TYPE_FLOAT(1<<3): | ||||
425 | return ED_CheckNumericType(kd, value, ED_TYPE_FLOAT(1<<3)); | ||||
426 | case ED_TYPE_INT(1<<4): | ||||
427 | return ED_CheckNumericType(kd, value, ED_TYPE_INT(1<<4)); | ||||
428 | case ED_TYPE_STRING(1<<5): | ||||
429 | case 0: /* string is the default */ | ||||
430 | return ED_OK1; /* all strings are good */ | ||||
431 | default: | ||||
432 | ED_RETURN_ERROR("ED_CheckTypeEntityKey: type not recognised in key def"){ snprintf(lastErr, sizeof(lastErr), "ED_CheckTypeEntityKey: type not recognised in key def" ); return -1; }; | ||||
433 | } | ||||
434 | } | ||||
435 | |||||
436 | /** | ||||
437 | * @brief gets the default value for the key | ||||
438 | * @param[in] defaultBuf the calling function is responsible for ensuring this buffer | ||||
439 | * has enough space | ||||
440 | * @param n the number of chars the defualtBuf can hold | ||||
441 | * @param kd the key definition to get the default for. E | ||||
442 | * @sa D_GetKeyDef can be used to find kd. | ||||
443 | * @return ED_OK or ED_ERROR (no default, or n is too small). | ||||
444 | */ | ||||
445 | int ED_GetDefaultString (char *defaultBuf, const size_t n, const entityKeyDef_t *kd) | ||||
446 | { | ||||
447 | ED_TEST_RETURN_ERROR(n < 1, "ED_GetDefaultString: n is %lu", (unsigned long)n)if (n < 1) { snprintf(lastErr, sizeof(lastErr), "ED_GetDefaultString: n is %lu" , (unsigned long)n); return -1; }; | ||||
448 | defaultBuf[0] = '\0'; /* be safe, in case an error is found, and we return early */ | ||||
449 | ED_TEST_RETURN_ERROR(!kd->defaultVal, "ED_GetDefaultString: no default available")if (!kd->defaultVal) { snprintf(lastErr, sizeof(lastErr), "ED_GetDefaultString: no default available" ); return -1; }; | ||||
450 | strncpy(defaultBuf, kd->defaultVal, n - 1); | ||||
451 | ED_TEST_RETURN_ERROR(strlen(defaultBuf) < strlen(kd->defaultVal),if (strlen(defaultBuf) < strlen(kd->defaultVal)) { snprintf (lastErr, sizeof(lastErr), "ED_GetDefaultString: target buffer supplied is too small" ); return -1; } | ||||
452 | "ED_GetDefaultString: target buffer supplied is too small")if (strlen(defaultBuf) < strlen(kd->defaultVal)) { snprintf (lastErr, sizeof(lastErr), "ED_GetDefaultString: target buffer supplied is too small" ); return -1; }; | ||||
453 | return ED_OK1; | ||||
454 | } | ||||
455 | |||||
456 | /** | ||||
457 | * @brief takes a type string (eg "V_FLOAT 6") and configures entity def | ||||
458 | * @return ED_ERROR or ED_OK | ||||
459 | */ | ||||
460 | static int ED_ParseType (entityKeyDef_t *kd, const char *parsedToken) | ||||
461 | { | ||||
462 | static char tokBuf[64]; | ||||
463 | const char *buf_p; | ||||
464 | int type; | ||||
465 | int vectorLen; | ||||
466 | const char *partToken; | ||||
467 | int_float_tu parsedNumber; | ||||
468 | |||||
469 | /* need a copy, as parsedToken is held in a static buffer in the | ||||
470 | * Com_Parse function */ | ||||
471 | ED_TEST_RETURN_ERROR((strlen(parsedToken) + 1) > sizeof(tokBuf),if ((strlen(parsedToken) + 1) > sizeof(tokBuf)) { snprintf (lastErr, sizeof(lastErr), "ED_ParseType: type string too long for buffer for key %s" , kd->name); return -1; } | ||||
472 | "ED_ParseType: type string too long for buffer for key %s", kd->name)if ((strlen(parsedToken) + 1) > sizeof(tokBuf)) { snprintf (lastErr, sizeof(lastErr), "ED_ParseType: type string too long for buffer for key %s" , kd->name); return -1; }; | ||||
473 | strncpy(tokBuf, parsedToken, sizeof(tokBuf)); | ||||
474 | tokBuf[sizeof(tokBuf) - 1] = '\0'; | ||||
475 | buf_p = tokBuf; | ||||
476 | |||||
477 | partToken = Com_Parse(&buf_p); | ||||
478 | |||||
479 | if (Q_streq("SIGNED", partToken)(strcmp("SIGNED", partToken) == 0)) { | ||||
480 | partToken = Com_Parse(&buf_p);/* get next token */ | ||||
481 | } else if (Q_streq("UNSIGNED", partToken)(strcmp("UNSIGNED", partToken) == 0)) { | ||||
482 | kd->flags |= ED_INSIST_POSITIVE(1<<9); | ||||
483 | partToken = Com_Parse(&buf_p); | ||||
484 | } | ||||
485 | |||||
486 | if (partToken[0] != '\0') { | ||||
487 | type = ED_Type2Constant(partToken); | ||||
488 | ED_PASS_ERROR(type)if ((type) == -1) { return -1; }; | ||||
489 | } else {/* default is string */ | ||||
490 | type = ED_TYPE_STRING(1<<5); | ||||
491 | } | ||||
492 | |||||
493 | kd->flags |= type; | ||||
494 | partToken = Com_Parse(&buf_p); | ||||
495 | vectorLen = atoi(partToken); | ||||
496 | if (vectorLen) | ||||
497 | ED_TEST_RETURN_ERROR(ED_ERROR == ED_CheckNumber(partToken, ED_TYPE_INT, 1, &parsedNumber),if (-1 == ED_CheckNumber(partToken, (1<<4), 1, &parsedNumber )) { snprintf(lastErr, sizeof(lastErr), "ED_ParseType: problem with vector length \"%s\" in key %s" , partToken, kd->name); return -1; } | ||||
498 | "ED_ParseType: problem with vector length \"%s\" in key %s",if (-1 == ED_CheckNumber(partToken, (1<<4), 1, &parsedNumber )) { snprintf(lastErr, sizeof(lastErr), "ED_ParseType: problem with vector length \"%s\" in key %s" , partToken, kd->name); return -1; } | ||||
499 | partToken, kd->name)if (-1 == ED_CheckNumber(partToken, (1<<4), 1, &parsedNumber )) { snprintf(lastErr, sizeof(lastErr), "ED_ParseType: problem with vector length \"%s\" in key %s" , partToken, kd->name); return -1; }; | ||||
500 | kd->vLen = strlen(partToken) ? (vectorLen ? vectorLen : 1) : 1; /* default is 1 */ | ||||
501 | return ED_OK1; | ||||
502 | } | ||||
503 | |||||
504 | /** | ||||
505 | * @brief converts a block name (eg "optional") to an constant (eg ED_OPTIONAL). | ||||
506 | * @return the parse mode or ED_ERROR | ||||
507 | */ | ||||
508 | static int ED_Block2Constant (const char *blockName) | ||||
509 | { | ||||
510 | if (Q_streq("optional", blockName)(strcmp("optional", blockName) == 0)) | ||||
511 | return ED_OPTIONAL(1<<0); | ||||
512 | else if (Q_streq("mandatory", blockName)(strcmp("mandatory", blockName) == 0)) | ||||
513 | return ED_MANDATORY(1<<1); | ||||
514 | else if (Q_streq("abstract", blockName)(strcmp("abstract", blockName) == 0)) | ||||
515 | return ED_ABSTRACT(1<<2); | ||||
516 | else if (Q_streq("default", blockName)(strcmp("default", blockName) == 0)) | ||||
517 | return ED_DEFAULT(1<<6); | ||||
518 | else if (Q_streq("type", blockName)(strcmp("type", blockName) == 0)) | ||||
519 | return ED_MODE_TYPE(1<<7); | ||||
520 | else if (Q_streq("range", blockName)(strcmp("range", blockName) == 0)) | ||||
521 | return ED_RANGE(1<<8); | ||||
522 | else | ||||
523 | ED_RETURN_ERROR("parse mode not recognised"){ snprintf(lastErr, sizeof(lastErr), "parse mode not recognised" ); return -1; }; | ||||
524 | } | ||||
525 | |||||
526 | /** | ||||
527 | * @brief converts an internal constant integer to a string | ||||
528 | * representation of a type (eg V_FLOAT) | ||||
529 | * @return the string, or NULL if the integer is not recognised. | ||||
530 | */ | ||||
531 | static const char *ED_Constant2Block (int constInt) | ||||
532 | { | ||||
533 | switch (constInt) { | ||||
534 | case ED_OPTIONAL(1<<0): | ||||
535 | return "optional"; | ||||
536 | case ED_MANDATORY(1<<1): | ||||
537 | return "mandatory"; | ||||
538 | case ED_ABSTRACT(1<<2): | ||||
539 | return "abstract"; | ||||
540 | case ED_DEFAULT(1<<6): | ||||
541 | return "default"; | ||||
542 | case ED_MODE_TYPE(1<<7): | ||||
543 | return "type"; | ||||
544 | case ED_RANGE(1<<8): | ||||
545 | return "range"; | ||||
546 | default: | ||||
547 | snprintf(lastErr, sizeof(lastErr), "ED_Constant2Block: constant not recognised"); | ||||
548 | return NULL__null; | ||||
549 | } | ||||
550 | } | ||||
551 | |||||
552 | static int ED_AllocRange (entityKeyDef_t *kd, const char *rangeStr) | ||||
553 | { | ||||
554 | entityKeyRange_t **newRanges; | ||||
555 | /* start a new range */ | ||||
556 | char *newStr = strdup(rangeStr); | ||||
557 | entityKeyRange_t *newRange = (entityKeyRange_t *)malloc(sizeof(entityKeyRange_t)); | ||||
558 | OBJZERO(*newRange)(memset(&((*newRange)), (0), sizeof((*newRange)))); | ||||
559 | /* resize array of pointers */ | ||||
560 | newRanges = (entityKeyRange_t **)malloc((kd->numRanges + 1) * sizeof(entityKeyRange_t *)); | ||||
| |||||
561 | ED_TEST_RETURN_ERROR(!newRanges || !newStr || !newRange, "ED_AllocRange: out of memory")if (!newRanges || !newStr || !newRange) { snprintf(lastErr, sizeof (lastErr), "ED_AllocRange: out of memory"); return -1; }; | ||||
| |||||
562 | newRange->str = newStr; | ||||
563 | newRanges[kd->numRanges] = newRange; | ||||
564 | if (kd->ranges) { | ||||
565 | memcpy(newRanges, kd->ranges, kd->numRanges * sizeof(entityKeyRange_t *)); | ||||
566 | free(kd->ranges); | ||||
567 | } | ||||
568 | kd->numRanges++; | ||||
569 | kd->ranges = newRanges; | ||||
570 | return ED_OK1; | ||||
571 | } | ||||
572 | |||||
573 | /** | ||||
574 | * @return ED_ERROR or ED_OK. | ||||
575 | */ | ||||
576 | static int ED_PairParsed (entityKeyDef_t keyDefsBuf[], int *numKeyDefsSoFar_p, | ||||
577 | const char *newName, const char *newVal, const int mode) | ||||
578 | { | ||||
579 | /* check if there is already a key def */ | ||||
580 | entityKeyDef_t *keyDef = ED_FindKeyDefInArray(keyDefsBuf, *numKeyDefsSoFar_p, newName, mode); | ||||
581 | |||||
582 | /* create one if required */ | ||||
583 | if (!keyDef) { | ||||
| |||||
584 | keyDef = &keyDefsBuf[(*numKeyDefsSoFar_p)++]; | ||||
585 | ED_TEST_RETURN_ERROR(*numKeyDefsSoFar_p >= ED_MAX_KEYS_PER_ENT, "ED_PairParsed: too many keys for buffer")if (*numKeyDefsSoFar_p >= 32) { snprintf(lastErr, sizeof(lastErr ), "ED_PairParsed: too many keys for buffer"); return -1; }; | ||||
586 | keyDef->name = strdup(newName); | ||||
587 | ED_TEST_RETURN_ERROR(!keyDef->name, "ED_PairParsed: out of memory")if (!keyDef->name) { snprintf(lastErr, sizeof(lastErr), "ED_PairParsed: out of memory" ); return -1; }; | ||||
588 | } | ||||
589 | |||||
590 | /* multiple range defs are permitted, different elements can have different ranges */ | ||||
591 | ED_TEST_RETURN_ERROR(keyDef->flags & mode & ~ED_RANGE,if (keyDef->flags & mode & ~(1<<8)) { snprintf (lastErr, sizeof(lastErr), "Duplicate %s for %s key. second value: %s" , ED_Constant2Block(mode), newName, newVal); return -1; } | ||||
592 | "Duplicate %s for %s key. second value: %s", ED_Constant2Block(mode), newName, newVal)if (keyDef->flags & mode & ~(1<<8)) { snprintf (lastErr, sizeof(lastErr), "Duplicate %s for %s key. second value: %s" , ED_Constant2Block(mode), newName, newVal); return -1; }; | ||||
593 | |||||
594 | keyDef->flags |= mode; | ||||
595 | |||||
596 | /* store information */ | ||||
597 | switch (mode) { | ||||
| |||||
598 | case ED_MANDATORY(1<<1): | ||||
599 | case ED_OPTIONAL(1<<0): | ||||
600 | case ED_ABSTRACT(1<<2): /* intentional fall-through */ | ||||
601 | keyDef->desc = strdup(newVal); | ||||
602 | ED_TEST_RETURN_ERROR(!keyDef->desc, "ED_PairParsed: out of memory while storing string.")if (!keyDef->desc) { snprintf(lastErr, sizeof(lastErr), "ED_PairParsed: out of memory while storing string." ); return -1; }; | ||||
603 | return ED_OK1; | ||||
604 | case ED_DEFAULT(1<<6): | ||||
605 | keyDef->defaultVal = strdup(newVal); | ||||
606 | ED_TEST_RETURN_ERROR(!keyDef->defaultVal, "ED_PairParsed: out of memory while storing string.")if (!keyDef->defaultVal) { snprintf(lastErr, sizeof(lastErr ), "ED_PairParsed: out of memory while storing string."); return -1; }; | ||||
607 | return ED_OK1; | ||||
608 | case ED_MODE_TYPE(1<<7): | ||||
609 | /* only optional or mandatory keys may have types, not possible to test for this here, | ||||
610 | * as the type block may come before the optional or mandatory block */ | ||||
611 | ED_PASS_ERROR(ED_ParseType(keyDef, newVal))if ((ED_ParseType(keyDef, newVal)) == -1) { return -1; }; | ||||
612 | return ED_OK1; | ||||
613 | case ED_RANGE(1<<8): | ||||
614 | /* only typed keys may have ranges, this may only be tested after | ||||
615 | * the whole ent has been parsed: the blocks may come in any order */ | ||||
616 | ED_PASS_ERROR(ED_AllocRange(keyDef, newVal))if ((ED_AllocRange(keyDef, newVal)) == -1) { return -1; }; | ||||
| |||||
617 | return ED_OK1; | ||||
618 | default: | ||||
619 | ED_RETURN_ERROR("ED_PairParsed: parse mode not recognised"){ snprintf(lastErr, sizeof(lastErr), "ED_PairParsed: parse mode not recognised" ); return -1; }; | ||||
620 | } | ||||
621 | } | ||||
622 | |||||
623 | /** | ||||
624 | * @return ED_ERROR or ED_OK | ||||
625 | */ | ||||
626 | static int ED_ParseEntities (const char **data_p) | ||||
627 | { | ||||
628 | int braceLevel = 0; | ||||
629 | int tokensOnLevel0 = 0; | ||||
630 | int mode = ED_ABSTRACT(1<<2); | ||||
631 | entityKeyDef_t keyDefBuf[ED_MAX_KEYS_PER_ENT32]; | ||||
632 | char lastTokenBuf[ED_MAX_TOKEN_LEN512]; | ||||
633 | int keyIndex = 0; | ||||
634 | int toggle = 0; /* many lines should have a pair of tokens on, this toggles 0, 1 to indicate progress */ | ||||
635 | |||||
636 | while (data_p) { | ||||
| |||||
| |||||
| |||||
| |||||
637 | const char *parsedToken = Com_Parse(data_p); | ||||
638 | toggle ^= 1; | ||||
639 | |||||
640 | if (parsedToken[0] == '\0' && braceLevel == 0) | ||||
| |||||
| |||||
| |||||
| |||||
641 | break; | ||||
642 | |||||
643 | if (parsedToken[0] == '{') { | ||||
| |||||
| |||||
| |||||
| |||||
644 | braceLevel++; | ||||
645 | ED_TEST_RETURN_ERROR(braceLevel > 2, "Too many open braces, nested %i deep", braceLevel)if (braceLevel > 2) { snprintf(lastErr, sizeof(lastErr), "Too many open braces, nested %i deep" , braceLevel); return -1; }; | ||||
646 | ED_TEST_RETURN_ERROR(!toggle, "ED_ParseEntities: Incorrect number of tokens before '{'")if (!toggle) { snprintf(lastErr, sizeof(lastErr), "ED_ParseEntities: Incorrect number of tokens before '{'" ); return -1; }; | ||||
647 | toggle ^= 1; /* reset, as toggle is only for counting proper text tokens, not braces */ | ||||
648 | tokensOnLevel0 = 0; | ||||
649 | continue; | ||||
| |||||
650 | } | ||||
651 | |||||
652 | if (parsedToken[0] == '}') { | ||||
| |||||
| |||||
| |||||
653 | braceLevel--; | ||||
654 | ED_TEST_RETURN_ERROR(braceLevel < 0, "Too many close braces. after %i entities", numEntityDefs)if (braceLevel < 0) { snprintf(lastErr, sizeof(lastErr), "Too many close braces. after %i entities" , numEntityDefs); return -1; }; | ||||
655 | toggle ^= 1; /* reset, as toggle is only for counting proper text tokens, not braces */ | ||||
656 | if (braceLevel == 0) { /* finished parsing entity def and prepare for the next one */ | ||||
657 | ED_PASS_ERROR(ED_AllocEntityDef(keyDefBuf, keyIndex, numEntityDefs))if ((ED_AllocEntityDef(keyDefBuf, keyIndex, numEntityDefs)) == -1) { return -1; }; | ||||
658 | numEntityDefs++; | ||||
659 | ED_TEST_RETURN_ERROR(numEntityDefs >= ED_MAX_DEFS, "ED_ParseEntities: too many entity defs for buffer")if (numEntityDefs >= 64) { snprintf(lastErr, sizeof(lastErr ), "ED_ParseEntities: too many entity defs for buffer"); return -1; }; | ||||
660 | } | ||||
661 | if (braceLevel == 1) /* ending a default, mandatory, etc block, go back to default parse mode */ | ||||
662 | mode = ED_ABSTRACT(1<<2); | ||||
663 | |||||
664 | continue; | ||||
665 | } | ||||
666 | |||||
667 | if (braceLevel == 0) { | ||||
| |||||
| |||||
| |||||
668 | if (tokensOnLevel0 == 0 && !Q_streq(parsedToken, "entity")(strcmp(parsedToken, "entity") == 0)) | ||||
669 | ED_RETURN_ERROR( "Next entity expected, found \"%s\"", parsedToken){ snprintf(lastErr, sizeof(lastErr), "Next entity expected, found \"%s\"" , parsedToken); return -1; }; | ||||
670 | |||||
671 | if (tokensOnLevel0 == 1) {/* classname of entity, start parsing new entity */ | ||||
672 | const entityDef_t *prevED = ED_GetEntityDef(parsedToken); | ||||
673 | ED_TEST_RETURN_ERROR(prevED, "ED_ParseEntities: duplicate entity definition \"%s\"", parsedToken)if (prevED) { snprintf(lastErr, sizeof(lastErr), "ED_ParseEntities: duplicate entity definition \"%s\"" , parsedToken); return -1; }; | ||||
674 | OBJZERO(keyDefBuf)(memset(&((keyDefBuf)), (0), sizeof((keyDefBuf)))); /* ensure pointers are not carried over from previous entity */ | ||||
675 | keyIndex = 0; | ||||
676 | ED_PASS_ERROR(ED_PairParsed(keyDefBuf, &keyIndex, "classname", parsedToken, ED_MANDATORY))if ((ED_PairParsed(keyDefBuf, &keyIndex, "classname", parsedToken , (1<<1))) == -1) { return -1; }; | ||||
677 | mode = ED_ABSTRACT(1<<2); | ||||
678 | } | ||||
679 | |||||
680 | if (tokensOnLevel0 > 1) | ||||
681 | ED_RETURN_ERROR( "Start of entity block expected found \"%s\"", parsedToken){ snprintf(lastErr, sizeof(lastErr), "Start of entity block expected found \"%s\"" , parsedToken); return -1; }; | ||||
682 | |||||
683 | tokensOnLevel0++; | ||||
684 | } else { /* braceLevel > 0 */ | ||||
685 | const int newMode = ED_Block2Constant(parsedToken); | ||||
686 | if (ED_ERROR-1 == newMode) { /* must be a key name or value */ | ||||
| |||||
| |||||
| |||||
687 | if (toggle) { /* store key name til after next token is parsed */ | ||||
| |||||
| |||||
688 | if ('\0' == parsedToken[0]) | ||||
| |||||
689 | ED_RETURN_ERROR("key name null string, \"\", or missing closing brace"){ snprintf(lastErr, sizeof(lastErr), "key name null string, \"\", or missing closing brace" ); return -1; }; | ||||
690 | strncpy(lastTokenBuf, parsedToken, sizeof(lastTokenBuf)); | ||||
691 | lastTokenBuf[sizeof(lastTokenBuf) - 1] = '\0'; | ||||
692 | } else { /* store key-value pair in buffer until whole entity is parsed */ | ||||
693 | ED_PASS_ERROR(ED_PairParsed(keyDefBuf, &keyIndex, lastTokenBuf, parsedToken, mode))if ((ED_PairParsed(keyDefBuf, &keyIndex, lastTokenBuf, parsedToken , mode)) == -1) { return -1; }; | ||||
694 | } | ||||
695 | } else { | ||||
696 | mode = newMode; | ||||
697 | toggle ^= 1; /* start of a mode changing block is the only time that only one non-brace token is on a line */ | ||||
698 | } | ||||
699 | } | ||||
700 | } | ||||
701 | |||||
702 | return ED_OK1; | ||||
703 | } | ||||
704 | |||||
705 | /** | ||||
706 | * @brief checks if the default block entries meet the type and range definitions. | ||||
707 | * @return ED_ERROR or ED_OK | ||||
708 | * @sa CheckLastError | ||||
709 | */ | ||||
710 | static int ED_CheckDefaultTypes (void) | ||||
711 | { | ||||
712 | const entityDef_t *ed; | ||||
713 | const entityKeyDef_t *kd; | ||||
714 | for (ed = entityDefs; ed->numKeyDefs; ed++) | ||||
715 | for (kd = ed->keyDefs; kd->name; kd++) | ||||
716 | if (kd->defaultVal) | ||||
717 | ED_PASS_ERROR_EXTRAMSG(ED_CheckKey(kd, kd->defaultVal),if ((ED_CheckKey(kd, kd->defaultVal)) == -1) { snprintf(lastErrExtra , sizeof(lastErr), " while checking default block entry agrees with type" ); strncat(lastErr, lastErrExtra, sizeof(lastErr) - strlen(lastErr ) -1); return -1; } | ||||
718 | " while checking default block entry agrees with type")if ((ED_CheckKey(kd, kd->defaultVal)) == -1) { snprintf(lastErrExtra , sizeof(lastErr), " while checking default block entry agrees with type" ); strncat(lastErr, lastErrExtra, sizeof(lastErr) - strlen(lastErr ) -1); return -1; } | ||||
719 | |||||
720 | return ED_OK1; | ||||
721 | } | ||||
722 | |||||
723 | /** | ||||
724 | * @brief finish parsing ranges. Could not be done earlier as would not have necessarily known | ||||
725 | * types and defaults. parses values in ranges into ints or floats and tests ranges against types | ||||
726 | * and defaults against ranges. | ||||
727 | * @return ED_ERROR or ED_OK | ||||
728 | */ | ||||
729 | static int ED_ProcessRanges (void) | ||||
730 | { | ||||
731 | static int ibuf[32]; | ||||
732 | static float fbuf[32]; | ||||
733 | |||||
734 | entityDef_t *ed; | ||||
735 | for (ed = entityDefs; ed->numKeyDefs; ed++) { | ||||
736 | entityKeyDef_t *kd; | ||||
737 | for (kd = ed->keyDefs; kd->name; kd++) { | ||||
738 | const int keyType = kd->flags & ED_KEY_TYPE((1<<3) | (1<<4) | (1<<5)); | ||||
739 | int i; | ||||
740 | for (i = 0; i < kd->numRanges ;i++) { | ||||
741 | int numElements = 0; | ||||
742 | entityKeyRange_t *kr = kd->ranges[i]; | ||||
743 | const char *tmpRange_p = kr->str; | ||||
744 | ED_TEST_RETURN_ERROR(!keyType || (keyType & ED_TYPE_STRING), "ED_ProcessRanges: ranges may not be specified for strings. note that V_STRING is the default type. %s in %s",if (!keyType || (keyType & (1<<5))) { snprintf(lastErr , sizeof(lastErr), "ED_ProcessRanges: ranges may not be specified for strings. note that V_STRING is the default type. %s in %s" , kd->name, ed->classname); return -1; } | ||||
745 | kd->name, ed->classname)if (!keyType || (keyType & (1<<5))) { snprintf(lastErr , sizeof(lastErr), "ED_ProcessRanges: ranges may not be specified for strings. note that V_STRING is the default type. %s in %s" , kd->name, ed->classname); return -1; }; | ||||
746 | while (tmpRange_p) { | ||||
747 | int_float_tu parsedNumber; | ||||
748 | const char *tok = Com_Parse(&tmpRange_p); | ||||
749 | if (tok[0] == '\0') | ||||
750 | break; | ||||
751 | if (Q_streq("-", tok)(strcmp("-", tok) == 0)) { | ||||
752 | kr->continuous = 1; | ||||
753 | ED_TEST_RETURN_ERROR(numElements != 1, "ED_ProcessRanges: problem with continuous range, \"%s\" in %s in %s",if (numElements != 1) { snprintf(lastErr, sizeof(lastErr), "ED_ProcessRanges: problem with continuous range, \"%s\" in %s in %s" , kr->str, kd->name, ed->classname); return -1; } | ||||
754 | kr->str, kd->name, ed->classname)if (numElements != 1) { snprintf(lastErr, sizeof(lastErr), "ED_ProcessRanges: problem with continuous range, \"%s\" in %s in %s" , kr->str, kd->name, ed->classname); return -1; }; | ||||
755 | continue; | ||||
756 | } | ||||
757 | ED_PASS_ERROR(ED_CheckNumber(tok, keyType, kd->flags & ED_INSIST_POSITIVE, &parsedNumber))if ((ED_CheckNumber(tok, keyType, kd->flags & (1<< 9), &parsedNumber)) == -1) { return -1; }; | ||||
758 | switch (keyType) { | ||||
759 | case ED_TYPE_INT(1<<4): | ||||
760 | ibuf[numElements++] = atoi(tok); | ||||
761 | break; | ||||
762 | case ED_TYPE_FLOAT(1<<3): | ||||
763 | fbuf[numElements++] = atof(tok); | ||||
764 | break; | ||||
765 | default: | ||||
766 | ED_RETURN_ERROR("ED_ProcessRanges: unexpected type"){ snprintf(lastErr, sizeof(lastErr), "ED_ProcessRanges: unexpected type" ); return -1; }; | ||||
767 | } | ||||
768 | } | ||||
769 | kr->numElements = numElements; | ||||
770 | ED_TEST_RETURN_ERROR(kr->continuous && numElements != 2,if (kr->continuous && numElements != 2) { snprintf (lastErr, sizeof(lastErr), "ED_ProcessRanges: continuous range should only have 2 elements, upper and lower bounds, \"%s\" in %s in %s" , kr->str, kd->name, ed->classname); return -1; } | ||||
771 | "ED_ProcessRanges: continuous range should only have 2 elements, upper and lower bounds, \"%s\" in %s in %s",if (kr->continuous && numElements != 2) { snprintf (lastErr, sizeof(lastErr), "ED_ProcessRanges: continuous range should only have 2 elements, upper and lower bounds, \"%s\" in %s in %s" , kr->str, kd->name, ed->classname); return -1; } | ||||
772 | kr->str, kd->name, ed->classname)if (kr->continuous && numElements != 2) { snprintf (lastErr, sizeof(lastErr), "ED_ProcessRanges: continuous range should only have 2 elements, upper and lower bounds, \"%s\" in %s in %s" , kr->str, kd->name, ed->classname); return -1; }; | ||||
773 | if (ED_TYPE_INT(1<<4) == keyType) { | ||||
774 | const size_t size = numElements * sizeof(int); | ||||
775 | kr->iArr = (int *)malloc(size); | ||||
776 | ED_TEST_RETURN_ERROR(!kr->iArr, "ED_ProcessRanges: out of memory")if (!kr->iArr) { snprintf(lastErr, sizeof(lastErr), "ED_ProcessRanges: out of memory" ); return -1; }; | ||||
777 | memcpy(kr->iArr, ibuf, size); | ||||
778 | } else { /* ED_TYPE_FLOAT */ | ||||
779 | const size_t size = numElements * sizeof(float); | ||||
780 | kr->fArr = (float *)malloc(size); | ||||
781 | ED_TEST_RETURN_ERROR(!kr->fArr, "ED_ProcessRanges: out of memory")if (!kr->fArr) { snprintf(lastErr, sizeof(lastErr), "ED_ProcessRanges: out of memory" ); return -1; }; | ||||
782 | memcpy(kr->fArr, fbuf, size); | ||||
783 | } | ||||
784 | } | ||||
785 | ED_TEST_RETURN_ERROR(kd->numRanges && kd->numRanges != 1 && kd->vLen != kd->numRanges,if (kd->numRanges && kd->numRanges != 1 && kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof( lastErr), "ED_ProcessRanges: if range definitions are supplied, " "there must be one (which is applied to each element of a vector), " "or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions" , ed->classname, kd->name, kd->vLen, kd->numRanges ); return -1; } | ||||
786 | "ED_ProcessRanges: if range definitions are supplied, "if (kd->numRanges && kd->numRanges != 1 && kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof( lastErr), "ED_ProcessRanges: if range definitions are supplied, " "there must be one (which is applied to each element of a vector), " "or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions" , ed->classname, kd->name, kd->vLen, kd->numRanges ); return -1; } | ||||
787 | "there must be one (which is applied to each element of a vector), "if (kd->numRanges && kd->numRanges != 1 && kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof( lastErr), "ED_ProcessRanges: if range definitions are supplied, " "there must be one (which is applied to each element of a vector), " "or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions" , ed->classname, kd->name, kd->vLen, kd->numRanges ); return -1; } | ||||
788 | "or one for each element of the vector. "if (kd->numRanges && kd->numRanges != 1 && kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof( lastErr), "ED_ProcessRanges: if range definitions are supplied, " "there must be one (which is applied to each element of a vector), " "or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions" , ed->classname, kd->name, kd->vLen, kd->numRanges ); return -1; } | ||||
789 | "%s in %s has %i elements in vector and %i range definitions",if (kd->numRanges && kd->numRanges != 1 && kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof( lastErr), "ED_ProcessRanges: if range definitions are supplied, " "there must be one (which is applied to each element of a vector), " "or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions" , ed->classname, kd->name, kd->vLen, kd->numRanges ); return -1; } | ||||
790 | ed->classname, kd->name, kd->vLen, kd->numRanges)if (kd->numRanges && kd->numRanges != 1 && kd->vLen != kd->numRanges) { snprintf(lastErr, sizeof( lastErr), "ED_ProcessRanges: if range definitions are supplied, " "there must be one (which is applied to each element of a vector), " "or one for each element of the vector. " "%s in %s has %i elements in vector and %i range definitions" , ed->classname, kd->name, kd->vLen, kd->numRanges ); return -1; }; | ||||
791 | } | ||||
792 | } | ||||
793 | return ED_OK1; | ||||
794 | } | ||||
795 | |||||
796 | /** | ||||
797 | * parses entity definitions from entities.ufo | ||||
798 | * @return ED_OK or ED_ERR | ||||
799 | * @sa ED_GetLastErr | ||||
800 | */ | ||||
801 | int ED_Parse (const char *data_p) | ||||
802 | { | ||||
803 | /* only do this once, repeat calls are OK */ | ||||
804 | static int done = 0; | ||||
805 | if (done) | ||||
| |||||
806 | return ED_OK1; | ||||
807 | |||||
808 | snprintf(lastErr, sizeof(lastErr), "no error"); | ||||
809 | /* Zero out so that looping through the ones that have already | ||||
810 | * been parsed is possible while the rest are parsed */ | ||||
811 | OBJZERO(entityDefs)(memset(&((entityDefs)), (0), sizeof((entityDefs)))); | ||||
812 | numEntityDefs = 0; | ||||
813 | |||||
814 | ED_PASS_ERROR(ED_ParseEntities(&data_p))if ((ED_ParseEntities(&data_p)) == -1) { return -1; }; | ||||
| |||||
815 | ED_TEST_RETURN_ERROR(numEntityDefs == 0, "ED_Parse: Zero entity definitions found")if (numEntityDefs == 0) { snprintf(lastErr, sizeof(lastErr), "ED_Parse: Zero entity definitions found" ); return -1; }; | ||||
816 | |||||
817 | ED_PASS_ERROR(ED_ProcessRanges())if ((ED_ProcessRanges()) == -1) { return -1; }; | ||||
818 | |||||
819 | ED_PASS_ERROR(ED_CheckDefaultTypes())if ((ED_CheckDefaultTypes()) == -1) { return -1; }; | ||||
820 | |||||
821 | done = 1; /* do not do it again */ | ||||
822 | |||||
823 | #ifdef DEBUG_ED | ||||
824 | ED_Dump(); | ||||
825 | #endif | ||||
826 | |||||
827 | return ED_OK1; | ||||
828 | } | ||||
829 | |||||
830 | const char *ED_GetLastError (void) | ||||
831 | { | ||||
832 | return lastErr; | ||||
833 | } | ||||
834 | |||||
835 | /** | ||||
836 | * @brief searches for the parsed key def | ||||
837 | * @param abstract send abstract to find an abstract key with this name | ||||
838 | * @return NULL if the entity def or key def is not found. call ED_GetLastError to get a relevant message. | ||||
839 | */ | ||||
840 | const entityKeyDef_t *ED_GetKeyDef (const char *classname, const char *keyname, const int abstract) | ||||
841 | { | ||||
842 | const entityDef_t *ed = ED_GetEntityDef(classname); | ||||
843 | return ED_GetKeyDefEntity(ed, keyname, abstract); | ||||
844 | } | ||||
845 | |||||
846 | /** | ||||
847 | * @brief searches for the parsed key def, when the entity def is known | ||||
848 | * @param abstract send a nonzero value if the abstract (radiant - not in any block) version of the | ||||
849 | * key is required | ||||
850 | * @return NULL if the entity def or key def is not found. call ED_GetLastError to get a relevant message. | ||||
851 | */ | ||||
852 | const entityKeyDef_t *ED_GetKeyDefEntity (const entityDef_t *ed, const char *keyname, const int abstract) | ||||
853 | { | ||||
854 | const entityKeyDef_t *kd; | ||||
855 | |||||
856 | if (!ed) | ||||
857 | return NULL__null; | ||||
858 | |||||
859 | for (kd = ed->keyDefs; kd->name; kd++) | ||||
860 | if (Q_streq(keyname, kd->name)(strcmp(keyname, kd->name) == 0)) { | ||||
861 | if (abstract) { | ||||
862 | if (kd->flags & ED_ABSTRACT(1<<2)) | ||||
863 | return kd; | ||||
864 | } else { | ||||
865 | if (!(kd->flags & ED_ABSTRACT(1<<2))) | ||||
866 | return kd; | ||||
867 | } | ||||
868 | } | ||||
869 | |||||
870 | snprintf(lastErr, sizeof(lastErr), "ED_GetKeyDefEntity: no key definition for %s found in entity %s entities.ufo", keyname, ed->classname); | ||||
871 | return NULL__null; | ||||
872 | } | ||||
873 | |||||
874 | /** | ||||
875 | * @brief searches for the parsed entity def by classname | ||||
876 | * @return NULL if the entity def is not found. call ED_GetLastError to get a relevant message. | ||||
877 | */ | ||||
878 | const entityDef_t *ED_GetEntityDef (const char *classname) | ||||
879 | { | ||||
880 | const entityDef_t *ed; | ||||
881 | |||||
882 | for (ed = entityDefs; ed->numKeyDefs; ed++) | ||||
883 | if (Q_streq(classname, ed->classname)(strcmp(classname, ed->classname) == 0)) | ||||
884 | return ed; | ||||
885 | |||||
886 | snprintf(lastErr, sizeof(lastErr), "ED_GetEntityDef: no entity definition for %s found in entities.ufo", classname); | ||||
887 | return NULL__null; | ||||
888 | } | ||||
889 | |||||
890 | void ED_Free (void) | ||||
891 | { | ||||
892 | if (numEntityDefs) { | ||||
893 | for (entityDef_t* ed = entityDefs; ed->numKeyDefs; ++ed) { | ||||
894 | free(ed->classname); | ||||
895 | for (entityKeyDef_t* kd = ed->keyDefs; kd->name; ++kd) { | ||||
896 | free(kd->name); | ||||
897 | free(kd->desc); | ||||
898 | free(kd->defaultVal); | ||||
899 | if (kd->numRanges) { | ||||
900 | int i; | ||||
901 | for (i = 0; i < kd->numRanges ;i++) { | ||||
902 | entityKeyRange_t *kr = kd->ranges[i]; | ||||
903 | free(kr->iArr); | ||||
904 | free(kr->fArr); | ||||
905 | free(kr->str); | ||||
906 | free(kr); | ||||
907 | } | ||||
908 | free(kd->ranges); | ||||
909 | } | ||||
910 | } | ||||
911 | free(ed->keyDefs); | ||||
912 | } | ||||
913 | } | ||||
914 | } | ||||
915 | |||||
916 | #ifdef DEBUG_ED | ||||
917 | |||||
918 | void ED_PrintRange (const entityKeyRange_t *kr) | ||||
919 | { | ||||
920 | int i; | ||||
921 | printf(" rge:%c:%c>", kr->continuous ? 'c' : 'd', kr->iArr ? 'i' : 'f'); | ||||
922 | for (i = 0; i < kr->numElements; i++) { | ||||
923 | if (kr->iArr) | ||||
924 | printf("%i ", kr->iArr[i]); | ||||
925 | else | ||||
926 | printf("%.1f ",kr->fArr[i]); | ||||
927 | } | ||||
928 | printf("\b<"); | ||||
929 | } | ||||
930 | |||||
931 | void ED_PrintParsedDefault (const entityKeyDef_t *kd) | ||||
932 | { | ||||
933 | float fa[32]; | ||||
934 | int ia[32]; | ||||
935 | char ca[128]; | ||||
936 | int i; | ||||
937 | |||||
938 | if (!kd->defaultVal) | ||||
939 | return; | ||||
940 | |||||
941 | printf(" parsed default>"); | ||||
942 | |||||
943 | switch (kd->flags & ED_KEY_TYPE((1<<3) | (1<<4) | (1<<5))) { | ||||
944 | case ED_TYPE_INT(1<<4): | ||||
945 | ED_GetDefaultInt(ia, kd->vLen, kd); | ||||
946 | for (i = 0; i < kd->vLen; i++) | ||||
947 | printf("%i ", ia[i]); | ||||
948 | break; | ||||
949 | case ED_TYPE_FLOAT(1<<3): | ||||
950 | ED_GetDefaultFloat(fa, kd->vLen, kd); | ||||
951 | for (i = 0; i < kd->vLen; i++) | ||||
952 | printf("%f ", fa[i]); | ||||
953 | break; | ||||
954 | case ED_TYPE_STRING(1<<5): | ||||
955 | ED_GetDefaultString(ca, sizeof(ca) - 1, kd); | ||||
956 | printf("%s", ca); | ||||
957 | break; | ||||
958 | } | ||||
959 | |||||
960 | printf("<"); | ||||
961 | } | ||||
962 | |||||
963 | void ED_PrintKeyDef (const entityKeyDef_t *kd) | ||||
964 | { | ||||
965 | printf(" >%s< mandtry:%i opt:%i abst:%i type:%i", kd->name, | ||||
966 | kd->flags & ED_MANDATORY(1<<1) ? 1 : 0, | ||||
967 | kd->flags & ED_OPTIONAL(1<<0) ? 1 : 0, | ||||
968 | kd->flags & ED_ABSTRACT(1<<2) ? 1 : 0, | ||||
969 | kd->flags & ED_MODE_TYPE(1<<7) ? 1 : 0); | ||||
970 | if (kd->flags & ED_MODE_TYPE(1<<7)) | ||||
971 | printf(" type:%s[%i]", ED_Constant2Type(kd->flags & ED_KEY_TYPE((1<<3) | (1<<4) | (1<<5))), kd->vLen); | ||||
972 | |||||
973 | if (kd->defaultVal) | ||||
974 | printf(" default>%s<",kd->defaultVal); | ||||
975 | |||||
976 | if (kd->numRanges) { | ||||
977 | int i; | ||||
978 | for (i = 0; i < kd->numRanges; i++) { | ||||
979 | ED_PrintRange(kd->ranges[i]); | ||||
980 | } | ||||
981 | } | ||||
982 | |||||
983 | ED_PrintParsedDefault(kd); | ||||
984 | |||||
985 | printf("\n"); | ||||
986 | } | ||||
987 | |||||
988 | /** | ||||
989 | * @brief print all the parsed entity definitions | ||||
990 | */ | ||||
991 | void ED_Dump (void) | ||||
992 | { | ||||
993 | const entityDef_t *ed; | ||||
994 | printf("ED_Dump:\n"); | ||||
995 | for (ed = entityDefs; ed->numKeyDefs; ed++) { | ||||
996 | const entityKeyDef_t *kd; | ||||
997 | printf("entity def >%s<\n", ed->classname); | ||||
998 | for (kd = &ed->keyDefs[0]; kd->name; kd++) { | ||||
999 | ED_PrintKeyDef(kd); | ||||
1000 | } | ||||
1001 | } | ||||
1002 | } | ||||
1003 | #endif |