Example #1
0
void
ProcessArea(
	__in const NWN::ResRef32 & AreaResRef,
	__in ResourceManager & ResMan,
	__in IDebugTextOut * TextOut,
	__in unsigned long ObjectTypeMask,
	__in const StringVec & TemplateNames,
	__in const StringVec & ExcludeFields,
    __in const StringVec & IncludeFields
	)
/*++

Routine Description:

	This routine updates placed instances within a given area with data from
	their templates.

Arguments:

	AreaResRef - Supplies the resource name of the area to display.

	ResMan - Supplies a reference to the resource manager instance to use in
	         order to load any associated resource data.

	TextOut - Supplies the text output interface.

	ObjectTypeMask - Supplies the mask of object types to update templates for.

	TemplateNames - Supplies the RESREF names of templates that are to be
	                updated.

	ExcludeFields - Supplies a list of fields that are to be excluded from
	                copying even if they are present in the template.

Return Value:

	None.  An std::exception is raised on failure.

Environment:

	User mode.

--*/
{
	//
	// Areas are comprised of two files, an <area>.are with area parameters,
	// and an <area>.git with the object instance parameters about objects that
	// have been placed in the area via the toolset.
	//

	DemandResource32                 AreFile( ResMan, AreaResRef, NWN::ResARE );
	DemandResource32                 GitFile( ResMan, AreaResRef, NWN::ResGIT );
	GffFileReader                    Are( AreFile, ResMan );
	GffFileReader::Ptr               Git = new GffFileReader( GitFile, ResMan );
	GffFileWriter                    GitWriter;
	const GffFileReader::GffStruct * RootStruct;
	GffFileWriter::GffStruct         GitWriterRoot;
	std::string                      AreaName;
	std::string                      AreaTag;

	//
	// Start off by duplicating the current GIT contents over to the new output
	// GIT.
	//

	RootStruct = Are.GetRootStruct( );
	GitWriter.InitializeFromReader( Git.get( ) );

	//
	// Acquire parameters we need from area.git.
	//

	if (!RootStruct->GetCExoLocString( "Name", AreaName ))
		throw std::runtime_error( "Failed to read area Name" );
	if (!RootStruct->GetCExoString( "Tag", AreaTag ))
		throw std::runtime_error( "Failed to read area Tag" );
	
	TextOut->WriteText(
		"Updating instance information for area %s (tag %s)...\n", 
		AreaName.c_str( ),
		AreaTag.c_str( ));

	//
	// Now update each of the instance data items that we are interested in.
	//

	RootStruct    = Git->GetRootStruct( );
	GitWriterRoot = GitWriter.GetRootStruct( );

	for (size_t i = 0; i < NumValidObjectTypes; i += 1)
	{
		if (ObjectTypeMask > 0 && !(ObjectTypeMask & (1 << ValidObjectTypes[ i ].TypeCode )))
			continue;

		//
		// This is an object type we're interested in, scan for objects that have
		// a template we're to refresh and copy the data.
		//

		for (size_t j = 0; j <= ULONG_MAX; j += 1)
		{
			GffFileReader::GffStruct ObjStructIn;
			GffFileWriter::GffStruct ObjStructOut;
			NWN::ResRef32            TemplateResRef;
			std::string              TemplateString;
			bool                     MatchingTemplate;
			std::string              FileName;
			GffFileReader::Ptr       TemplateReader;

			//
			// Fetch the corresponding list element in both the input and output
			// GITs so that we can make modifications as necessary.
			//

			if (!RootStruct->GetListElement( ValidObjectTypes[ i ].InstanceListName, j, ObjStructIn ))
				break;

			if (!GitWriterRoot.GetListElement( ValidObjectTypes[ i ].InstanceListName, j, ObjStructOut ))
				throw std::runtime_error( "Internal error: GFF reader/writer out of sync." );

			//
			// If the object instance had no associated template, there's nothing
			// for us to update, so skip it.
			//

			if (!ObjStructIn.GetResRef( "TemplateResRef", TemplateResRef ))
				continue;

			TemplateString   = ResMan.StrFromResRef( TemplateResRef );
			MatchingTemplate = TemplateNames.empty(); // if empty then ignore filter

			for (StringVec::const_iterator it = TemplateNames.begin( );
			     it != TemplateNames.end( );
			     ++it)
			{
				if (!_stricmp( it->c_str( ), TemplateString.c_str( ) ))
				{
					MatchingTemplate = true;
					break;
				}
			}

			if (!MatchingTemplate)
				continue;

			//
			// This instance appears to be one that we should update, try and
			// process it.
			//

			TextOut->WriteText(
				"Refreshing template data for object #%lu of type %s (template %s.%s)...\n",
				(unsigned long) j,
				ValidObjectTypes[ i ].TypeName,
				TemplateString.c_str( ),
				ResMan.ResTypeToExt( ValidObjectTypes[ i ].TemplateResType ));

			//
			// Note that we must be careful here, as not only may objects have bad
			// template RESREFs, they may also have RESREFs to files that are not
			// even legal GFF-based templates to begin with! (e.g. fireplace.upe).
			//

			try
			{
				FileName = ResMan.Demand(
					TemplateResRef,
					ValidObjectTypes[ i ].TemplateResType);
			}
			catch (std::exception &e)
			{
				TextOut->WriteText(
					"WARNING:  Exception '%s' locating template %s.%s, skipping object instance...\n",
					e.what( ),
					TemplateString.c_str( ),
					ResMan.ResTypeToExt( ValidObjectTypes[ i ].TemplateResType ));

				continue;
			}

			try
			{
				TemplateReader = new GffFileReader(
					FileName,
					ResMan);
			}
			catch (std::exception &e)
			{
				TextOut->WriteText(
					"WARNING:  Exception '%s' loading template %s.%s, skipping object instance...\n",
					e.what( ),
					TemplateString.c_str( ),
					ResMan.ResTypeToExt( ValidObjectTypes[ i ].TemplateResType ));
				ResMan.Release( FileName );

				continue;
			}

			//
			// Finally, update the instance data.
			//

			try
			{
				UpdateObjectInstanceFromTemplate(
					TemplateReader->GetRootStruct( ),
					&ObjStructIn,
					&ObjStructOut,
					ExcludeFields,
                    IncludeFields,
					TextOut);
			}
			catch (std::exception &e)
			{
				TextOut->WriteText(
					"WARNING:  Exception '%s' refreshing object instance from template %s.%s, skipping object instance...\n",
					e.what( ),
					TemplateString.c_str( ),
					ResMan.ResTypeToExt( ValidObjectTypes[ i ].TemplateResType ));
			}

			TemplateReader = NULL;
			ResMan.Release( FileName );
		}
	}

	//
	// Now replace the object instance GFF with our edited version.
	//

	Git = NULL;

	GitWriter.Commit(
		GitFile,
		GffFileWriter::GIT_FILE_TYPE,
		GffFileWriter::GFF_COMMIT_FLAG_SEQUENTIAL);
}
/*static*/ bool StructureParser::matchLine (char finishing) {
	// Case 1. Namespace definition
	if (mIncomingLine.size() == 2 && mIncomingLine[0] == "namespace" && finishing == '{'){
		NamespaceElement * element = new NamespaceElement();
		element->name = mIncomingLine[1];
		push (element);
		return true;
	}
	// Case 2. Enum definition
	if (mIncomingLine.size() == 2 && mIncomingLine[0] == "enum" && finishing == '{'){
		EnumElement * element = new EnumElement;
		element->name = mIncomingLine[1];
		push (element);
		return true;
	}
	// Case 3. Simple Structure/Class Definition
	if (mIncomingLine.size() == 2 && (mIncomingLine[0] == "struct" || mIncomingLine[0] == "class") && finishing == '{'){
		ClassElement * element = new ClassElement;
		element->name = mIncomingLine[1];
		element->isStruct = (mIncomingLine[0] == "struct");
		element->currentVisibility = element->isStruct ? Public : Private;
		push (element);
		return true;
	}
	// Case 4. Structure/Class derived from some other type
	if (mIncomingLine.size() > 3 && (mIncomingLine[0] == "struct" || mIncomingLine[0] == "class") && mIncomingLine[2] == ":"){
		ClassElement * element = new ClassElement;
		element->name = mIncomingLine[1];
		element->isStruct = (mIncomingLine[0] == "struct");
		element->currentVisibility = element->isStruct ? Public : Private;
		StringVec::const_iterator i = mIncomingLine.begin() + 3;
		while (i != mIncomingLine.end()){
			Visibility v = element->currentVisibility;
			if (*i == "public")   { v = Public; i++; }
			if (*i == "private")  { v = Private; i++; }
			if (*i == "protected"){ v = Protected; i++;}
			bool suc;
			StringVec::const_iterator end = matchTypeName (i, mIncomingLine.end(), &suc);
			if (!suc) {
				fprintf (stderr, "Error: could not match a typename\n");
				delete element;
				return false;
			}
			ClassElement::Parent p;
			p.first  = v;
			p.second = formatType (i,end);
			element->parents.push_back (p);
			i = end;
			if (i == mIncomingLine.end()) break;
			if (*i == ",") { i++; continue; }
			fprintf (stderr, "Error: Invalid character: %s\n", i->c_str());
			delete element;
			return false;
		}
		push (element);
		return true;
	}
	// Case 5. Member Variable
	if (finishing == ';'){
		VariableDefinition definition;
		bool result = matchVariableDefinition (mIncomingLine.begin(), mIncomingLine.end(), &definition);
		if (result) {
			if (mDebug) printf ("Matched variable definition: %s\n", sf::toJSON(definition).c_str());
			StackElement * element = mStack.top();
			if (element->type == StackElement::Class){
				ClassElement * celement = static_cast<ClassElement*> (element);
				celement->memberVariables.push_back (std::make_pair(celement->currentVisibility,definition));
				return true;
			}
		}
	}
	// Case 6a: Handling of SF_AUTOREFLECT_COMMAND;
	if (finishing == ';'){
		size_t size =  mIncomingLine.size();
		if (size >= 1){
			const std::string & x (mIncomingLine.back());
			size_t prefix = beginsWidth (x, mCommandPrefix);
			if (prefix != x.npos){
				std::string cmd = x.substr (prefix, x.npos);
				assert (!mStack.empty());
				StackElement * element = mStack.top ();
				element->commands.insert (cmd);
				mIncomingLine.pop_back ();
				return true;
			}
		}
	}
	// Case 6b: handling of SF_AUTOREFLECT_COMMAND(element-name)
	if (finishing == ';'){
		size_t size = mIncomingLine.size();
		if (size >= 4){
			const std::string & x (mIncomingLine.front());
			size_t prefix = beginsWidth (x, mCommandPrefix);
			if (prefix != x.npos && mIncomingLine[1] == "(" && mIncomingLine[size-1] == ")"){
				std::string cmd = x.substr (prefix, x.npos);
				StringVec::iterator beginType = mIncomingLine.begin() + 2;
				StringVec::iterator endType   = mIncomingLine.end() - 1;
				bool suc;
				StringVec::const_iterator foundEnd = matchTypeName (beginType, endType, &suc);
				if (!suc) {
					fprintf (stderr, "Could not match a type name in %s command", cmd.c_str());
					return false;
				}
				std::string elementName = formatType (beginType, foundEnd);
				StackElement * parent = mStack.top ();
				StackElement * child = parent->findChild (elementName);
				if (child){
					child->commands.insert (cmd);
					mIncomingLine.erase(mIncomingLine.end() - 4, mIncomingLine.end());
					return true;
				} else {
					fprintf (stderr, "Did not found a element with name %s as child of element of name %s, for command %s\n", elementName.c_str(), sf::toJSON(parent).c_str(), cmd.c_str());
					return false;
				}

			}
		}
	}
	// Case 7 Function declaration
	if (finishing == ';' || finishing == '{') {
		FunctionDeclarationElement * declaration = new FunctionDeclarationElement();
		bool result = matchFunctionDeclaration (mIncomingLine.begin(), mIncomingLine.end(), declaration);
		if (result) {
			if (mDebug) printf ("Matched function declaration %s\n", sf::toJSON (declaration).c_str());
			StackElement * element = mStack.top();
			if (element->type == StackElement::Class)
				declaration->visibility = (static_cast<ClassElement*> (element))->currentVisibility;

			if (
					(element->type == StackElement::Class)
				 || (element->type == StackElement::Root)
				 || (element->type == StackElement::Namespace)){
				element->children.push_back (declaration);
			} else {
				// May happen. Regular functions are similar to be parsed like functions
				// E.g. int bla () { X x ();} // must not declare x but can also declare an instance of (struct/class) X, called x.
				// fprintf (stderr, "Matched a function declaration %s outside a class/root/namespace element, parent=%s\n", sf::toJSON (declaration).c_str(), sf::toJSON (*element).c_str());
				delete declaration;
				if (finishing != '{') // otherwise there is still a block
					return true;
			}
		}
		if (finishing != '{') // otherwise there is still a block
			return true;
	}

	// Unknown block, starting
	if (finishing == '{'){
		if (mDebug){
			printf ("Unknown block start: ");
			for (StringVec::const_iterator i = mIncomingLine.begin(); i != mIncomingLine.end(); i++){
				printf ("%s ", i->c_str());
			}
			printf ("<stop>\n");
		}

		UnknownBlock * element = new UnknownBlock ();
		push (element);
		return true;
	}
	// End of a block, ending
	if (finishing == '}'){
		bool ret = pop ();
		return ret;
	}
	// Command, ending
	if (finishing == ';'){
		if (mDebug){
			printf ("Unknown command: ");
			for (StringVec::const_iterator i = mIncomingLine.begin(); i != mIncomingLine.end(); i++){
				printf ("%s ", i->c_str());
			}
			printf ("<stop>\n");
		}
		return true;
	}
	return true;
}