UFO: Alien Invasion
Loading...
Searching...
No Matches
entitiesdef.cpp
Go to the documentation of this file.
1
5
6/*
7All original material Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9Copyright (C) 1997-2001 Id Software, Inc.
10
11This program is free software; you can redistribute it and/or
12modify it under the terms of the GNU General Public License
13as published by the Free Software Foundation; either version 2
14of the License, or (at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
20See the GNU General Public License for more details.
21
22You should have received a copy of the GNU General Public License
23along with this program; if not, write to the Free Software
24Foundation, 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_ENT 32
39#define ED_MAX_TOKEN_LEN 512
40#define ED_MAX_ERR_LEN 512
41
42static char lastErr[ED_MAX_ERR_LEN];
44
47
52#define ED_RETURN_ERROR(...) \
53 { \
54 snprintf(lastErr, sizeof(lastErr), __VA_ARGS__); \
55 return ED_ERROR; \
56 }
57
62#define ED_TEST_RETURN_ERROR(condition,...) \
63 if (condition) { \
64 snprintf(lastErr, sizeof(lastErr), __VA_ARGS__); \
65 return ED_ERROR; \
66 }
67
73#define ED_PASS_ERROR(function_call) \
74 if ((function_call) == ED_ERROR) { \
75 return ED_ERROR; \
76 }
77
84#define ED_PASS_ERROR_EXTRAMSG(function_call,...) \
85 if ((function_call) == ED_ERROR) { \
86 snprintf(lastErrExtra, sizeof(lastErr), __VA_ARGS__); \
87 strncat(lastErr, lastErrExtra, sizeof(lastErr) - strlen(lastErr) -1); \
88 return ED_ERROR; \
89 }
90
95static 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");
102 eDef->numKeyDefs = numKeyDefs;
103
104 /* and copy from temp buffer */
105 memcpy(eDef->keyDefs, newKeyDefs, numKeyDefs * sizeof(entityKeyDef_t));
106
107 /* set nullptrs at the end, to enable looping through using a pointer */
108 OBJZERO(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");
113
114 return ED_OK;
115}
116
121static entityKeyDef_t* ED_FindKeyDefInArray (entityKeyDef_t keyDefs[], int numDefs, const char* name, int parseMode)
122{
123 for (int i = 0; i < numDefs; i++) {
124 const entityKeyDef_t* keyDef = &keyDefs[i];
125 /* names equal. both abstract or both not abstract */
126 if (Q_streq(keyDef->name, name) && !((keyDef->flags ^ parseMode) & ED_ABSTRACT)) {
127 return &keyDefs[i];
128 }
129 }
130 return nullptr;
131}
132
138static int ED_Type2Constant (const char* strType)
139{
140 if (Q_streq(strType, "V_FLOAT"))
141 return ED_TYPE_FLOAT;
142 else if (Q_streq(strType, "V_INT"))
143 return ED_TYPE_INT;
144 else if (Q_streq(strType, "V_BOOL"))
145 return ED_TYPE_BOOL;
146 else if (Q_streq(strType, "V_STRING"))
147 return ED_TYPE_STRING;
148
149 ED_RETURN_ERROR("ED_Type2Constant: type string not recognised: \"%s\"", strType);
150}
151
157static const char* ED_Constant2Type (int constInt)
158{
159 switch (constInt) {
160 case ED_TYPE_FLOAT:
161 return "V_FLOAT";
162 case ED_TYPE_BOOL:
163 return "V_BOOL";
164 case ED_TYPE_INT:
165 return "V_INT";
166 case ED_TYPE_STRING:
167 return "V_STRING";
168 default:
169 snprintf(lastErr, sizeof(lastErr), "ED_Constant2Type: constant not recognised");
170 return nullptr;
171 }
172}
173
183static int ED_GetIntVectorFromString (const char* str, int v[], const int n)
184{
185 int i;
186 const char* buf_p = str;
187
188 for (i = 0; buf_p; i++) {
189 const char* tok = Com_Parse(&buf_p);
190 if (tok[0] == '\0')
191 break; /* previous tok was the last real one, don't waste time */
192 ED_TEST_RETURN_ERROR(i >= n, "ED_GetIntVectorFromString: v[%i] too small for ints from string \"%s\"", n, str);
193 v[i] = atoi(tok);
194 }
195 ED_TEST_RETURN_ERROR(i != n, "ED_GetIntVectorFromString: v[%i] wrong size for ints from string \"%s\"", n, str);
196 return ED_OK;
197}
198
208int ED_GetIntVector (const entityKeyDef_t* kd, int v[], const int n)
209{
211 return ED_OK;
212}
213
226static int ED_CheckNumber (const char* value, const int floatOrInt, const int insistPositive, int_float_tu* parsedNumber)
227{
228 char* end_p;
229 /* V_INTs are protected from octal and hex as strtol with base 10 is used.
230 * this test is useful for V_INT, as it gives a specific error message.
231 * V_FLOATs are not protected from hex, inf, nan, so this check is here.
232 * strstr is used for hex, as 0x may not be the start of the string.
233 * eg -0x0.2 is a negative hex float */
234 ED_TEST_RETURN_ERROR(value[0] == 'i' || value[0] == 'I' || value[0] == 'n' || value[0] == 'N'
235 || strstr(value, "0x") || strstr(value, "0X"),
236 "infinity, NaN, hex (0x...) not allowed. found \"%s\"", value);
237 switch (floatOrInt) {
238 case ED_TYPE_FLOAT:
239 parsedNumber->f = strtof(value, &end_p);
240 ED_TEST_RETURN_ERROR(insistPositive && parsedNumber->f < 0.0f, "ED_CheckNumber: not positive %s", value);
241 break;
242 case ED_TYPE_BOOL:
243 parsedNumber->i = (int)strtol(value, &end_p, 10);
244 ED_TEST_RETURN_ERROR(parsedNumber->i < 0 || parsedNumber->i > 1, "ED_CheckNumber: no boolean %s", value);
245 break;
246 case ED_TYPE_INT:
247 parsedNumber->i = (int)strtol(value, &end_p, 10);
248 ED_TEST_RETURN_ERROR(insistPositive && parsedNumber->i < 0, "ED_CheckNumber: not positive %s", value);
249 break;
250 default:
251 ED_RETURN_ERROR("ED_CheckNumber: type to test against not recognised");
252 }
253 /* if strto* did not use the whole token, then there is some non-number part to it */
254 ED_TEST_RETURN_ERROR(strlen(value) != (unsigned int)(end_p-value),
255 "problem with numeric value: \"%s\" declared as %s. (might be relevant: only use whitespace to delimit values)",
256 value, ED_Constant2Type(floatOrInt));
257 return ED_OK;
258}
259
269static int ED_CheckRange (const entityKeyDef_t* keyDef, const int type, const int index, int_float_tu parsedNumber)
270{
271 /* there may be a range for each element of the value, or there may only be one */
272 const int useRangeIndex = (keyDef->numRanges == 1) ? 0 : index;
274 const float discreteFloatEpsilon = 0.0001f;
275 if (0 == keyDef->numRanges)
276 return ED_OK; /* if no range defined, that is OK */
277 assert(useRangeIndex < keyDef->numRanges);
278 kr = keyDef->ranges[useRangeIndex];
279 switch (type) {
280 case ED_TYPE_BOOL:
281 return ED_OK;
282 case ED_TYPE_FLOAT:
283 if (kr->continuous) {
284 ED_TEST_RETURN_ERROR(parsedNumber.f < kr->fArr[0] || parsedNumber.f > kr->fArr[1],
285 "ED_CheckRange: %.1f out of range, \"%s\" in %s",
286 parsedNumber.f, kr->str, keyDef->name);
287 return ED_OK;
288 } else {
289 for (int j = 0; j < kr->numElements; j++)
290 if (fabs(parsedNumber.f - kr->fArr[j]) < discreteFloatEpsilon)
291 return ED_OK;
292 }
293 break;
294 case ED_TYPE_INT:
295 if (kr->continuous) {
296 ED_TEST_RETURN_ERROR(parsedNumber.i < kr->iArr[0] || parsedNumber.i > kr->iArr[1],
297 "ED_CheckRange: %i out of range, \"%s\" in %s",
298 parsedNumber.i, kr->str, keyDef->name);
299 return ED_OK;
300 } else {
301 for (int j = 0; j < kr->numElements; j++)
302 if (kr->iArr[j] == parsedNumber.i)
303 return ED_OK;
304 }
305 break;
306 default:
307 ED_RETURN_ERROR( "ED_CheckRange: type to test against not recognised in %s", keyDef->name);
308 }
309 ED_RETURN_ERROR("ED_CheckRange: value not specified in range definition, \"%s\" in %s",
310 kr->str, keyDef->name);
311}
312
321static int ED_CheckNumericType (const entityKeyDef_t* keyDef, const char* value, const int type)
322{
323 int i = 0;
324 static char tokBuf[64];
325 const char* buf_p = tokBuf;
326
327 strncpy(tokBuf, value, sizeof(tokBuf));
328 assert(type == ED_TYPE_INT || type == ED_TYPE_FLOAT || type == ED_TYPE_BOOL);
329 while (buf_p) {
330 const char* tok = Com_Parse(&buf_p);
331 int_float_tu parsedNumber;
332 if (tok[0] == '\0')
333 break; /* previous tok was the last real one, don't waste time */
334
335 ED_PASS_ERROR_EXTRAMSG(ED_CheckNumber(tok, type, keyDef->flags & ED_INSIST_POSITIVE, &parsedNumber),
336 " in key \"%s\"", keyDef->name);
337
338 ED_PASS_ERROR(ED_CheckRange(keyDef, type, i, parsedNumber));
339
340 i++;
341 }
342
343 ED_TEST_RETURN_ERROR(i != keyDef->vLen, "ED_CheckNumericType: %i elements in vector that should have %i for \"%s\" key",
344 i, keyDef->vLen, keyDef->name);
345
346 return ED_OK;
347}
348
357int ED_Check (const char* classname, const char* key, const char* value)
358{
359 const entityKeyDef_t* kd = ED_GetKeyDef(classname, key, 0);
360 if (!kd)
361 return ED_ERROR;
362
363 return ED_CheckKey(kd, value);
364}
365
372int ED_CheckKey (const entityKeyDef_t* kd, const char* value)
373{
374 ED_TEST_RETURN_ERROR(!kd, "ED_CheckTypeEntityKey: null key def");
375 switch (kd->flags & ED_KEY_TYPE) {
376 case ED_TYPE_FLOAT:
377 return ED_CheckNumericType(kd, value, ED_TYPE_FLOAT);
378 case ED_TYPE_INT:
379 return ED_CheckNumericType(kd, value, ED_TYPE_INT);
380 case ED_TYPE_BOOL:
381 return ED_CheckNumericType(kd, value, ED_TYPE_BOOL);
382 case ED_TYPE_STRING:
383 case 0: /* string is the default */
384 return ED_OK; /* all strings are good */
385 default:
386 ED_RETURN_ERROR("ED_CheckTypeEntityKey: type not recognised in key def");
387 }
388}
389
394static int ED_ParseType (entityKeyDef_t* kd, const char* parsedToken)
395{
396 static char tokBuf[64];
397 const char* buf_p;
398 int type;
399 int vectorLen;
400 const char* partToken;
401 int_float_tu parsedNumber;
402
403 /* need a copy, as parsedToken is held in a static buffer in the
404 * Com_Parse function */
405 ED_TEST_RETURN_ERROR((strlen(parsedToken) + 1) > sizeof(tokBuf),
406 "ED_ParseType: type string too long for buffer for key %s", kd->name);
407 strncpy(tokBuf, parsedToken, sizeof(tokBuf));
408 tokBuf[sizeof(tokBuf) - 1] = '\0';
409 buf_p = tokBuf;
410
411 partToken = Com_Parse(&buf_p);
412
413 if (Q_streq("SIGNED", partToken)) {
414 partToken = Com_Parse(&buf_p);/* get next token */
415 } else if (Q_streq("UNSIGNED", partToken)) {
417 partToken = Com_Parse(&buf_p);
418 }
419
420 if (partToken[0] != '\0') {
421 type = ED_Type2Constant(partToken);
423 } else {/* default is string */
425 }
426
427 kd->flags |= type;
428 partToken = Com_Parse(&buf_p);
429 vectorLen = atoi(partToken);
430 if (vectorLen)
431 ED_TEST_RETURN_ERROR(ED_ERROR == ED_CheckNumber(partToken, ED_TYPE_INT, 1, &parsedNumber),
432 "ED_ParseType: problem with vector length \"%s\" in key %s",
433 partToken, kd->name);
434 kd->vLen = strlen(partToken) ? (vectorLen ? vectorLen : 1) : 1; /* default is 1 */
435 return ED_OK;
436}
437
442static int ED_Block2Constant (const char* blockName)
443{
444 if (Q_streq("optional", blockName))
445 return ED_OPTIONAL;
446 else if (Q_streq("mandatory", blockName))
447 return ED_MANDATORY;
448 else if (Q_streq("abstract", blockName))
449 return ED_ABSTRACT;
450 else if (Q_streq("default", blockName))
451 return ED_DEFAULT;
452 else if (Q_streq("type", blockName))
453 return ED_MODE_TYPE;
454 else if (Q_streq("range", blockName))
455 return ED_RANGE;
456 else
457 ED_RETURN_ERROR("parse mode not recognised");
458}
459
465static const char* ED_Constant2Block (int constInt)
466{
467 switch (constInt) {
468 case ED_OPTIONAL:
469 return "optional";
470 case ED_MANDATORY:
471 return "mandatory";
472 case ED_ABSTRACT:
473 return "abstract";
474 case ED_DEFAULT:
475 return "default";
476 case ED_MODE_TYPE:
477 return "type";
478 case ED_RANGE:
479 return "range";
480 default:
481 snprintf(lastErr, sizeof(lastErr), "ED_Constant2Block: constant not recognised");
482 return nullptr;
483 }
484}
485
486static int ED_AllocRange (entityKeyDef_t* kd, const char* rangeStr)
487{
488 entityKeyRange_t** newRanges;
489 /* start a new range */
490 char* newStr = strdup(rangeStr);
491 entityKeyRange_t* newRange = (entityKeyRange_t*)malloc(sizeof(entityKeyRange_t));
492 OBJZERO(*newRange);
493 /* resize array of pointers */
494 newRanges = (entityKeyRange_t**)malloc((kd->numRanges + 1) * sizeof(entityKeyRange_t*));
495 if(!newRanges || !newStr || !newRange) {
496 free(newRanges);
497 free(newRange);
498 free(newStr);
499 ED_RETURN_ERROR("ED_AllocRange: out of memory");
500 }
501 newRange->str = newStr;
502 newRanges[kd->numRanges] = newRange;
503 if (kd->ranges) {
504 memcpy(newRanges, kd->ranges, kd->numRanges * sizeof(entityKeyRange_t*));
505 free(kd->ranges);
506 }
507 kd->numRanges++;
508 kd->ranges = newRanges;
509 return ED_OK;
510}
511
515static int ED_PairParsed (entityKeyDef_t keyDefsBuf[], int* numKeyDefsSoFar_p,
516 const char* newName, const char* newVal, const int mode)
517{
518 /* check if there is already a key def */
519 entityKeyDef_t* keyDef = ED_FindKeyDefInArray(keyDefsBuf, *numKeyDefsSoFar_p, newName, mode);
520
521 /* create one if required */
522 if (!keyDef) {
523 keyDef = &keyDefsBuf[(*numKeyDefsSoFar_p)++];
524 ED_TEST_RETURN_ERROR(*numKeyDefsSoFar_p >= ED_MAX_KEYS_PER_ENT, "ED_PairParsed: too many keys for buffer");
525 keyDef->name = strdup(newName);
526 ED_TEST_RETURN_ERROR(!keyDef->name, "ED_PairParsed: out of memory");
527 }
528
529 /* multiple range defs are permitted, different elements can have different ranges */
531 "Duplicate %s for %s key. second value: %s", ED_Constant2Block(mode), newName, newVal);
532
533 keyDef->flags |= mode;
534
535 /* store information */
536 switch (mode) {
537 case ED_MANDATORY:
538 case ED_OPTIONAL:
539 case ED_ABSTRACT: /* intentional fall-through */
540 keyDef->desc = strdup(newVal);
541 ED_TEST_RETURN_ERROR(!keyDef->desc, "ED_PairParsed: out of memory while storing string.");
542 return ED_OK;
543 case ED_DEFAULT:
544 keyDef->defaultVal = strdup(newVal);
545 ED_TEST_RETURN_ERROR(!keyDef->defaultVal, "ED_PairParsed: out of memory while storing string.");
546 return ED_OK;
547 case ED_MODE_TYPE:
548 /* only optional or mandatory keys may have types, not possible to test for this here,
549 * as the type block may come before the optional or mandatory block */
550 ED_PASS_ERROR(ED_ParseType(keyDef, newVal));
551 return ED_OK;
552 case ED_RANGE:
553 /* only typed keys may have ranges, this may only be tested after
554 * the whole ent has been parsed: the blocks may come in any order */
555 ED_PASS_ERROR(ED_AllocRange(keyDef, newVal));
556 return ED_OK;
557 default:
558 ED_RETURN_ERROR("ED_PairParsed: parse mode not recognised");
559 }
560}
561
565static int ED_ParseEntities (const char** data_p)
566{
567 int braceLevel = 0;
568 int tokensOnLevel0 = 0;
569 int mode = ED_ABSTRACT;
571 char lastTokenBuf[ED_MAX_TOKEN_LEN];
572 int keyIndex = 0;
573 int toggle = 0; /* many lines should have a pair of tokens on, this toggles 0, 1 to indicate progress */
574
575 while (data_p) {
576 const char* parsedToken = Com_Parse(data_p);
577 toggle ^= 1;
578
579 if (parsedToken[0] == '\0' && braceLevel == 0)
580 break;
581
582 if (parsedToken[0] == '{') {
583 braceLevel++;
584 ED_TEST_RETURN_ERROR(braceLevel > 2, "Too many open braces, nested %i deep", braceLevel);
585 ED_TEST_RETURN_ERROR(!toggle, "ED_ParseEntities: Incorrect number of tokens before '{'");
586 toggle ^= 1; /* reset, as toggle is only for counting proper text tokens, not braces */
587 tokensOnLevel0 = 0;
588 continue;
589 }
590
591 if (parsedToken[0] == '}') {
592 braceLevel--;
593 ED_TEST_RETURN_ERROR(braceLevel < 0, "Too many close braces. after %i entities", numEntityDefs);
594 toggle ^= 1; /* reset, as toggle is only for counting proper text tokens, not braces */
595 if (braceLevel == 0) { /* finished parsing entity def and prepare for the next one */
596 ED_PASS_ERROR(ED_AllocEntityDef(keyDefBuf, keyIndex, numEntityDefs));
598 ED_TEST_RETURN_ERROR(numEntityDefs >= ED_MAX_DEFS, "ED_ParseEntities: too many entity defs for buffer");
599 }
600 if (braceLevel == 1) /* ending a default, mandatory, etc block, go back to default parse mode */
602
603 continue;
604 }
605
606 if (braceLevel == 0) {
607 if (tokensOnLevel0 == 0 && !Q_streq(parsedToken, "entity"))
608 ED_RETURN_ERROR( "Next entity expected, found \"%s\"", parsedToken);
609
610 if (tokensOnLevel0 == 1) {/* classname of entity, start parsing new entity */
611 const entityDef_t* prevED = ED_GetEntityDef(parsedToken);
612 ED_TEST_RETURN_ERROR(prevED, "ED_ParseEntities: duplicate entity definition \"%s\"", parsedToken);
613 OBJZERO(keyDefBuf); /* ensure pointers are not carried over from previous entity */
614 keyIndex = 0;
615 ED_PASS_ERROR(ED_PairParsed(keyDefBuf, &keyIndex, "classname", parsedToken, ED_MANDATORY));
617 }
618
619 if (tokensOnLevel0 > 1)
620 ED_RETURN_ERROR( "Start of entity block expected found \"%s\"", parsedToken);
621
622 tokensOnLevel0++;
623 } else { /* braceLevel > 0 */
624 const int newMode = ED_Block2Constant(parsedToken);
625 if (ED_ERROR == newMode) { /* must be a key name or value */
626 if (toggle) { /* store key name til after next token is parsed */
627 if ('\0' == parsedToken[0])
628 ED_RETURN_ERROR("key name null string, \"\", or missing closing brace");
629 strncpy(lastTokenBuf, parsedToken, sizeof(lastTokenBuf));
630 lastTokenBuf[sizeof(lastTokenBuf) - 1] = '\0';
631 } else { /* store key-value pair in buffer until whole entity is parsed */
632 ED_PASS_ERROR(ED_PairParsed(keyDefBuf, &keyIndex, lastTokenBuf, parsedToken, mode));
633 }
634 } else {
635 mode = newMode;
636 toggle ^= 1; /* start of a mode changing block is the only time that only one non-brace token is on a line */
637 }
638 }
639 }
640
641 return ED_OK;
642}
643
649static int ED_CheckDefaultTypes (void)
650{
651 const entityDef_t* ed;
652 const entityKeyDef_t* kd;
653 for (ed = entityDefs; ed->numKeyDefs; ed++)
654 for (kd = ed->keyDefs; kd->name; kd++)
655 if (kd->defaultVal)
657 " while checking default block entry agrees with type")
658
659 return ED_OK;
660}
661
668static int ED_ProcessRanges (void)
669{
670 static int ibuf[32];
671 static float fbuf[32];
672
673 for (entityDef_t* ed = entityDefs; ed->numKeyDefs; ed++) {
674 for (entityKeyDef_t* kd = ed->keyDefs; kd->name; kd++) {
675 const int keyType = kd->flags & ED_KEY_TYPE;
676 for (int i = 0; i < kd->numRanges ;i++) {
677 int numElements = 0;
678 entityKeyRange_t* kr = kd->ranges[i];
679 const char* tmpRange_p = kr->str;
680 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",
681 kd->name, ed->classname);
682 while (tmpRange_p) {
683 int_float_tu parsedNumber;
684 const char* tok = Com_Parse(&tmpRange_p);
685 if (tok[0] == '\0')
686 break;
687 if (Q_streq("-", tok)) {
688 kr->continuous = 1;
689 ED_TEST_RETURN_ERROR(numElements != 1, "ED_ProcessRanges: problem with continuous range, \"%s\" in %s in %s",
690 kr->str, kd->name, ed->classname);
691 continue;
692 }
693 ED_PASS_ERROR(ED_CheckNumber(tok, keyType, kd->flags & ED_INSIST_POSITIVE, &parsedNumber));
694 switch (keyType) {
695 case ED_TYPE_BOOL:
696 case ED_TYPE_INT:
697 ibuf[numElements++] = atoi(tok);
698 break;
699 case ED_TYPE_FLOAT:
700 fbuf[numElements++] = atof(tok);
701 break;
702 default:
703 ED_RETURN_ERROR("ED_ProcessRanges: unexpected type");
704 }
705 }
706 kr->numElements = numElements;
707 ED_TEST_RETURN_ERROR(kr->continuous && numElements != 2,
708 "ED_ProcessRanges: continuous range should only have 2 elements, upper and lower bounds, \"%s\" in %s in %s",
709 kr->str, kd->name, ed->classname);
710 if (ED_TYPE_INT == keyType || ED_TYPE_BOOL == keyType) {
711 const size_t size = numElements * sizeof(int);
712 kr->iArr = (int*)malloc(size);
713 ED_TEST_RETURN_ERROR(!kr->iArr, "ED_ProcessRanges: out of memory");
714 memcpy(kr->iArr, ibuf, size);
715 } else { /* ED_TYPE_FLOAT */
716 const size_t size = numElements * sizeof(float);
717 kr->fArr = (float*)malloc(size);
718 ED_TEST_RETURN_ERROR(!kr->fArr, "ED_ProcessRanges: out of memory");
719 memcpy(kr->fArr, fbuf, size);
720 }
721 }
722 ED_TEST_RETURN_ERROR(kd->numRanges && kd->numRanges != 1 && kd->vLen != kd->numRanges,
723 "ED_ProcessRanges: if range definitions are supplied, "
724 "there must be one (which is applied to each element of a vector), "
725 "or one for each element of the vector. "
726 "%s in %s has %i elements in vector and %i range definitions",
727 ed->classname, kd->name, kd->vLen, kd->numRanges);
728 }
729 }
730 return ED_OK;
731}
732
738int ED_Parse (const char* data_p)
739{
740 /* only do this once, repeat calls are OK */
741 static int done = 0;
742 if (done)
743 return ED_OK;
744
745 snprintf(lastErr, sizeof(lastErr), "no error");
746 /* Zero out so that looping through the ones that have already
747 * been parsed is possible while the rest are parsed */
749 numEntityDefs = 0;
750
752 ED_TEST_RETURN_ERROR(numEntityDefs == 0, "ED_Parse: Zero entity definitions found");
753
755
757
758 done = 1; /* do not do it again */
759
760 return ED_OK;
761}
762
763const char* ED_GetLastError (void)
764{
765 return lastErr;
766}
767
775const entityKeyDef_t* ED_GetKeyDef (const char* classname, const char* keyname, const int abstract)
776{
777 const entityDef_t* ed = ED_GetEntityDef(classname);
778 return ED_GetKeyDefEntity(ed, keyname, abstract);
779}
780
789const entityKeyDef_t* ED_GetKeyDefEntity (const entityDef_t* ed, const char* keyname, const int abstract)
790{
791 const entityKeyDef_t* kd;
792
793 if (!ed)
794 return nullptr;
795
796 for (kd = ed->keyDefs; kd->name; kd++)
797 if (Q_streq(keyname, kd->name)) {
798 if (abstract) {
799 if (kd->flags & ED_ABSTRACT)
800 return kd;
801 } else {
802 if (!(kd->flags & ED_ABSTRACT))
803 return kd;
804 }
805 }
806
807 snprintf(lastErr, sizeof(lastErr), "ED_GetKeyDefEntity: no key definition for %s found in entity %s entities.ufo", keyname, ed->classname);
808 return nullptr;
809}
810
815const entityDef_t* ED_GetEntityDef (const char* classname)
816{
817 const entityDef_t* ed;
818
819 for (ed = entityDefs; ed->numKeyDefs; ed++)
820 if (Q_streq(classname, ed->classname))
821 return ed;
822
823 snprintf(lastErr, sizeof(lastErr), "ED_GetEntityDef: no entity definition for %s found in entities.ufo", classname);
824 return nullptr;
825}
826
827void ED_Free (void)
828{
829 if (numEntityDefs) {
830 for (entityDef_t* ed = entityDefs; ed->numKeyDefs; ++ed) {
831 free(ed->classname);
832 for (entityKeyDef_t* kd = ed->keyDefs; kd->name; ++kd) {
833 free(kd->name);
834 free(kd->desc);
835 free(kd->defaultVal);
836 if (kd->numRanges) {
837 for (int i = 0; i < kd->numRanges ;i++) {
838 entityKeyRange_t* kr = kd->ranges[i];
839 free(kr->iArr);
840 free(kr->fArr);
841 free(kr->str);
842 free(kr);
843 }
844 free(kd->ranges);
845 }
846 }
847 free(ed->keyDefs);
848 }
849 }
850}
unsigned int key
Definition cl_input.cpp:64
int numEntityDefs
static char lastErrExtra[ED_MAX_ERR_LEN]
int ED_Check(const char *classname, const char *key, const char *value)
tests if a value string matches the type for this key. Also checks the value against the range,...
static int ED_AllocEntityDef(entityKeyDef_t *newKeyDefs, int numKeyDefs, int entityIndex)
allocate space for key defs etc, pointers for which are stored in the entityDef_t
int ED_Parse(const char *data_p)
static int ED_ParseType(entityKeyDef_t *kd, const char *parsedToken)
takes a type string (eg "V_FLOAT 6") and configures entity def
static const char * ED_Constant2Block(int constInt)
converts an internal constant integer to a string representation of a type (eg V_FLOAT)
int ED_CheckKey(const entityKeyDef_t *kd, const char *value)
as ED_Check, but where the entity and key are known, so takes different arguments.
static int ED_Block2Constant(const char *blockName)
converts a block name (eg "optional") to an constant (eg ED_OPTIONAL).
static int ED_CheckRange(const entityKeyDef_t *keyDef, const int type, const int index, int_float_tu parsedNumber)
check a value against the range for the key
const entityKeyDef_t * ED_GetKeyDefEntity(const entityDef_t *ed, const char *keyname, const int abstract)
searches for the parsed key def, when the entity def is known
const entityKeyDef_t * ED_GetKeyDef(const char *classname, const char *keyname, const int abstract)
searches for the parsed key def
static int ED_CheckNumber(const char *value, const int floatOrInt, const int insistPositive, int_float_tu *parsedNumber)
checks that a string represents a single number
static int ED_ProcessRanges(void)
finish parsing ranges. Could not be done earlier as would not have necessarily known types and defaul...
int ED_GetIntVector(const entityKeyDef_t *kd, int v[], const int n)
parses a value from the definition
static int ED_CheckNumericType(const entityKeyDef_t *keyDef, const char *value, const int type)
tests if a value string matches the type for this key. this includes each element of a numeric array....
static int ED_ParseEntities(const char **data_p)
static int ED_Type2Constant(const char *strType)
converts a string representation of a type (eg V_FLOAT) to the appropriate internal constant integer
#define ED_TEST_RETURN_ERROR(condition,...)
test a condition, write an error message and exit the current function with ED_ERROR
const char * ED_GetLastError(void)
void ED_Free(void)
static int ED_CheckDefaultTypes(void)
checks if the default block entries meet the type and range definitions.
static int ED_AllocRange(entityKeyDef_t *kd, const char *rangeStr)
#define ED_PASS_ERROR(function_call)
if the function returns ED_ERROR, then the function that the macro is in also returns ED_ERROR....
entityDef_t entityDefs[ED_MAX_DEFS+1]
const entityDef_t * ED_GetEntityDef(const char *classname)
searches for the parsed entity def by classname
#define ED_PASS_ERROR_EXTRAMSG(function_call,...)
if the function returns ED_ERROR, then the function that the macro is in also returns ED_ERROR....
static entityKeyDef_t * ED_FindKeyDefInArray(entityKeyDef_t keyDefs[], int numDefs, const char *name, int parseMode)
search for an existing keyDef to add a new parsed pair info to.
#define ED_MAX_TOKEN_LEN
static const char * ED_Constant2Type(int constInt)
converts an internal constant integer to a string representation of a type (eg V_FLOAT)
#define ED_MAX_ERR_LEN
static int ED_PairParsed(entityKeyDef_t keyDefsBuf[], int *numKeyDefsSoFar_p, const char *newName, const char *newVal, const int mode)
#define ED_RETURN_ERROR(...)
write an error message and exit the current function returning ED_ERROR
static int ED_GetIntVectorFromString(const char *str, int v[], const int n)
parses an int array from a string
#define ED_MAX_KEYS_PER_ENT
static char lastErr[ED_MAX_ERR_LEN]
Handles definition of entities, parsing them from entities.ufo.
#define ED_INSIST_POSITIVE
Definition entitiesdef.h:44
#define ED_RANGE
Definition entitiesdef.h:43
#define ED_TYPE_INT
Definition entitiesdef.h:39
#define ED_ERROR
Definition entitiesdef.h:32
#define ED_TYPE_FLOAT
Definition entitiesdef.h:38
#define ED_MAX_DEFS
Definition entitiesdef.h:30
#define ED_DEFAULT
Definition entitiesdef.h:41
#define ED_OK
Definition entitiesdef.h:33
#define ED_MODE_TYPE
Definition entitiesdef.h:42
#define ED_ABSTRACT
Definition entitiesdef.h:37
#define ED_OPTIONAL
Definition entitiesdef.h:35
#define ED_KEY_TYPE
Definition entitiesdef.h:48
#define ED_TYPE_STRING
Definition entitiesdef.h:40
#define ED_TYPE_BOOL
Definition entitiesdef.h:45
#define ED_MANDATORY
Definition entitiesdef.h:36
voidpf void uLong size
Definition ioapi.h:42
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
const char int mode
Definition ioapi.h:41
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
Definition parse.cpp:107
Shared parsing functions.
QGL_EXTERN int GLboolean GLfloat * v
Definition r_gl.h:120
QGL_EXTERN GLuint index
Definition r_gl.h:110
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
char parsedToken[MAX_TOKEN_CHARS]
Definition scriplib.cpp:45
#define Q_streq(a, b)
Definition shared.h:136
#define OBJZERO(obj)
Definition shared.h:178
char * classname
Definition entitiesdef.h:74
entityKeyDef_t * keyDefs
Definition entitiesdef.h:75
char * defaultVal
Definition entitiesdef.h:66
entityKeyRange_t ** ranges
Definition entitiesdef.h:69