예제 #1
0
void CTemplateLoader::ConstructTemplateActor(const std::string& actorName, CParamNode& out)
{
	// Load the base actor template if necessary
	const char* templateName = "special/actor";
	if (!LoadTemplateFile(templateName, 0))
	{
		LOGERROR("Failed to load entity template '%s'", templateName);
		return;
	}

	// Copy the actor template
	out = m_TemplateFileData[templateName];

	// Initialise the actor's name and make it an Atlas selectable entity.
	std::wstring actorNameW = wstring_from_utf8(actorName);
	std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(actorNameW));
	std::string xml = "<Entity>"
	                      "<VisualActor><Actor>" + name + "</Actor><ActorOnly/></VisualActor>"
						  // arbitrary-sized Footprint definition to make actors' selection outlines show up in Atlas
						  "<Footprint><Circle radius='2.0'/><Height>1.0</Height></Footprint>"
	                      "<Selectable>"
	                          "<EditorOnly/>"
	                          "<Overlay><Texture><MainTexture>actor.png</MainTexture><MainTextureMask>actor_mask.png</MainTextureMask></Texture></Overlay>"
	                      "</Selectable>"
	                  "</Entity>";

	CParamNode::LoadXMLString(out, xml.c_str(), actorNameW.c_str());
}
예제 #2
0
void dParserCompiler::GenerateHeaderFile (
	const dString& className, 
	const dString& scannerClassName, 
	const char* const outputFileName, 
	const dTree<dTokenInfo, dCRCTYPE>& symbolList, 
	const dString& userVariableClass) 
{
	dString templateHeader;
	LoadTemplateFile("dParserTemplate_h.txt", templateHeader);

	ReplaceAllMacros (templateHeader, className, "$(className)");
	ReplaceAllMacros (templateHeader, scannerClassName, "$(scannerClass)");
	ReplaceMacro (templateHeader, userVariableClass, "$(userVariableClass)");


	dTree<dTree<dTokenInfo, dCRCTYPE>::dTreeNode*, int> sortToken;
	dTree<dTokenInfo, dCRCTYPE>::Iterator iter (symbolList);
	for (iter.Begin(); iter; iter ++) {
		const dTokenInfo& info = iter.GetNode()->GetInfo();
		if ((info.m_type == TERMINAL) && (info.m_tokenId >= 256)) {
			sortToken.Insert(iter.GetNode(), info.m_tokenId);
		}
	} 

	dTree<dTree<dTokenInfo, dCRCTYPE>::dTreeNode*, int>::Iterator iter1 (sortToken);
	bool first = true;
	char text[256];
	sprintf (text, " = %d, \n", DACCEPTING_TOKEN);
	dString enumdTokens ("\t\tACCEPTING_TOKEN");
	enumdTokens += text;

	sprintf (text, " = %d, \n", DERROR_TOKEN);
	enumdTokens += "\t\tERROR_TOKEN";
	enumdTokens += text;

	for (iter1.Begin(); iter1; iter1 ++) {
		dTree<dTokenInfo, dCRCTYPE>::dTreeNode* const node = iter1.GetNode()->GetInfo();
		//const dString& name = node->GetKey();
		const dString& name = node->GetInfo().m_name;
		enumdTokens += "\t\t";
		enumdTokens += name;
		if (first) {
			first = false;
			sprintf (text, " = %d, \n", iter1.GetKey());
			enumdTokens += text;
		} else {
			enumdTokens += ",\n";
		}
	}

	enumdTokens.Replace(enumdTokens.Size()-2, 2, "");
	ReplaceMacro (templateHeader, enumdTokens, "$(Tokens)");

	SaveFile(outputFileName, ".h", templateHeader);
}
예제 #3
0
const CParamNode& CTemplateLoader::GetTemplateFileData(const std::string& templateName)
{
	// Load the template if necessary
	if (!LoadTemplateFile(templateName, 0))
	{
		LOGERROR("Failed to load entity template '%s'", templateName.c_str());
		return NULL_NODE;
	}

	return m_TemplateFileData[templateName];
}
예제 #4
0
void dParserCompiler::ScanGrammarFile(
	const dString& inputRules, 
	dProductionRule& ruleList, 
	dTree<dTokenInfo, dCRCTYPE>& symbolList, 
	dOperatorsPrecedence& operatorPrecedence,
	dString& userCodeBlock,
	dString& userVariableClass,
	dString& endUserCode,
	int& lastTokenEnum)
{
	dString startSymbol ("");
	int tokenEnumeration = 256;
	int operatorPrecedencePriority = 0;

	dParserLexical lexical (inputRules.GetStr());
	LoadTemplateFile("dParserUserVariableTemplate_cpp.txt", userVariableClass);

	// scan the definition segment
	for (dToken token = dToken(lexical.NextToken()); token != GRAMMAR_SEGMENT; ) {
		switch (int (token)) 
		{
			case START:
			{
				token = dToken(lexical.NextToken());
				startSymbol = lexical.GetTokenString();
				token = dToken(lexical.NextToken());
				break;
			}

			case TOKEN:
			{
				for (token = dToken(lexical.NextToken()); token == LITERAL; token = dToken(lexical.NextToken())) {
					const char* const name = lexical.GetTokenString();
					symbolList.Insert(dTokenInfo (tokenEnumeration, TERMINAL, name), dCRC64 (name));
					tokenEnumeration ++;
				}
				break;
			}

			case LEFT:
			case RIGHT:
			{
				dOperatorsAssociation& association = operatorPrecedence.Append()->GetInfo();
				association.m_prioprity = operatorPrecedencePriority;
				operatorPrecedencePriority ++;
				switch (int (token))
				{
					case LEFT:
						association.m_associativity = dOperatorsAssociation::m_left;
						break;
					case RIGHT:
						association.m_associativity = dOperatorsAssociation::m_right;
						break;
				}

				for (token = dToken(lexical.NextToken()); (token == LITERAL) || ((token < 256) && !isalnum (token)); token = dToken(lexical.NextToken())) {
					association.Append(dCRC64 (lexical.GetTokenString()));
				}
				break;
			}


			case UNION:
			{
				token = dToken(lexical.NextToken());
				dAssert (token == SEMANTIC_ACTION);
				userVariableClass = lexical.GetTokenString() + 1;
				userVariableClass.Replace(userVariableClass.Size() - 1, 1, "");
				token = dToken(lexical.NextToken());
				break;
			}


			case CODE_BLOCK:
			{
				userCodeBlock += lexical.GetTokenString();
				token = dToken(lexical.NextToken());
				break;
			}

			case EXPECT:
			{
				token = dToken(lexical.NextToken());
				dAssert (token == INTEGER);
				m_shiftReduceExpectedWarnings = atoi (lexical.GetTokenString());
				token = dToken(lexical.NextToken());
				break;
			}

			default:;
			{
				dAssert (0);
				token = dToken(lexical.NextToken());
			}
		}
	}


	int ruleNumber = 1;
	lastTokenEnum = tokenEnumeration;

	// scan the production rules segment
	dToken token1 = dToken(lexical.NextToken());
	for (; (token1 != GRAMMAR_SEGMENT) && (token1 != -1); token1 = dToken(lexical.NextToken())) {
		//dTrace (("%s\n", lexical.GetTokenString()));
		switch (int (token1)) 
		{
			case LITERAL:
			{
				// add the first Rule;
				dRuleInfo& rule = ruleList.Append()->GetInfo();
				rule.m_token = token1;
				rule.m_type = NONTERMINAL;
				rule.m_name = lexical.GetTokenString();
				rule.m_nameCRC = dCRC64 (lexical.GetTokenString());

				dTree<dTokenInfo, dCRCTYPE>::dTreeNode* nonTerminalIdNode = symbolList.Find(rule.m_nameCRC);
				if (!nonTerminalIdNode) {
					nonTerminalIdNode = symbolList.Insert(dTokenInfo (tokenEnumeration, NONTERMINAL, rule.m_name), rule.m_nameCRC);
					tokenEnumeration ++;
				}
				rule.m_ruleId = nonTerminalIdNode->GetInfo().m_tokenId;
				rule.m_ruleNumber = ruleNumber;
				ruleNumber ++;

				token1 = ScanGrammarRule(lexical, ruleList, symbolList, ruleNumber, tokenEnumeration, operatorPrecedence); 
				break;
			}
			default:
				dAssert (0);
		}
	}

	dProductionRule::dListNode* firtRuleNode = ruleList.GetFirst();	
	if (startSymbol != "") {
		firtRuleNode = ruleList.Find (dCRC64 (startSymbol.GetStr()));	
	}

	//Expand the Grammar Rule by adding an empty start Rule;
	const dRuleInfo& firstRule = firtRuleNode->GetInfo();

	dRuleInfo& rule = ruleList.Addtop()->GetInfo();
	rule.m_ruleNumber = 0;
	rule.m_ruleId = tokenEnumeration;
	rule.m_token = firstRule.m_token;
	rule.m_type = NONTERMINAL;
	rule.m_name = firstRule.m_name + dString("__");
	rule.m_nameCRC = dCRC64 (rule.m_name.GetStr());
	symbolList.Insert(dTokenInfo (tokenEnumeration, rule.m_type, rule.m_name), rule.m_nameCRC);
	tokenEnumeration ++;
	
	dSymbol& symbol = rule.Append()->GetInfo();
	symbol.m_token = firstRule.m_token;
	symbol.m_type = firstRule.m_type;
	symbol.m_name = firstRule.m_name;
	symbol.m_nameCRC = firstRule.m_nameCRC;

	// scan literal use code
	if (token1 == GRAMMAR_SEGMENT) {
		endUserCode = lexical.GetNextBuffer();
		//endUserCode += "\n";
	}
}
예제 #5
0
void dParserCompiler::GenerateParserCode (
	const dString& className, 
	const dString& scannerClassName, 
	const char* const outputFileName, 
	const dTree<dTokenInfo, dCRCTYPE>& symbolList, 
	dTree<dState*, dCRCTYPE>& stateList, 
	const dString& userCode, 
	const dString& endUserCode,
	int lastTerminalTokenEnum)
{
	dString templateHeader ("");
	LoadTemplateFile("dParserTemplate _cpp.txt", templateHeader);

	int position = templateHeader.Find ("$(userCode)");
	templateHeader.Replace(position, 11, userCode);

	ReplaceAllMacros (templateHeader, className, "$(className)");
	ReplaceAllMacros (templateHeader, scannerClassName, "$(scannerClass)");


	char text[256];
	sprintf (text, "%d", lastTerminalTokenEnum);
	ReplaceMacro (templateHeader, text, "&(lastTerminalToken)");

	dTree<dState*, dCRCTYPE> sortedStates;
	dTree<dState*, dCRCTYPE>::Iterator stateIter (stateList);
	for (stateIter.Begin(); stateIter; stateIter ++) {
		dState* const state = stateIter.GetNode()->GetInfo();
		sortedStates.Insert(state, state->m_number);
	}

	dTree<int, dString> actionFilter;

	dString emptySematicAction ("");
	dString stateActionsStart ("");
	dString stateActionsCount ("");
	dString nextActionsStateList ("");
	dString sematicActions ("");
	int entriesCount = 0;

	int newLineCount = 0;
	int starAndCountIndex = 0;
	dTree<dState*, dCRCTYPE>::Iterator sortStateIter (sortedStates);


	const char* const caseTabs0 = "\t\t\t\t\t\t";
	//const char* const caseTabs1 = "\t\t\t\t\t\t\t";
	for (sortStateIter.Begin(); sortStateIter; sortStateIter ++) {
		dState* const state = sortStateIter.GetNode()->GetInfo();

		int count = 0;
		dTree<dActionEntry, int> actionSort;
		dTree<dAction, dCRCTYPE>::Iterator actionIter (state->m_actions);
		for (actionIter.Begin(); actionIter; actionIter++) {
			count ++;

			dAction& action = actionIter.GetNode()->GetInfo();
			if (action.m_type == dSHIFT) {
				dCRCTYPE actionSymbol = actionIter.GetKey();
				dAssert (symbolList.Find(actionSymbol));

				dActionEntry entry;
				entry.m_stateType = char (action.m_type);
				entry.m_errorRule = state->m_hasErroItem ? 1 : 0;
				entry.m_ruleIndex = 0;
				entry.m_ruleSymbols = 0;
				entry.m_nextState = short (action.m_nextState);
				entry.m_token = short (symbolList.Find(actionSymbol)->GetInfo().m_tokenId);

				actionSort.Insert (entry, entry.m_token);

			} else if (action.m_type == dREDUCE) {

				dCRCTYPE actionSymbol = actionIter.GetKey();
				dAssert (symbolList.Find(actionSymbol));

				dRuleInfo& reduceRule = action.m_reduceRuleNode->GetInfo();
				dAssert (symbolList.Find(reduceRule.m_nameCRC));
				dAssert (symbolList.Find(reduceRule.m_nameCRC)->GetInfo().m_tokenId >= 256);

				dActionEntry entry;
				entry.m_stateType = char (action.m_type);
				entry.m_errorRule = 0; //state->m_hasErroItem ? 1 : 0;
				entry.m_ruleIndex = short (reduceRule.m_ruleNumber);
				entry.m_ruleSymbols = short (reduceRule.GetCount());
				entry.m_nextState = short (symbolList.Find(reduceRule.m_nameCRC)->GetInfo().m_tokenId - lastTerminalTokenEnum);
				entry.m_token = short (symbolList.Find(actionSymbol)->GetInfo().m_tokenId);
				actionSort.Insert (entry, entry.m_token);

				if (!reduceRule.m_ruleReduced && (reduceRule.m_semanticActionCode != emptySematicAction)) {
					// issue a sematic action code;

					reduceRule.m_ruleReduced = true;
					char text[128];
					dString userSematicAction (reduceRule.m_semanticActionCode);
					int symbolsCount = int (entry.m_ruleSymbols);
					for (int i = 0; i < symbolsCount; i ++) {

						sprintf (text, "%d", symbolsCount - i);
						dString macro ("$");
						macro += text;

						sprintf (text, "%d", symbolsCount - i - 1);
						dString macroVariable ("parameter[");
						macroVariable += text;
						macroVariable += "].m_value";
						ReplaceAllMacros (userSematicAction, macroVariable, macro);
					}
					ReplaceAllMacros (userSematicAction, "entry.m_value", "$$");

					sprintf (text, "%d:", reduceRule.m_ruleNumber);
					sematicActions += caseTabs0;
					sematicActions += "case "; 
					sematicActions += text; 
					//sematicActions += "// rule ";
					sematicActions += "// ";
					sematicActions += reduceRule.m_name;
					sematicActions += " : ";
					for (dRuleInfo::dListNode* node = reduceRule.GetFirst(); node; node = node->GetNext()) {
						sematicActions+= node->GetInfo().m_name;
						sematicActions += " ";
					}
					sematicActions += "\n";
					sematicActions += userSematicAction;
					sematicActions += "\nbreak;\n\n";
				}

			} else {
				dAssert (action.m_type == dACCEPT);

				dActionEntry entry;
				entry.m_stateType = char (action.m_type);
				entry.m_errorRule = 0; //state->m_hasErroItem ? 1 : 0;
				entry.m_ruleIndex = 0;
				entry.m_ruleSymbols = 0;
				entry.m_nextState = 0;
				entry.m_token = DACCEPTING_TOKEN;
				actionSort.Insert (entry, entry.m_token);
			}
		}

		int actionIndex = entriesCount;
		dString stateActions ("");
		dTree<dActionEntry, int>::Iterator iter (actionSort);
		for (iter.Begin(); iter; iter ++) {
			const dActionEntry& entry = iter.GetNode()->GetInfo();
			sprintf (text, "%d, %d, %d, %d, %d, %d, ", entry.m_token, entry.m_errorRule, entry.m_stateType, entry.m_nextState, entry.m_ruleSymbols, entry.m_ruleIndex);
			stateActions += text; 
			entriesCount ++;
		}

		dTree<int, dString>::dTreeNode* const stateActionNode = actionFilter.Find(stateActions);
		if (stateActionNode) {
			entriesCount = actionIndex;
			actionIndex =  stateActionNode->GetInfo();
		} else {
			actionFilter.Insert(actionIndex, stateActions);

			for (iter.Begin(); iter; iter ++) {
				if (newLineCount % 4 == 0) {
					nextActionsStateList += "\n\t\t\t";
				}
				newLineCount ++;
				const dActionEntry& entry = iter.GetNode()->GetInfo();
				sprintf (text, "dActionEntry (%d, %d, %d, %d, %d, %d), ", entry.m_token, entry.m_errorRule, entry.m_stateType, entry.m_nextState, entry.m_ruleSymbols, entry.m_ruleIndex);
				nextActionsStateList += text; 
			}
		}

		if ((starAndCountIndex % 24) == 0) {
			stateActionsStart += "\n\t\t\t";
			stateActionsCount += "\n\t\t\t";
		}
		starAndCountIndex ++;

		sprintf (text, "%d, ", actionIndex);
		stateActionsStart += text;

		sprintf (text, "%d, ", count);
		stateActionsCount += text;
	}
	nextActionsStateList.Replace(nextActionsStateList.Size()-2, 2, "");
	stateActionsCount.Replace(stateActionsCount.Size()-2, 2, "");
	stateActionsStart.Replace(stateActionsStart.Size()-2, 2, "");

	ReplaceMacro (templateHeader, stateActionsCount, "$(actionsCount)");
	ReplaceMacro (templateHeader, stateActionsStart, "$(actionsStart)");
	ReplaceMacro (templateHeader, nextActionsStateList, "$(actionTable)");

	ReplaceMacro (templateHeader, sematicActions, "$(semanticActionsCode)");


	dString stateGotoStart ("");
	dString stateGotoCount ("");
	dString nextGotoStateList ("");
	entriesCount = 0;
	int newLine = 0;
	int gotoStateCount = 0;
	for (sortStateIter.Begin(); sortStateIter; sortStateIter ++) {

		char text[256];
		dState* const state = sortStateIter.GetNode()->GetInfo();

		int currentEntryuCount = entriesCount;

		int count = 0;
		dTree<dState*, dCRCTYPE>::Iterator gotoIter (state->m_goto); 
		dTree<dTree<dState*, dCRCTYPE>::dTreeNode*, int> sortGotoActions;
		for (gotoIter.Begin(); gotoIter; gotoIter++) {
			int id = symbolList.Find(gotoIter.GetKey())->GetInfo().m_tokenId;
			sortGotoActions.Insert(gotoIter.GetNode(), id);
		}

		dTree<dTree<dState*, dCRCTYPE>::dTreeNode*, int>::Iterator iter1 (sortGotoActions);
		for (iter1.Begin(); iter1; iter1++) {
			count ++;
			if ((newLine % 5) == 0) {
				nextGotoStateList += "\n\t\t\t";
			}
			newLine ++;

			dTree<dState*, dCRCTYPE>::dTreeNode* const node = iter1.GetNode()->GetInfo();
			dState* const targetState = node->GetInfo();

			dGotoEntry entry;
			entry.m_nextState = short (targetState->m_number);
			entry.m_token = short(iter1.GetKey());

			sprintf (text, "dGotoEntry (%d, %d), ", entry.m_token, entry.m_nextState);
			nextGotoStateList += text;
			entriesCount ++;
		}

		if ((gotoStateCount % 24) == 0) {
			stateGotoStart += "\n\t\t\t";
			stateGotoCount += "\n\t\t\t";
		}
		gotoStateCount ++;

		sprintf (text, "%d, ", currentEntryuCount);
		stateGotoStart += text;

		sprintf (text, "%d, ", count);
		stateGotoCount += text;
	}

	nextGotoStateList.Replace(nextGotoStateList.Size()-2, 2, "");
	stateGotoCount.Replace(stateGotoCount.Size()-2, 2, "");
	stateGotoStart.Replace(stateGotoStart.Size()-2, 2, "");

	ReplaceMacro (templateHeader, stateGotoCount, "$(gotoCount)");
	ReplaceMacro (templateHeader, stateGotoStart, "$(gotoStart)");
	ReplaceMacro (templateHeader, nextGotoStateList, "$(gotoTable)");

	templateHeader += endUserCode;
	SaveFile(outputFileName, ".cpp", templateHeader);
}
예제 #6
0
bool CTemplateLoader::LoadTemplateFile(const std::string& templateName, int depth)
{
	// If this file was already loaded, we don't need to do anything
	if (m_TemplateFileData.find(templateName) != m_TemplateFileData.end())
		return true;

	// Handle infinite loops more gracefully than running out of stack space and crashing
	if (depth > 100)
	{
		LOGERROR("Probable infinite inheritance loop in entity template '%s'", templateName.c_str());
		return false;
	}

	// Handle special case "actor|foo"
	if (templateName.find("actor|") == 0)
	{
		ConstructTemplateActor(templateName.substr(6), m_TemplateFileData[templateName]);
		return true;
	}

	// Handle special case "preview|foo"
	if (templateName.find("preview|") == 0)
	{
		// Load the base entity template, if it wasn't already loaded
		std::string baseName = templateName.substr(8);
		if (!LoadTemplateFile(baseName, depth+1))
		{
			LOGERROR("Failed to load entity template '%s'", baseName.c_str());
			return false;
		}
		// Copy a subset to the requested template
		CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], false);
		return true;
	}

	// Handle special case "corpse|foo"
	if (templateName.find("corpse|") == 0)
	{
		// Load the base entity template, if it wasn't already loaded
		std::string baseName = templateName.substr(7);
		if (!LoadTemplateFile(baseName, depth+1))
		{
			LOGERROR("Failed to load entity template '%s'", baseName.c_str());
			return false;
		}
		// Copy a subset to the requested template
		CopyPreviewSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName], true);
		return true;
	}

	// Handle special case "mirage|foo"
	if (templateName.find("mirage|") == 0)
	{
		// Load the base entity template, if it wasn't already loaded
		std::string baseName = templateName.substr(7);
		if (!LoadTemplateFile(baseName, depth+1))
		{
			LOGERROR("Failed to load entity template '%s'", baseName.c_str());
			return false;
		}
		// Copy a subset to the requested template
		CopyMirageSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
		return true;
	}

	// Handle special case "foundation|foo"
	if (templateName.find("foundation|") == 0)
	{
		// Load the base entity template, if it wasn't already loaded
		std::string baseName = templateName.substr(11);
		if (!LoadTemplateFile(baseName, depth+1))
		{
			LOGERROR("Failed to load entity template '%s'", baseName.c_str());
			return false;
		}
		// Copy a subset to the requested template
		CopyFoundationSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
		return true;
	}

	// Handle special case "construction|foo"
	if (templateName.find("construction|") == 0)
	{
		// Load the base entity template, if it wasn't already loaded
		std::string baseName = templateName.substr(13);
		if (!LoadTemplateFile(baseName, depth+1))
		{
			LOGERROR("Failed to load entity template '%s'", baseName.c_str());
			return false;
		}
		// Copy a subset to the requested template
		CopyConstructionSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
		return true;
	}

	// Handle special case "resource|foo"
	if (templateName.find("resource|") == 0)
	{
		// Load the base entity template, if it wasn't already loaded
		std::string baseName = templateName.substr(9);
		if (!LoadTemplateFile(baseName, depth+1))
		{
			LOGERROR("Failed to load entity template '%s'", baseName.c_str());
			return false;
		}
		// Copy a subset to the requested template
		CopyResourceSubset(m_TemplateFileData[templateName], m_TemplateFileData[baseName]);
		return true;
	}

	// Normal case: templateName is an XML file:

	VfsPath path = VfsPath(TEMPLATE_ROOT) / wstring_from_utf8(templateName + ".xml");
	CXeromyces xero;
	PSRETURN ok = xero.Load(g_VFS, path);
	if (ok != PSRETURN_OK)
		return false; // (Xeromyces already logged an error with the full filename)

	int attr_parent = xero.GetAttributeID("parent");
	CStr parentName = xero.GetRoot().GetAttributes().GetNamedItem(attr_parent);
	if (!parentName.empty())
	{
		// To prevent needless complexity in template design, we don't allow |-separated strings as parents
		if (parentName.find('|') != parentName.npos)
		{
			LOGERROR("Invalid parent '%s' in entity template '%s'", parentName.c_str(), templateName.c_str());
			return false;
		}

		// Ensure the parent is loaded
		if (!LoadTemplateFile(parentName, depth+1))
		{
			LOGERROR("Failed to load parent '%s' of entity template '%s'", parentName.c_str(), templateName.c_str());
			return false;
		}

		CParamNode& parentData = m_TemplateFileData[parentName];

		// Initialise this template with its parent
		m_TemplateFileData[templateName] = parentData;
	}

	// Load the new file into the template data (overriding parent values)
	CParamNode::LoadXML(m_TemplateFileData[templateName], xero, wstring_from_utf8(templateName).c_str());

	return true;
}