File: | shared/entitiesdef.cpp |
Location: | line 561, column 2 |
Description: | Memory is never released; potential leak of memory pointed to by 'newStr' |
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 |