示例#1
0
/**
 * @brief Copies an entry from the building description file into the list of building types.
 * @note Parses one "building" entry in the basemanagement.ufo file and writes
 * it into the next free entry in bmBuildings[0], which is the list of buildings
 * in the first base (building_t).
 * @param[in] name Unique script id of a building. This is parsed from "building xxx" -> id=xxx.
 * @param[in] text the whole following text that is part of the "building" item definition in .ufo.
 * @param[in] link Bool value that decides whether to link the tech pointer in or not
 * @sa CL_ParseScriptFirst (link is false here)
 * @sa CL_ParseScriptSecond (link it true here)
 */
void B_ParseBuildings (const char *name, const char **text, qboolean link)
{
	building_t *building;
	technology_t *techLink;
	const char *errhead = "B_ParseBuildings: unexpected end of file (names ";
	const char *token;

	/* get id list body */
	token = Com_Parse(text);
	if (!*text || *token != '{') {
		Com_Printf("B_ParseBuildings: building \"%s\" without body ignored\n", name);
		return;
	}

	if (ccs.numBuildingTemplates >= MAX_BUILDINGS)
		Com_Error(ERR_DROP, "B_ParseBuildings: too many buildings");

	if (!link) {
		int i;
		for (i = 0; i < ccs.numBuildingTemplates; i++) {
			if (Q_streq(ccs.buildingTemplates[i].id, name)) {
				Com_Printf("B_ParseBuildings: Second building with same name found (%s) - second ignored\n", name);
				return;
			}
		}

		/* new entry */
		building = &ccs.buildingTemplates[ccs.numBuildingTemplates];
		OBJZERO(*building);
		building->id = Mem_PoolStrDup(name, cp_campaignPool, 0);

		Com_DPrintf(DEBUG_CLIENT, "...found building %s\n", building->id);

		/* set standard values */
		building->tpl = building;	/* Self-link just in case ... this way we can check if it is a template or not. */
		building->idx = -1;			/* No entry in buildings list (yet). */
		building->base = NULL;
		building->buildingType = MAX_BUILDING_TYPE;
		building->dependsBuilding = NULL;
		building->maxCount = -1;	/* Default: no limit */
		building->size[0] = 1;
		building->size[1] = 1;

		ccs.numBuildingTemplates++;
		do {
			/* get the name type */
			token = Com_EParse(text, errhead, name);
			if (!*text)
				break;
			if (*token == '}')
				break;

			/* get values */
			if (Q_streq(token, "type")) {
				token = Com_EParse(text, errhead, name);
				if (!*text)
					return;

				building->buildingType = B_GetBuildingTypeByBuildingID(token);
				if (building->buildingType >= MAX_BUILDING_TYPE)
					Com_Printf("didn't find buildingType '%s'\n", token);
			} else {
				/* no linking yet */
				if (Q_streq(token, "depends")) {
					token = Com_EParse(text, errhead, name);
					if (!*text)
						return;
				} else {
					if (!Com_ParseBlockToken(name, text, building, valid_building_vars, cp_campaignPool, token))
						Com_Printf("B_ParseBuildings: unknown token \"%s\" ignored (building %s)\n", token, name);
				}
			}
		} while (*text);
		if (building->size[0] < 1 || building->size[1] < 1 || building->size[0] >= BASE_SIZE || building->size[1] >= BASE_SIZE) {
			Com_Printf("B_ParseBuildings: Invalid size for building %s (%i, %i)\n", building->id, (int)building->size[0], (int)building->size[1]);
			ccs.numBuildingTemplates--;
		}
	} else {
		building = B_GetBuildingTemplate(name);
		if (!building)
			Com_Error(ERR_DROP, "B_ParseBuildings: Could not find building with id %s\n", name);

		techLink = RS_GetTechByProvided(name);
		if (techLink)
			building->tech = techLink;

		do {
			/* get the name type */
			token = Com_EParse(text, errhead, name);
			if (!*text)
				break;
			if (*token == '}')
				break;
			/* get values */
			if (Q_streq(token, "depends")) {
				const building_t *dependsBuilding = B_GetBuildingTemplate(Com_EParse(text, errhead, name));
				if (!dependsBuilding)
					Com_Error(ERR_DROP, "Could not find building depend of %s\n", building->id);
				building->dependsBuilding = dependsBuilding;
				if (!*text)
					return;
			}
		} while (*text);
	}
}
示例#2
0
static void CL_ParsePtlCmds (const char *name, const char **text)
{
	ptlCmd_t *pc;
	const value_t *pp;
	const char *errhead = "CL_ParsePtlCmds: unexpected end of file";
	const char *token;
	int i, j;

	/* get it's body */
	token = Com_Parse(text);

	if (!*text || *token != '{') {
		Com_Printf("CL_ParsePtlCmds: particle cmds \"%s\" without body ignored\n", name);
		return;
	}

	do {
		token = Com_EParse(text, errhead, name);
		if (!*text)
			break;
		if (*token == '}')
			break;

		for (i = 0; i < PC_NUM_PTLCMDS; i++)
			if (Q_streq(token, pc_strings[i])) {
				/* allocate an new cmd */
				if (numPtlCmds >= MAX_PTLCMDS)
					Com_Error(ERR_DROP, "CL_ParsePtlCmds: MAX_PTLCMDS exceeded");
				pc = &ptlCmd[numPtlCmds++];
				OBJZERO(*pc);

				pc->cmd = i;

				if (!pc_types[i])
					break;

				/* get parameter type */
				token = Com_EParse(text, errhead, name);
				if (!*text)
					return;

				/* operate on the top element on the stack */
				if (token[0] == '#') {
					pc->ref = RSTACK;
					if (token[1] == '.')
						pc->ref -= (token[2] - '0');
					break;
				}

				if (token[0] == '*') {
					int len;
					char baseComponentToken[MAX_VAR];

					/* it's a variable reference */
					token++;

					/* we maybe have to modify it */
					Q_strncpyz(baseComponentToken, token, sizeof(baseComponentToken));

					/* check for component specifier */
					len = strlen(baseComponentToken);
					/* it's possible to change only the second value of e.g. a vector
					 * just defined e.g. 'size.2' to modify the second value of size */
					if (len >= 2 && baseComponentToken[len - 2] == '.') {
						baseComponentToken[len - 2] = 0;
					} else
						len = 0;

					for (pp = pps; pp->string; pp++)
						if (Q_streq(baseComponentToken, pp->string))
							break;

					if (!pp->string) {
						Com_Printf("CL_ParsePtlCmds: bad reference \"%s\" specified (particle %s)\n", token, name);
						numPtlCmds--;
						break;
					}

					if ((pc_types[i] & PTL_ONLY_ONE_TYPE)) {
						if ((pc_types[i] & ~PTL_ONLY_ONE_TYPE) != pp->type) {
							Com_Printf("CL_ParsePtlCmds: bad type in var \"%s\" (PTL_ONLY_ONE_TYPE) specified (particle %s) (ptl type: %i (pc_type: %i), string: %s)\n", token, name, pp->type, pc_types[i], pc_strings[i]);
							numPtlCmds--;
							break;
						}
					} else if (pp->type >= V_NUM_TYPES || !((1 << pp->type) & pc_types[i])) {
						Com_Printf("CL_ParsePtlCmds: bad type in var \"%s\" specified (particle %s) (ptl type: %i (pc_type: %i), string: %s)\n", token, name, pp->type, pc_types[i], pc_strings[i]);
						numPtlCmds--;
						break;
					}

					if (len) {
						/* get single component */
						if ((1 << pp->type) & V_VECS) {
							const int component = (baseComponentToken[len - 1] - '1');
							/* get the component we want to modify */
							if (component > 3) {
								Com_Printf("CL_ParsePtlCmds: bad component value - it's bigger than 3: %i (particle %s)\n", component, name);
								numPtlCmds--;
								break;
							}
							pc->type = V_FLOAT;
							/* go to component offset */
							pc->ref = -((int)pp->ofs) - component * sizeof(float);
							break;
						} else {
							Com_Printf("CL_ParsePtlCmds: can't get components of a non-vector type (particle %s)\n", name);
							numPtlCmds--;
							break;
						}
					}

					/* set the values */
					pc->type = pp->type;
					pc->ref = -((int)pp->ofs);
					break;
				}

				/* get the type */
				if (pc_types[i] & PTL_ONLY_ONE_TYPE)
					/* extract the real type */
					j = pc_types[i] & ~PTL_ONLY_ONE_TYPE;
				else {
					for (j = 0; j < V_NUM_TYPES; j++)
						if (Q_streq(token, vt_names[j]))
							break;

					if (j >= V_NUM_TYPES || !((1 << j) & pc_types[i])) {
						Com_Printf("CL_ParsePtlCmds: bad type \"%s\" specified (particle %s)\n", token, name);
						numPtlCmds--;
						break;
					}

					/* get the value */
					token = Com_EParse(text, errhead, name);
					if (!*text)
						return;
				}

				/* set the values */
				pc->type = j;

				pcmdPos = (byte*) Com_AlignPtr(pcmdPos, (valueTypes_t)pc->type);
				pc->ref = (int) (pcmdPos - pcmdData);
				pcmdPos += Com_EParseValue(pcmdPos, token, (valueTypes_t)pc->type, 0, 0);

/*				Com_Printf("%s %s %i\n", vt_names[pc->type], token, pcmdPos - pc->ref, (char *)pc->ref); */
				break;
			}

		if (i < PC_NUM_PTLCMDS)
			continue;

		for (pp = pps; pp->string; pp++)
			if (Q_streq(token, pp->string)) {
				/* get parameter */
				token = Com_EParse(text, errhead, name);
				if (!*text)
					return;

				/* translate set to a push and pop */
				if (numPtlCmds >= MAX_PTLCMDS - 1)
					Com_Error(ERR_DROP, "CL_ParsePtlCmds: MAX_PTLCMDS exceeded");
				pc = &ptlCmd[numPtlCmds++];
				pc->cmd = PC_PUSH;
				pc->type = pp->type;

				pcmdPos = (byte*) Com_AlignPtr(pcmdPos, (valueTypes_t)pc->type);
				pc->ref = (int) (pcmdPos - pcmdData);
				pcmdPos += Com_EParseValue(pcmdPos, token, (valueTypes_t)pc->type, 0, 0);

				pc = &ptlCmd[numPtlCmds++];
				pc->cmd = PC_POP;
				pc->type = pp->type;
				pc->ref = -((int)pp->ofs);
				break;
			}

		if (!pp->string)
			Com_Printf("CL_ParsePtlCmds: unknown token \"%s\" ignored (particle %s)\n", token, name);

	} while (*text);

	/* terminalize cmd chain */
	if (numPtlCmds >= MAX_PTLCMDS)
		Com_Error(ERR_DROP, "CL_ParsePtlCmds: MAX_PTLCMDS exceeded");
	pc = &ptlCmd[numPtlCmds++];
	OBJZERO(*pc);
}
示例#3
0
/**
 * @brief Parses particle definitions from UFO-script files
 * @param[in] name particle name/id
 * @param[in] text pointer to the buffer to parse from
 * @return the position of the particle in ptlDef array
 * @sa CL_ParseClientData
 */
void CL_ParseParticle (const char *name, const char **text)
{
	const char *errhead = "CL_ParseParticle: unexpected end of file (particle ";
	ptlDef_t *pd;
	const char *token;
	int i;

	/* search for particles with same name */
	for (i = 0; i < numPtlDefs; i++)
		if (Q_streq(name, ptlDef[i].name))
			break;

	if (i < numPtlDefs) {
		Com_Printf("CL_ParseParticle: particle def \"%s\" with same name found, reset first one\n", name);
		pd = &ptlDef[i];
	} else {
		if (numPtlDefs < MAX_PTLDEFS - 1) {
			/* initialize the new particle */
			pd = &ptlDef[numPtlDefs++];
		} else {
			Com_Printf("CL_ParseParticle: max particle definitions reached - skip the current one: '%s'\n", name);
			return;
		}
	}
	OBJZERO(*pd);

	Q_strncpyz(pd->name, name, sizeof(pd->name));

	/* get it's body */
	token = Com_Parse(text);

	if (!*text || *token != '{') {
		Com_Printf("CL_ParseParticle: particle def \"%s\" without body ignored\n", name);
		if (i == numPtlDefs)
			numPtlDefs--;
		return;
	}

	do {
		token = Com_EParse(text, errhead, name);
		if (!*text)
			break;
		if (*token == '}')
			break;

		for (i = 0; i < PF_NUM_PTLFUNCS; i++)
			if (Q_streq(token, pf_strings[i])) {
				/* allocate the first particle command */
				ptlCmd_t **pc;

				pc = (ptlCmd_t **) ((byte *) pd + pf_values[i]);
				*pc = &ptlCmd[numPtlCmds];

				/* parse the commands */
				CL_ParsePtlCmds(name, text);
				break;
			}

		if (i == PF_NUM_PTLFUNCS)
			Com_Printf("CL_ParseParticle: unknown token \"%s\" ignored (particle %s)\n", token, name);

	} while (*text);

	/* check for an init function */
	if (!pd->init) {
		Com_Printf("CL_ParseParticle: particle definition %s without init function ignored\n", name);
		if (i == numPtlDefs)
			numPtlDefs--;
	}
}
示例#4
0
/**
 * @brief Read a node body
 * @note Node header already read, we are over the node name, or '{'
 * @code
 * Allowed syntax
 * { properties }
 * OR
 * { nodes }
 * OR
 * { { properties } nodes }
 * @endcode
 */
static bool UI_ParseNodeBody (uiNode_t *node, const char **text, const char **token, const char *errhead)
{
	bool result = true;

	if ((*token)[0] != '{') {
		/* read the body block start */
		*token = Com_EParse(text, errhead, node->name);
		if (!*text)
			return false;
		if ((*token)[0] != '{') {
			Com_Printf("UI_ParseNodeBody: node doesn't have body, token '%s' read (node \"%s\")\n", *token, UI_GetPath(node));
			ui_global.numNodes--;
			return false;
		}
	}

	/* functions are a special case */
	if (UI_Node_IsFunction(node)) {
		result = UI_ParseFunction(node, text, token);
	} else {

		/* check the content */
		*token = Com_EParse(text, errhead, node->name);
		if (!*text)
			return false;

		if ((*token)[0] == '{') {
			/* we have a special block for properties */
			result = UI_ParseNodeProperties(node, text, token);
			if (!result)
				return false;

			/* move token over the next node behaviour */
			*token = Com_EParse(text, errhead, node->name);
			if (!*text)
				return false;

			/* and then read all nodes */
			while ((*token)[0] != '}') {
				uiNode_t *newNode = UI_ParseNode(node, text, token, errhead);
				if (!newNode)
					return false;

				*token = Com_EParse(text, errhead, node->name);
				if (*text == nullptr)
					return false;
			}
		} else if (UI_GetPropertyFromBehaviour(node->behaviour, *token)) {
			/* we should have a block with properties only */
			result = UI_ParseNodeProperties(node, text, token);
		} else {
			/* we should have a block with nodes only */
			while ((*token)[0] != '}') {
				uiNode_t *newNode = UI_ParseNode(node, text, token, errhead);
				if (!newNode)
					return false;

				*token = Com_EParse(text, errhead, node->name);
				if (*text == nullptr)
					return false;
			}
		}
	}
	if (!result) {
		Com_Printf("UI_ParseNodeBody: node with bad body ignored (node \"%s\")\n", UI_GetPath(node));
		ui_global.numNodes--;
		return false;
	}

	/* already check on UI_ParseNodeProperties */
	assert((*token)[0] == '}');
	return true;
}
示例#5
0
/**
 * @brief Reads the sequence values from given text-pointer
 * @sa CL_ParseClientData
 */
void CL_ParseSequence (const char* name, const char** text)
{
	const char* errhead = "CL_ParseSequence: unexpected end of file (sequence ";
	int i;

	/* search for sequences with same name */
	for (i = 0; i < numSequences; i++)
		if (Q_streq(name, sequences[i].name))
			break;

	if (i < numSequences) {
		Com_Printf("CL_ParseSequence: sequence def \"%s\" with same name found, second ignored\n", name);
		return;
	}

	/* initialize the sequence */
	if (numSequences >= MAX_SEQUENCES)
		Com_Error(ERR_FATAL, "Too many sequences");

	sequence_t* sp = &sequences[numSequences++];
	OBJZERO(*sp);
	Q_strncpyz(sp->name, name, sizeof(sp->name));
	sp->start = numSeqCmds;

	/* get it's body */
	const char* token = Com_Parse(text);

	if (!*text || *token != '{') {
		Com_Printf("CL_ParseSequence: sequence def \"%s\" without body ignored\n", name);
		numSequences--;
		return;
	}

	do {
		token = Com_EParse(text, errhead, name);
		if (!*text)
			break;
		if (*token == '}')
			break;

		/* check for commands */
		int i = CL_FindSequenceCommand(token);
		if (i != -1) {
			int maxLength = MAX_DATA_LENGTH;
			char* data;

			/* found a command */
			token = Com_EParse(text, errhead, name);
			if (!*text)
				return;

			if (numSeqCmds >= MAX_SEQCMDS)
				Com_Error(ERR_FATAL, "Too many sequence commands for %s\n", name);

			/* init seqCmd */
			if (seqCmds == nullptr)
				seqCmds = Mem_PoolAllocTypeN(seqCmd_t, MAX_SEQCMDS, cl_genericPool);
			seqCmd_t* sc = &seqCmds[numSeqCmds++];
			OBJZERO(*sc);
			sc->handler = seqCmdFunc[i];
			sp->length++;

			/* copy name */
			Q_strncpyz(sc->name, token, sizeof(sc->name));

			/* read data */
			token = Com_EParse(text, errhead, name);
			if (!*text)
				return;
			if (*token == '{') {
				// TODO depth is useless IMHO (bayo)
				int depth = 1;
				data = &sc->data[0];
				while (depth) {
					if (maxLength <= 0) {
						Com_Printf("Too much data for sequence %s\n", sc->name);
						break;
					}
					token = Com_EParse(text, errhead, name);
					if (!*text)
						return;

					if (*token == '{')
						depth++;
					else if (*token == '}')
						depth--;
					if (depth) {
						Q_strncpyz(data, token, maxLength);
						data += strlen(token) + 1;
						maxLength -= (strlen(token) + 1);
					}
				}
			} else if (*token == '(') {
				linkedList_t* list;
				Com_UnParseLastToken();
				if (!Com_ParseList(text, &list)) {
					Com_Error(ERR_DROP, "CL_ParseSequence: error while reading list (sequence \"%s\")", name);
				}
				data = &sc->data[0];
				for (linkedList_t* element = list; element != nullptr; element = element->next) {
					if (maxLength <= 0) {
						Com_Printf("Too much data for sequence %s", sc->name);
						break;
					}
					const char* v = (char*)element->data;
					Q_strncpyz(data, v, maxLength);
					data += strlen(v) + 1;
					maxLength -= (strlen(v) + 1);
				}
				LIST_Delete(&list);
			} else {
				Com_UnParseLastToken();
			}
		} else {
			Com_Printf("CL_ParseSequence: unknown command \"%s\" ignored (sequence %s)\n", token, name);
			Com_EParse(text, errhead, name);
		}
	} while (*text);
}
示例#6
0
/**
 * @brief Parse actions and return action list
 * @return The first element from a list of action
 * @sa ea_t
 * @todo need cleanup, compute action out of the final memory; reduce number of var
 */
static uiAction_t *UI_ParseActionList (uiNode_t *node, const char **text, const char **token)
{
	const char *errhead = "UI_ParseActionList: unexpected end of file (in event)";
	uiAction_t *firstAction;
	uiAction_t *lastAction;
	uiAction_t *action;

	lastAction = nullptr;
	firstAction = nullptr;

	/* prevent bad position */
	if ((*token)[0] != '{') {
		Com_Printf("UI_ParseActionList: token \"{\" expected, but \"%s\" found (in event) (node: %s)\n", *token, UI_GetPath(node));
		return nullptr;
	}

	while (true) {
		bool result;
		int type = EA_NULL;

		/* get new token */
		*token = Com_EParse(text, errhead, nullptr);
		if (!*token)
			return nullptr;

		if ((*token)[0] == '}')
			break;

		type = UI_GetActionTokenType(*token, EA_ACTION);
		/* setter form */
		if (type == EA_NULL && (*token)[0] == '*')
			type = EA_ASSIGN;

		/* unknown, we break the parsing */
		if (type == EA_NULL) {
			Com_Printf("UI_ParseActionList: unknown token \"%s\" ignored (in event) (node: %s)\n", *token, UI_GetPath(node));
			return nullptr;
		}

		/* add the action */
		action = UI_AllocStaticAction();
		/** @todo better to append the action after initialization */
		if (lastAction)
			lastAction->next = action;
		if (!firstAction)
			firstAction = action;
		action->type = type;

		/* decode action */
		switch (action->type) {
		case EA_CMD:
			/* get parameter values */
			*token = Com_EParse(text, errhead, nullptr);
			if (!*text)
				return nullptr;

			/* get the value */
			action->d.terminal.d1.constString = UI_AllocStaticString(*token, 0);
			break;

		case EA_ASSIGN:
			result = UI_ParseSetAction(node, action, text, token, errhead);
			if (!result)
				return nullptr;
			break;

		case EA_CALL:
			result = UI_ParseCallAction(node, action, text, token, errhead);
			if (!result)
				return nullptr;
			break;

		case EA_DELETE:
			{
				uiAction_t *expression;
				expression = UI_ParseExpression(text);
				if (expression == nullptr)
					return nullptr;

				if (expression->type != EA_VALUE_CVARNAME) {
					Com_Printf("UI_ParseActionList: \"delete\" keyword only support cvarname (node: %s)\n", UI_GetPath(node));
					return nullptr;
				}

				action->d.nonTerminal.left = expression;
				break;
			}

		case EA_ELIF:
			/* check previous action */
			if (!lastAction || (lastAction->type != EA_IF && lastAction->type != EA_ELIF)) {
				Com_Printf("UI_ParseActionList: 'elif' must be set after an 'if' or an 'elif' (node: %s)\n", UI_GetPath(node));
				return nullptr;
			}
			/* then it execute EA_IF, no break */
		case EA_WHILE:
		case EA_IF:
			{
				uiAction_t *expression;

				/* get the condition */
				expression = UI_ParseExpression(text);
				if (expression == nullptr)
					return nullptr;
				action->d.nonTerminal.left = expression;

				/* get the action block */
				*token = Com_EParse(text, errhead, nullptr);
				if (!*text)
					return nullptr;
				action->d.nonTerminal.right = UI_ParseActionList(node, text, token);
				if (action->d.nonTerminal.right == nullptr) {
					if (action->type == EA_IF)
						Com_Printf("UI_ParseActionList: block expected after \"if\" (node: %s)\n", UI_GetPath(node));
					else if (action->type == EA_ELIF)
						Com_Printf("UI_ParseActionList: block expected after \"elif\" (node: %s)\n", UI_GetPath(node));
					else
						Com_Printf("UI_ParseActionList: block expected after \"while\" (node: %s)\n", UI_GetPath(node));
					return nullptr;
				}
				break;
			}

		case EA_ELSE:
			/* check previous action */
			if (!lastAction || (lastAction->type != EA_IF && lastAction->type != EA_ELIF)) {
				Com_Printf("UI_ParseActionList: 'else' must be set after an 'if' or an 'elif' (node: %s)\n", UI_GetPath(node));
				return nullptr;
			}

			/* get the action block */
			*token = Com_EParse(text, errhead, nullptr);
			if (!*text)
				return nullptr;
			action->d.nonTerminal.left = nullptr;
			action->d.nonTerminal.right = UI_ParseActionList(node, text, token);
			if (action->d.nonTerminal.right == nullptr) {
				Com_Printf("UI_ParseActionList: block expected after \"else\" (node: %s)\n", UI_GetPath(node));
				return nullptr;
			}
			break;

		default:
			assert(false);
		}

		/* step */
		lastAction = action;
	}

	assert((*token)[0] == '}');

	/* return non nullptr value */
	if (firstAction == nullptr) {
		firstAction = UI_AllocStaticAction();
	}

	return firstAction;
}
示例#7
0
/**
 * @brief Parse a property value
 * @todo don't read the next token (need to change the script language)
 */
static bool UI_ParseProperty (void *object, const value_t *property, const char* objectName, const char **text, const char **token)
{
	const char *errhead = "UI_ParseProperty: unexpected end of file (object";
	static const char *notWellFormedValue = "UI_ParseProperty: \"%s\" is not a well formed node name (it must be quoted, uppercase const, a number, or prefixed with '*')\n";
	size_t bytes;
	int result;
	const int specialType = property->type & V_UI_MASK;

	if (property->type == V_NULL) {
		return false;
	}

	switch (specialType) {
	case V_NOT_UI:	/* common type */

		*token = Com_EParse(text, errhead, objectName);
		if (!*text)
			return false;
		if (!UI_TokenIsValue(*token, Com_GetType(text) == TT_QUOTED_WORD)) {
			Com_Printf(notWellFormedValue, *token);
			return false;
		}

		if (property->type == V_TRANSLATION_STRING) {
			/* selectbox values are static arrays */
			char* const target = Com_GetValue<char[]>(object, property);
			const char *translatableToken = *token;
			assert(property->size);
			if (translatableToken[0] == '_')
				translatableToken++;
			Q_strncpyz(target, translatableToken, property->size);
		} else {
			result = Com_ParseValue(object, *token, property->type, property->ofs, property->size, &bytes);
			if (result != RESULT_OK) {
				Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
				return false;
			}
		}
		break;

	case V_UI_REF:
		*token = Com_EParse(text, errhead, objectName);
		if (!*text)
			return false;
		if (!UI_TokenIsValue(*token, Com_GetType(text) == TT_QUOTED_WORD)) {
			Com_Printf(notWellFormedValue, *token);
			return false;
		}

		/* a reference to data is handled like this */
		ui_global.curadata = (byte*) Com_AlignPtr(ui_global.curadata, (valueTypes_t) (property->type & V_BASETYPEMASK));
		Com_GetValue<byte*>(object, property) = ui_global.curadata;

		/** @todo check for the moment its not a cvar */
		assert((*token)[0] != '*');

		/* sanity check */
		if ((property->type & V_BASETYPEMASK) == V_STRING && strlen(*token) > MAX_VAR - 1) {
			Com_Printf("UI_ParseProperty: Value '%s' is too long (key %s)\n", *token, property->string);
			return false;
		}

		result = Com_ParseValue(ui_global.curadata, *token, (valueTypes_t) (property->type & V_BASETYPEMASK), 0, property->size, &bytes);
		if (result != RESULT_OK) {
			Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
			return false;
		}
		ui_global.curadata += bytes;

		break;

	case V_UI_CVAR:	/* common type */
		*token = Com_EParse(text, errhead, objectName);
		if (!*text)
			return false;
		if (!UI_TokenIsValue(*token, Com_GetType(text) == TT_QUOTED_WORD)) {
			Com_Printf(notWellFormedValue, *token);
			return false;
		}

		/* references are parsed as string */
		if ((*token)[0] == '*') {
			/* a reference to data */
			ui_global.curadata = (byte*) Com_AlignPtr(ui_global.curadata, V_STRING);
			Com_GetValue<byte*>(object, property) = ui_global.curadata;

			/* sanity check */
			if (strlen(*token) > MAX_VAR - 1) {
				Com_Printf("UI_ParseProperty: Value '%s' is too long (key %s)\n", *token, property->string);
				return false;
			}

			result = Com_ParseValue(ui_global.curadata, *token, V_STRING, 0, 0, &bytes);
			if (result != RESULT_OK) {
				Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
				return false;
			}
			ui_global.curadata += bytes;
		} else {
			/* a reference to data */
			ui_global.curadata = (byte*) Com_AlignPtr(ui_global.curadata, (valueTypes_t)(property->type & V_BASETYPEMASK));
			Com_GetValue<byte*>(object, property) = ui_global.curadata;

			/* sanity check */
			if ((property->type & V_BASETYPEMASK) == V_STRING && strlen(*token) > MAX_VAR - 1) {
				Com_Printf("UI_ParseProperty: Value '%s' is too long (key %s)\n", *token, property->string);
				return false;
			}

			result = Com_ParseValue(ui_global.curadata, *token, (valueTypes_t)(property->type & V_BASETYPEMASK), 0, property->size, &bytes);
			if (result != RESULT_OK) {
				Com_Printf("UI_ParseProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
				return false;
			}
			ui_global.curadata += bytes;
		}
		break;

	case V_UI:

		switch ((int)property->type) {
		case V_UI_ACTION:
			result = UI_ParseEventProperty(static_cast<uiNode_t*>(object), property, text, token, errhead);
			if (!result)
				return false;
			break;

		case V_UI_EXCLUDERECT:
			result = UI_ParseExcludeRect(static_cast<uiNode_t*>(object), text, token, errhead);
			if (!result)
				return false;
			break;

		case V_UI_SPRITEREF:
			{
				*token = Com_EParse(text, errhead, objectName);
				if (!*text)
					return false;

				uiSprite_t const*& sprite = Com_GetValue<uiSprite_t const*>(object, property);
				sprite = UI_GetSpriteByName(*token);
				if (!sprite) {
					Com_Printf("UI_ParseProperty: sprite '%s' not found (object %s)\n", *token, objectName);
				}
			}
			break;

		case V_UI_IF:
			{
				*token = Com_EParse(text, errhead, objectName);
				if (!*text)
					return false;

				uiAction_t*& expression = Com_GetValue<uiAction_t*>(object, property);
				expression = UI_AllocStaticStringCondition(*token);
				if (!expression)
					return false;
			}
			break;

		case V_UI_DATAID:
			{
				*token = Com_EParse(text, errhead, objectName);
				if (!*text)
					return false;

				int& dataId = Com_GetValue<int>(object, property);
				dataId = UI_GetDataIDByName(*token);
				if (dataId < 0) {
					Com_Printf("UI_ParseProperty: Could not find shared data ID '%s' (%s@%s)\n",
							*token, objectName, property->string);
					return false;
				}
			}
			break;

		default:
			Com_Printf("UI_ParseProperty: unknown property type '%d' (0x%X) (%s@%s)\n",
					property->type, property->type, objectName, property->string);
			return false;
		}
		break;

	default:
		Com_Printf("UI_ParseProperties: unknown property type '%d' (0x%X) (%s@%s)\n",
				property->type, property->type, objectName, property->string);
		return false;
	}

	return true;
}
示例#8
0
/**
 * @brief Parse a component
 * @sa CL_ParseClientData
 * @code
 * component panel componentName {
 * }
 * @endcode
 */
bool UI_ParseComponent (const char *type, const char *name, const char **text)
{
	const char *errhead = "UI_ParseComponent: unexpected end of file (component";
	const char *token;

	if (!Q_streq(type, "component")) {
		Com_Error(ERR_FATAL, "UI_ParseComponent: \"component\" expected but \"%s\" found.\n", type);
		return false;	/* never reached */
	}

	/* check the name */
	if (!UI_TokenIsName(name, false)) {
		Com_Printf("UI_ParseNode: \"%s\" is not a well formed node name ([a-zA-Z_][a-zA-Z0-9_]*)\n", name);
		return false;
	}
	if (UI_TokenIsReserved(name)) {
		Com_Printf("UI_ParseNode: \"%s\" is a reserved token, we can't call a node with it\n", name);
		return false;
	}

	token = Com_EParse(text, errhead, "");
	if (text == nullptr)
		return false;

	/* get keyword */
	if (!Q_streq(token, "extends")) {
		Com_Printf("UI_ParseComponent: \"extends\" expected but \"%s\" found (component %s)\n", token, name);
		return false;
	}
	token = Com_EParse(text, errhead, "");
	if (text == nullptr)
		return false;

	/* initialize component */
	uiNode_t *component = nullptr;
	const uiBehaviour_t *behaviour = UI_GetNodeBehaviour(token);
	if (behaviour) {
		/* initialize a new node from behaviour */
		component = UI_AllocNode(name, behaviour->name, false);
	} else {
		const uiNode_t *inheritedComponent = UI_GetComponent(token);
		if (inheritedComponent) {
			/* initialize from a component */
			component = UI_CloneNode(inheritedComponent, nullptr, true, name, false);
		} else {
			Com_Printf("UI_ParseComponent: node behaviour/component '%s' doesn't exists (component %s)\n", token, name);
			return false;
		}
	}

	/* get body */
	token = Com_EParse(text, errhead, "");
	if (!*text)
		return false;
	bool result = UI_ParseNodeBody(component, text, &token, errhead);
	if (!result)
		return false;

	/* validate properties */
	UI_Node_Loaded(component);

	UI_InsertComponent(component);
	return true;
}
示例#9
0
/**
 * @brief Parser for setter command
 */
static bool UI_ParseSetAction (uiNode_t *node, uiAction_t *action, const char **text, const char **token, const char *errhead)
{
	const value_t *property;
	int type;
	uiAction_t* localAction;

	assert((*token)[0] == '*');

	Com_UnParseLastToken();
	action->d.nonTerminal.left = UI_ParseExpression(text);

	type = action->d.nonTerminal.left->type;
	if (type != EA_VALUE_CVARNAME && type != EA_VALUE_CVARNAME_WITHINJECTION
		&& type != EA_VALUE_PATHPROPERTY && type != EA_VALUE_PATHPROPERTY_WITHINJECTION) {
		Com_Printf("UI_ParseSetAction: Cvar or Node property expected. Type '%i' found\n", type);
		return false;
	}

	/* must use "equal" char between name and value */
	*token = Com_EParse(text, errhead, nullptr);
	if (!*text)
		return false;
	if (!Q_streq(*token, "=")) {
		Com_Printf("UI_ParseSetAction: Assign sign '=' expected between variable and value. '%s' found in node %s.\n", *token, UI_GetPath(node));
		return false;
	}

	/* get the value */
	if (type == EA_VALUE_CVARNAME || type == EA_VALUE_CVARNAME_WITHINJECTION) {
		action->d.nonTerminal.right = UI_ParseExpression(text);
		return true;
	}

	property = (const value_t *) action->d.nonTerminal.left->d.terminal.d2.data;

	*token = Com_EParse(text, errhead, nullptr);
	if (!*text)
		return false;

	if (Q_streq(*token, "{")) {
		uiAction_t* actionList;

		if (property != nullptr && property->type != V_UI_ACTION) {
			Com_Printf("UI_ParseSetAction: Property %s@%s do not expect code block.\n", UI_GetPath(node), property->string);
			return false;
		}

		actionList = UI_ParseActionList(node, text, token);
		if (actionList == nullptr)
			return false;

		localAction = UI_AllocStaticAction();
		localAction->type = EA_VALUE_RAW;
		localAction->d.terminal.d1.data = actionList;
		localAction->d.terminal.d2.integer = V_UI_ACTION;
		action->d.nonTerminal.right = localAction;

		return true;
	}

	if (Q_streq(*token, "(")) {
		Com_UnParseLastToken();
		action->d.nonTerminal.right = UI_ParseExpression(text);
		return true;
	}

	/* @todo everything should come from UI_ParseExpression */

	if (UI_IsInjectedString(*token)) {
		localAction = UI_AllocStaticAction();
		localAction->type = EA_VALUE_STRING_WITHINJECTION;
		localAction->d.terminal.d1.data = UI_AllocStaticString(*token, 0);
		action->d.nonTerminal.right = localAction;
		return true;
	}

	localAction = UI_AllocStaticAction();
	UI_InitRawActionValue(localAction, node, property, *token);
	action->d.nonTerminal.right = localAction;
	return true;
}
示例#10
0
/**
 * @brief parses the models.ufo and all files where UI models (menu_model) are defined
 * @sa CL_ParseClientData
 */
bool UI_ParseUIModel (const char *name, const char **text)
{
	uiModel_t *model;
	const char *token;
	int i;
	const char *errhead = "UI_ParseUIModel: unexpected end of file (names ";

	/* search for a UI models with same name */
	for (i = 0; i < ui_global.numModels; i++)
		if (Q_streq(ui_global.models[i].id, name)) {
			Com_Printf("UI_ParseUIModel: menu_model \"%s\" with same name found, second ignored\n", name);
			return false;
		}

	if (ui_global.numModels >= UI_MAX_MODELS) {
		Com_Printf("UI_ParseUIModel: Max UI models reached\n");
		return false;
	}

	/* initialize the model */
	model = &ui_global.models[ui_global.numModels];
	OBJZERO(*model);

	Vector4Set(model->color, 1, 1, 1, 1);

	model->id = Mem_PoolStrDup(name, ui_sysPool, 0);
	Com_DPrintf(DEBUG_CLIENT, "Found UI model %s (%i)\n", model->id, ui_global.numModels);

	/* get it's body */
	token = Com_Parse(text);

	if (!*text || token[0] != '{') {
		Com_Printf("UI_ParseUIModel: Model \"%s\" without body ignored\n", model->id);
		return false;
	}

	ui_global.numModels++;

	do {
		const value_t *v = nullptr;
		/* get the name type */
		token = Com_EParse(text, errhead, name);
		if (!*text)
			return false;
		if (token[0] == '}')
			break;

		v = UI_FindPropertyByName(uiModelProperties, token);
		if (!v) {
			Com_Printf("UI_ParseUIModel: unknown token \"%s\" ignored (UI model %s)\n", token, name);
			return false;
		}

		if (v->type == V_NULL) {
			if (Q_streq(v->string, "need")) {
				token = Com_EParse(text, errhead, name);
				if (!*text)
					return false;
				if (model->next != nullptr)
					Sys_Error("UI_ParseUIModel: second 'need' token found in model %s", name);
				model->next = UI_GetUIModel(token);
				 if (!model->next)
					Com_Printf("Could not find UI model %s", token);
			}
		} else {
			token = Com_EParse(text, errhead, name);
			if (!*text)
				return false;
			switch (v->type) {
			case V_HUNK_STRING:
				Mem_PoolStrDupTo(token, &Com_GetValue<char*>(model, v), ui_sysPool, 0);
				break;
			default:
				Com_EParseValue(model, token, v->type, v->ofs, v->size);
				break;
			}
		}
	} while (*text);

	return true;
}
示例#11
0
/**
 * @brief parse a node
 * @sa UI_ParseNodeProperties
 * @todo we can think about merging UI_ParseNodeProperties here
 * @note first token already read
 * @note dont read more than the need token (last right token is '}' of end of node)
 */
static uiNode_t *UI_ParseNode (uiNode_t *parent, const char **text, const char **token, const char *errhead)
{
	uiNode_t *node = nullptr;
	uiBehaviour_t *behaviour;
	uiNode_t *component = nullptr;

	/* get the behaviour */
	behaviour = UI_GetNodeBehaviour(*token);
	if (!behaviour) {
		component = UI_GetComponent(*token);
	}
	if (behaviour == nullptr && component == nullptr) {
		Com_Printf("UI_ParseNode: node behaviour/component '%s' doesn't exists (%s)\n", *token, UI_GetPath(parent));
		return nullptr;
	}

	/* get the name */
	*token = Com_EParse(text, errhead, "");
	if (!*text)
		return nullptr;
	if (!UI_TokenIsName(*token, Com_GetType(text) == TT_QUOTED_WORD)) {
		Com_Printf("UI_ParseNode: \"%s\" is not a well formed node name ([a-zA-Z_][a-zA-Z0-9_]*)\n", *token);
		return nullptr;
	}
	if (UI_TokenIsReserved(*token)) {
		Com_Printf("UI_ParseNode: \"%s\" is a reserved token, we can't call a node with it\n", *token);
		return nullptr;
	}

	/* test if node already exists */
	/* Already existing node should only come from inherited node,we should not have 2 definitions of the same node into the same window. */
	if (parent)
		node = UI_GetNode(parent, *token);

	/* reuse a node */
	if (node) {
		if (node->behaviour != behaviour) {
			Com_Printf("UI_ParseNode: we can't change node type (node \"%s\")\n", UI_GetPath(node));
			return nullptr;
		}
		Com_DPrintf(DEBUG_CLIENT, "... over-riding node %s\n", UI_GetPath(node));

	/* else initialize a component */
	} else if (component) {
		node = UI_CloneNode(component, nullptr, true, *token, false);
		if (parent) {
			if (parent->root)
				UI_UpdateRoot(node, parent->root);
			UI_AppendNode(parent, node);
		}

	/* else initialize a new node */
	} else {
		node = UI_AllocNode(*token, behaviour->name, false);
		node->parent = parent;
		if (parent)
			node->root = parent->root;
		/** @todo move it into caller */
		if (parent)
			UI_AppendNode(parent, node);
	}

	/* get body */
	const bool result = UI_ParseNodeBody(node, text, token, errhead);
	if (!result)
		return nullptr;

	/* validate properties */
	UI_Node_Loaded(node);

	return node;
}
示例#12
0
static void SCP_ParseMission (const char *name, const char **text)
{
	const char *errhead = "SCP_ParseMission: unexpected end of file (mission ";
	staticMission_t *ms;
	const value_t *vp;
	const char *token;
	int i;
	size_t writtenBytes;

	token = Com_Parse(text);
	if (token[0] == '\0' || text[0] == '\0') {
		Com_Printf("SCP_ParseMission: mission def \"%s\" without name ignored\n", name);
		return;
	}

	/* search for missions with same name */
	for (i = 0; i < scd->numMissions; i++)
		if (Q_streq(token, scd->missions[i].id))
			break;

	if (i < scd->numMissions) {
		Com_Printf("SCP_ParseMission: mission def \"%s\" with same name found, second ignored\n", token);
		return;
	}

	if (scd->numMissions >= MAX_STATIC_MISSIONS) {
		Com_Printf("SCP_ParseMission: Too many missions, ignore '%s'\n", token);
		return;
	}

	/* initialize the menu */
	ms = &scd->missions[scd->numMissions];
	OBJZERO(*ms);

	Q_strncpyz(ms->id, token, sizeof(ms->id));

	/* get it's body */
	token = Com_Parse(text);

	if (text[0] == '\0' || !Q_streq(token, "{")) {
		Com_Printf("SCP_ParseMission: mission def \"%s\" without body ignored\n", ms->id);
		return;
	}

	do {
		token = Com_EParse(text, errhead, ms->id);
		if (text[0] == '\0')
			break;
		if (token[0] == '}')
			break;

		for (vp = mission_vals; vp->string; vp++)
			if (Q_streq(token, vp->string)) {
				/* found a definition */
				token = Com_EParse(text, errhead, ms->id);
				if (text[0] == '\0')
					return;

				if (vp->ofs) {
					Com_ParseValue(ms, token, vp->type, vp->ofs, vp->size, &writtenBytes);
				} else {
					Com_Printf("SCP_ParseMission: unknown token '%s'\n", token);
				}
				break;
			}

		if (!vp->string) {
			if (Q_streq(token, "city")) {
				const city_t *city;
				token = Com_EParse(text, errhead, ms->id);
				if (text[0] == '\0')
					return;
				city = CP_GetCity(token);
				if (city == NULL)
					Com_Printf("SCP_ParseMission: unknown city \"%s\" ignored (mission %s)\n", token, ms->id);
				else
					Vector2Copy(city->pos, ms->pos);
			} else {
				Com_Printf("SCP_ParseMission: unknown token \"%s\" ignored (mission %s)\n", token, ms->id);
			}
		}

	} while (*text);

	mapDef_t *mapDef = cgi->Com_GetMapDefinitionByID(ms->id);
	if (mapDef == NULL) {
		Com_Printf("SCP_ParseMission: invalid mapdef for '%s'\n", ms->id);
		return;
	}

	if (Vector2Empty(ms->pos)) {
		if (!CP_GetRandomPosOnGeoscapeWithParameters(ms->pos, mapDef->terrains, mapDef->cultures, mapDef->populations, NULL)) {
			Com_Printf("SCP_ParseMission: could not find a valid position for '%s'\n", ms->id);
			return;
		}
	}

	scd->numMissions++;
}
示例#13
0
static void SCP_ParseStageSet (const char *name, const char **text)
{
	const char *errhead = "SCP_ParseStageSet: unexpected end of file (stageset ";
	stageSet_t *sp;
	const value_t *vp;
	char missionstr[256];
	const char *token, *misp;
	int j;
	size_t writtenBytes;

	/* initialize the stage */
	sp = &scd->stageSets[scd->numStageSets++];
	OBJZERO(*sp);
	Q_strncpyz(sp->name, name, sizeof(sp->name));

	/* get it's body */
	token = Com_Parse(text);
	if (text[0] == '\0' || !Q_streq(token, "{")) {
		Com_Printf("SCP_ParseStageSet: stageset def \"%s\" without body ignored\n", sp->name);
		scd->numStageSets--;
		return;
	}

	do {
		token = Com_EParse(text, errhead, sp->name);
		if (!*text)
			break;
		if (token[0] == '}')
			break;

		/* check for some standard values */
		for (vp = stageset_vals; vp->string; vp++)
			if (Q_streq(token, vp->string)) {
				/* found a definition */
				token = Com_EParse(text, errhead, sp->name);
				if (!*text)
					return;

				Com_ParseValue(sp, token, vp->type, vp->ofs, vp->size, &writtenBytes);
				break;
			}
		if (vp->string)
			continue;

		/* get mission set */
		if (Q_streq(token, "missions")) {
			token = Com_EParse(text, errhead, sp->name);
			if (!*text)
				return;
			Q_strncpyz(missionstr, token, sizeof(missionstr));
			misp = missionstr;

			/* add mission options */
			sp->numMissions = 0;
			do {
				token = Com_Parse(&misp);
				if (!misp)
					break;

				for (j = 0; j < scd->numMissions; j++) {
					if (Q_streq(token, scd->missions[j].id)) {
						sp->missions[sp->numMissions++] = j;
						break;
					}
				}

				if (j == scd->numMissions) {
					const mapDef_t *mapDef = cgi->Com_GetMapDefinitionByID(token);
					if (mapDef != NULL) {
						if (j < MAX_STATIC_MISSIONS - 1) {
							/* we don't need and mission definition in the scripts if we just would like to link a mapdef */
							staticMission_t *mis = &scd->missions[j];
							OBJZERO(*mis);
							Q_strncpyz(mis->id, token, sizeof(mis->id));
							if (!CP_GetRandomPosOnGeoscapeWithParameters(mis->pos, mapDef->terrains, mapDef->cultures, mapDef->populations, NULL)) {
								Com_Printf("SCP_ParseMission: could not find a valid position for '%s'\n", mis->id);
								continue;
							}
							sp->missions[sp->numMissions++] = scd->numMissions++;
						}
					} else {
						Com_Printf("SCP_ParseStageSet: unknown mission \"%s\" ignored (stageset %s)\n", token, sp->name);
					}
				}
			} while (misp && sp->numMissions < MAX_SETMISSIONS);
			continue;
		}

		Com_Printf("SCP_ParseStageSet: unknown token \"%s\" ignored (stageset %s)\n", token, sp->name);
	} while (*text);
}
示例#14
0
/**
 * @brief Copies an entry from the installation description file into the list of installation templates.
 * @note Parses one "installation" entry in the installation.ufo file and writes
 * it into the next free entry in installationTemplates.
 * @param[in] name Unique test-id of a installationTemplate_t.
 * @param[in] text the rest of the script file that is tokenized here
 */
void INS_ParseInstallations (const char *name, const char **text)
{
	installationTemplate_t *installation;
	const char *errhead = "INS_ParseInstallations: unexpected end of file (names ";
	const char *token;
	int i;

	/* get id list body */
	token = Com_Parse(text);
	if (!*text || *token != '{') {
		Com_Printf("INS_ParseInstallations: installation \"%s\" without body ignored\n", name);
		return;
	}

	if (!name) {
		Com_Printf("INS_ParseInstallations: installation name not specified.\n");
		return;
	}

	if (ccs.numInstallationTemplates >= MAX_INSTALLATION_TEMPLATES) {
		Com_Printf("INS_ParseInstallations: too many installation templates\n");
		ccs.numInstallationTemplates = MAX_INSTALLATION_TEMPLATES;	/* just in case it's bigger. */
		return;
	}

	for (i = 0; i < ccs.numInstallationTemplates; i++) {
		if (Q_streq(ccs.installationTemplates[i].name, name)) {
			Com_Printf("INS_ParseInstallations: Second installation with same name found (%s) - second ignored\n", name);
			return;
		}
	}

	/* new entry */
	installation = &ccs.installationTemplates[ccs.numInstallationTemplates];
	OBJZERO(*installation);
	installation->id = Mem_PoolStrDup(name, cp_campaignPool, 0);

	Com_DPrintf(DEBUG_CLIENT, "...found installation %s\n", installation->id);

	ccs.numInstallationTemplates++;
	do {
		/* get the name type */
		token = Com_EParse(text, errhead, name);
		if (!*text)
			break;
		if (*token == '}')
			break;

		/* check for some standard values */
		if (!Com_ParseBlockToken(name, text, installation, installation_vals, cp_campaignPool, token)) {
			/* other values */
			if (Q_streq(token, "cost")) {
				char cvarname[MAX_VAR] = "mn_installation_";

				Q_strcat(cvarname, installation->id, sizeof(cvarname));
				Q_strcat(cvarname, "_cost", sizeof(cvarname));

				token = Com_EParse(text, errhead, name);
				if (!*text)
					return;
				installation->cost = atoi(token);

				Cvar_Set(cvarname, va(_("%d c"), atoi(token)));
			} else if (Q_streq(token, "buildtime")) {
				char cvarname[MAX_VAR];

				token = Com_EParse(text, errhead, name);
				if (!*text)
					return;
				installation->buildTime = atoi(token);

				Com_sprintf(cvarname, sizeof(cvarname), "mn_installation_%s_buildtime", installation->id);
				Cvar_Set(cvarname, va(ngettext("%d day", "%d days", atoi(token)), atoi(token)));
			}
		}
	} while (*text);
}