/** * @brief searches for the parsed key def * @param abstract send abstract to find an abstract key with this name * @return NULL if the entity def or key def is not found. call ED_GetLastError to get a relevant message. */ const entityKeyDef_t *ED_GetKeyDef (const char *classname, const char *keyname, const int abstract) { const entityDef_t *ed = ED_GetEntityDef(classname); return ED_GetKeyDefEntity(ed, keyname, abstract); }
/** * @brief Perform an entity check */ void CheckEntities (void) { Check_InitEntityDefs(); for (int i = 0; i < num_entities; i++) { entity_t* e = &entities[i]; const char* name = ValueForKey(e, "classname"); const entityDef_t* ed = ED_GetEntityDef(name); const epair_t* kvp; const entityKeyDef_t* kd; if (!ed) { /* check that a definition exists */ Check_Printf(VERB_CHECK, false, i, -1, "Not defined in entities.ufo: %s\n", name); continue; } /* check alignment of info_.+_start */ if (Check_IsInfoStart(name) && !Check_InfoStartAligned(ed, e)) Check_Printf(VERB_CHECK, false, i, -1, "Misaligned %s\n", name); if (Q_strstart(name, "func_")) /* func_* entities should have brushes */ Check_EntityWithBrushes(e, name, i); /* check all keys in the entity - make sure they are OK */ for (kvp = e->epairs; kvp; kvp = kvp->next) { kd = ED_GetKeyDefEntity(ed, kvp->key, 0); /* zero means ignore abstract (radiant only) keys */ if (!kd) { /* make sure it has a definition */ Check_Printf(VERB_CHECK, false, i, -1, "Not defined in entities.ufo: %s in %s\n", kvp->key, name); continue; } if (ED_CheckKey(kd, kvp->value) == ED_ERROR) { /* check values against type and range definitions in entities.ufo */ Check_Printf(VERB_CHECK, false, i, -1, "%s\n", ED_GetLastError()); continue; } if (Q_streq("target", kvp->key) || Q_streq("targetname", kvp->key)) { if (!Check_TargetExists(kvp)) { Check_Printf(VERB_CHECK, false, i, -1, "%s with %s of %s: no corresponding entity with %s with matching value\n", ed->classname, kvp->key, kvp->value, Q_streq("target", kvp->key) ? "targetname" : "target"); } } } /* check keys in the entity definition - make sure mandatory ones are present */ for (kd = ed->keyDefs; kd->name; kd++) { if (kd->flags & ED_MANDATORY) { const char* keyNameInEnt = ValueForKey(e, kd->name); if (keyNameInEnt[0] == '\0') { const char* defaultVal = kd->defaultVal; const bool hasDefault = defaultVal ? true : false; Check_Printf(VERB_CHECK, hasDefault, i, -1, "Mandatory key missing from entity: %s in %s", kd->name, name); if (defaultVal) { Check_Printf(VERB_CHECK, hasDefault, i, -1, ", supplying default: %s", defaultVal); SetKeyValue(e, kd->name, defaultVal); } Check_Printf(VERB_CHECK, hasDefault, i, -1, "\n"); } } } } }
/** * @return ED_ERROR or ED_OK */ static int ED_ParseEntities (const char **data_p) { int braceLevel = 0; int tokensOnLevel0 = 0; int mode = ED_ABSTRACT; entityKeyDef_t keyDefBuf[ED_MAX_KEYS_PER_ENT]; char lastTokenBuf[ED_MAX_TOKEN_LEN]; int keyIndex = 0; int toggle = 0; /* many lines should have a pair of tokens on, this toggles 0, 1 to indicate progress */ while (data_p) { const char *parsedToken = Com_Parse(data_p); toggle ^= 1; if (parsedToken[0] == '\0' && braceLevel == 0) break; if (parsedToken[0] == '{') { braceLevel++; ED_TEST_RETURN_ERROR(braceLevel > 2, "Too many open braces, nested %i deep", braceLevel); ED_TEST_RETURN_ERROR(!toggle, "ED_ParseEntities: Incorrect number of tokens before '{'"); toggle ^= 1; /* reset, as toggle is only for counting proper text tokens, not braces */ tokensOnLevel0 = 0; continue; } if (parsedToken[0] == '}') { braceLevel--; ED_TEST_RETURN_ERROR(braceLevel < 0, "Too many close braces. after %i entities", numEntityDefs); toggle ^= 1; /* reset, as toggle is only for counting proper text tokens, not braces */ if (braceLevel == 0) { /* finished parsing entity def and prepare for the next one */ ED_PASS_ERROR(ED_AllocEntityDef(keyDefBuf, keyIndex, numEntityDefs)); numEntityDefs++; ED_TEST_RETURN_ERROR(numEntityDefs >= ED_MAX_DEFS, "ED_ParseEntities: too many entity defs for buffer"); } if (braceLevel == 1) /* ending a default, mandatory, etc block, go back to default parse mode */ mode = ED_ABSTRACT; continue; } if (braceLevel == 0) { if (tokensOnLevel0 == 0 && !Q_streq(parsedToken, "entity")) ED_RETURN_ERROR( "Next entity expected, found \"%s\"", parsedToken); if (tokensOnLevel0 == 1) {/* classname of entity, start parsing new entity */ const entityDef_t *prevED = ED_GetEntityDef(parsedToken); ED_TEST_RETURN_ERROR(prevED, "ED_ParseEntities: duplicate entity definition \"%s\"", parsedToken); OBJZERO(keyDefBuf); /* ensure pointers are not carried over from previous entity */ keyIndex = 0; ED_PASS_ERROR(ED_PairParsed(keyDefBuf, &keyIndex, "classname", parsedToken, ED_MANDATORY)); mode = ED_ABSTRACT; } if (tokensOnLevel0 > 1) ED_RETURN_ERROR( "Start of entity block expected found \"%s\"", parsedToken); tokensOnLevel0++; } else { /* braceLevel > 0 */ const int newMode = ED_Block2Constant(parsedToken); if (ED_ERROR == newMode) { /* must be a key name or value */ if (toggle) { /* store key name til after next token is parsed */ if ('\0' == parsedToken[0]) ED_RETURN_ERROR("key name null string, \"\", or missing closing brace"); strncpy(lastTokenBuf, parsedToken, sizeof(lastTokenBuf)); lastTokenBuf[sizeof(lastTokenBuf) - 1] = '\0'; } else { /* store key-value pair in buffer until whole entity is parsed */ ED_PASS_ERROR(ED_PairParsed(keyDefBuf, &keyIndex, lastTokenBuf, parsedToken, mode)); } } else { mode = newMode; toggle ^= 1; /* start of a mode changing block is the only time that only one non-brace token is on a line */ } } } return ED_OK; }