mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 02:56:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			414 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			414 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "ScriptLoader.hpp"
 | 
						|
 | 
						|
#include <vector>
 | 
						|
#include <map>
 | 
						|
#include <exception>
 | 
						|
#include <fstream>
 | 
						|
 | 
						|
#include <boost/filesystem.hpp>
 | 
						|
 | 
						|
namespace sh
 | 
						|
{
 | 
						|
	void ScriptLoader::loadAllFiles(ScriptLoader* c, const std::string& path)
 | 
						|
	{
 | 
						|
		for ( boost::filesystem::recursive_directory_iterator end, dir(path); dir != end; ++dir )
 | 
						|
		{
 | 
						|
			boost::filesystem::path p(*dir);
 | 
						|
			if(p.extension() == c->mFileEnding)
 | 
						|
			{
 | 
						|
				c->mCurrentFileName = (*dir).path().string();
 | 
						|
				std::ifstream in((*dir).path().string().c_str(), std::ios::binary);
 | 
						|
				c->parseScript(in);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	ScriptLoader::ScriptLoader(const std::string& fileEnding)
 | 
						|
		: mLoadOrder(0)
 | 
						|
		, mToken(TOKEN_NewLine)
 | 
						|
		, mLastToken(TOKEN_NewLine)
 | 
						|
 | 
						|
	{
 | 
						|
		mFileEnding = fileEnding;
 | 
						|
	}
 | 
						|
 | 
						|
	ScriptLoader::~ScriptLoader()
 | 
						|
	{
 | 
						|
		clearScriptList();
 | 
						|
	}
 | 
						|
 | 
						|
	void ScriptLoader::clearScriptList()
 | 
						|
	{
 | 
						|
		std::map <std::string, ScriptNode *>::iterator i;
 | 
						|
		for (i = m_scriptList.begin(); i != m_scriptList.end(); ++i)
 | 
						|
		{
 | 
						|
			delete i->second;
 | 
						|
		}
 | 
						|
		m_scriptList.clear();
 | 
						|
	}
 | 
						|
 | 
						|
	ScriptNode *ScriptLoader::getConfigScript(const std::string &name)
 | 
						|
	{
 | 
						|
		std::map <std::string, ScriptNode*>::iterator i;
 | 
						|
 | 
						|
		std::string key = name;
 | 
						|
		i = m_scriptList.find(key);
 | 
						|
 | 
						|
		//If found..
 | 
						|
		if (i != m_scriptList.end())
 | 
						|
		{
 | 
						|
			return i->second;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			return NULL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	std::map <std::string, ScriptNode*> ScriptLoader::getAllConfigScripts ()
 | 
						|
	{
 | 
						|
		return m_scriptList;
 | 
						|
	}
 | 
						|
 | 
						|
	void ScriptLoader::parseScript(std::ifstream &stream)
 | 
						|
	{
 | 
						|
		//Get first token
 | 
						|
		_nextToken(stream);
 | 
						|
		if (mToken == TOKEN_EOF)
 | 
						|
		{
 | 
						|
			stream.close();
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		//Parse the script
 | 
						|
		_parseNodes(stream, 0);
 | 
						|
 | 
						|
		stream.close();
 | 
						|
	}
 | 
						|
 | 
						|
	void ScriptLoader::_nextToken(std::ifstream &stream)
 | 
						|
	{
 | 
						|
		//EOF token
 | 
						|
		if (!stream.good())
 | 
						|
		{
 | 
						|
			mToken = TOKEN_EOF;
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		//(Get next character)
 | 
						|
		int ch = stream.get();
 | 
						|
 | 
						|
		while ((ch == ' ' || ch == 9) && !stream.eof())
 | 
						|
		{    //Skip leading spaces / tabs
 | 
						|
			ch = stream.get();
 | 
						|
		}
 | 
						|
 | 
						|
		if (!stream.good())
 | 
						|
		{
 | 
						|
			mToken = TOKEN_EOF;
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		//Newline token
 | 
						|
		if (ch == '\r' || ch == '\n')
 | 
						|
		{
 | 
						|
			do
 | 
						|
			{
 | 
						|
				ch = stream.get();
 | 
						|
			} while ((ch == '\r' || ch == '\n') && !stream.eof());
 | 
						|
 | 
						|
			stream.unget();
 | 
						|
 | 
						|
			mToken = TOKEN_NewLine;
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		//Open brace token
 | 
						|
		else if (ch == '{')
 | 
						|
		{
 | 
						|
			mToken = TOKEN_OpenBrace;
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		//Close brace token
 | 
						|
		else if (ch == '}')
 | 
						|
		{
 | 
						|
			mToken = TOKEN_CloseBrace;
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		//Text token
 | 
						|
		if (ch < 32 || ch > 122)    //Verify valid char
 | 
						|
		{
 | 
						|
			throw std::runtime_error("Parse Error: Invalid character, ConfigLoader::load()");
 | 
						|
		}
 | 
						|
 | 
						|
		mTokenValue = "";
 | 
						|
		mToken = TOKEN_Text;
 | 
						|
		do
 | 
						|
		{
 | 
						|
			//Skip comments
 | 
						|
			if (ch == '/')
 | 
						|
			{
 | 
						|
				int ch2 = stream.peek();
 | 
						|
 | 
						|
				//C++ style comment (//)
 | 
						|
				if (ch2 == '/')
 | 
						|
				{
 | 
						|
					stream.get();
 | 
						|
					do
 | 
						|
					{
 | 
						|
						ch = stream.get();
 | 
						|
					} while (ch != '\r' && ch != '\n' && !stream.eof());
 | 
						|
 | 
						|
					mToken = TOKEN_NewLine;
 | 
						|
					return;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			//Add valid char to tokVal
 | 
						|
			mTokenValue += (char)ch;
 | 
						|
 | 
						|
			//Next char
 | 
						|
			ch = stream.get();
 | 
						|
 | 
						|
		} while (ch > 32 && ch <= 122 && !stream.eof());
 | 
						|
 | 
						|
		stream.unget();
 | 
						|
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	void ScriptLoader::_skipNewLines(std::ifstream &stream)
 | 
						|
	{
 | 
						|
		while (mToken == TOKEN_NewLine)
 | 
						|
		{
 | 
						|
			_nextToken(stream);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	void ScriptLoader::_parseNodes(std::ifstream &stream, ScriptNode *parent)
 | 
						|
	{
 | 
						|
		typedef std::pair<std::string, ScriptNode*> ScriptItem;
 | 
						|
 | 
						|
		while (true)
 | 
						|
		{
 | 
						|
			switch (mToken)
 | 
						|
			{
 | 
						|
				//Node
 | 
						|
				case TOKEN_Text:
 | 
						|
				{
 | 
						|
					//Add the new node
 | 
						|
					ScriptNode *newNode;
 | 
						|
					if (parent)
 | 
						|
					{
 | 
						|
						newNode = parent->addChild(mTokenValue);
 | 
						|
					}
 | 
						|
					else
 | 
						|
					{
 | 
						|
						newNode = new ScriptNode(0, mTokenValue);
 | 
						|
					}
 | 
						|
 | 
						|
					//Get values
 | 
						|
					_nextToken(stream);
 | 
						|
					std::string valueStr;
 | 
						|
					int i=0;
 | 
						|
					while (mToken == TOKEN_Text)
 | 
						|
					{
 | 
						|
						if (i == 0)
 | 
						|
							valueStr += mTokenValue;
 | 
						|
						else
 | 
						|
							valueStr += " " + mTokenValue;
 | 
						|
						_nextToken(stream);
 | 
						|
						++i;
 | 
						|
					}
 | 
						|
					newNode->setValue(valueStr);
 | 
						|
 | 
						|
					//Add root nodes to scriptList
 | 
						|
					if (!parent)
 | 
						|
					{
 | 
						|
						std::string key;
 | 
						|
 | 
						|
						if (newNode->getValue() == "")
 | 
						|
								throw std::runtime_error("Root node must have a name (\"" + newNode->getName() + "\")");
 | 
						|
						key = newNode->getValue();
 | 
						|
 | 
						|
						m_scriptList.insert(ScriptItem(key, newNode));
 | 
						|
					}
 | 
						|
 | 
						|
					_skipNewLines(stream);
 | 
						|
 | 
						|
					//Add any sub-nodes
 | 
						|
					if (mToken == TOKEN_OpenBrace)
 | 
						|
					{
 | 
						|
						//Parse nodes
 | 
						|
						_nextToken(stream);
 | 
						|
						_parseNodes(stream, newNode);
 | 
						|
						//Check for matching closing brace
 | 
						|
						if (mToken != TOKEN_CloseBrace)
 | 
						|
						{
 | 
						|
							throw std::runtime_error("Parse Error: Expecting closing brace");
 | 
						|
						}
 | 
						|
						_nextToken(stream);
 | 
						|
						_skipNewLines(stream);
 | 
						|
					}
 | 
						|
 | 
						|
					newNode->mFileName = mCurrentFileName;
 | 
						|
 | 
						|
					break;
 | 
						|
				}
 | 
						|
 | 
						|
				//Out of place brace
 | 
						|
				case TOKEN_OpenBrace:
 | 
						|
					throw std::runtime_error("Parse Error: Opening brace out of plane");
 | 
						|
					break;
 | 
						|
 | 
						|
				//Return if end of nodes have been reached
 | 
						|
				case TOKEN_CloseBrace:
 | 
						|
					return;
 | 
						|
 | 
						|
				//Return if reached end of file
 | 
						|
				case TOKEN_EOF:
 | 
						|
					return;
 | 
						|
 | 
						|
				case TOKEN_NewLine:
 | 
						|
					_nextToken(stream);
 | 
						|
					break;
 | 
						|
			}
 | 
						|
		};
 | 
						|
	}
 | 
						|
 | 
						|
	ScriptNode::ScriptNode(ScriptNode *parent, const std::string &name)
 | 
						|
	{
 | 
						|
		mName = name;
 | 
						|
		mParent = parent;
 | 
						|
		mRemoveSelf = true;    //For proper destruction
 | 
						|
		mLastChildFound = -1;
 | 
						|
 | 
						|
		//Add self to parent's child list (unless this is the root node being created)
 | 
						|
		if (parent != NULL)
 | 
						|
		{
 | 
						|
			mParent->mChildren.push_back(this);
 | 
						|
			mIter = --(mParent->mChildren.end());
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	ScriptNode::~ScriptNode()
 | 
						|
	{
 | 
						|
		//Delete all children
 | 
						|
		std::vector<ScriptNode*>::iterator i;
 | 
						|
		for (i = mChildren.begin(); i != mChildren.end(); ++i)
 | 
						|
		{
 | 
						|
			ScriptNode *node = *i;
 | 
						|
			node->mRemoveSelf = false;
 | 
						|
			delete node;
 | 
						|
		}
 | 
						|
		mChildren.clear();
 | 
						|
 | 
						|
		//Remove self from parent's child list
 | 
						|
		if (mRemoveSelf && mParent != NULL)
 | 
						|
		{
 | 
						|
			mParent->mChildren.erase(mIter);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	ScriptNode *ScriptNode::addChild(const std::string &name, bool replaceExisting)
 | 
						|
	{
 | 
						|
		if (replaceExisting)
 | 
						|
		{
 | 
						|
			ScriptNode *node = findChild(name, false);
 | 
						|
			if (node)
 | 
						|
			{
 | 
						|
				return node;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return new ScriptNode(this, name);
 | 
						|
	}
 | 
						|
 | 
						|
	ScriptNode *ScriptNode::findChild(const std::string &name, bool recursive)
 | 
						|
	{
 | 
						|
		int indx;
 | 
						|
		int childCount = (int)mChildren.size();
 | 
						|
 | 
						|
		if (mLastChildFound != -1)
 | 
						|
		{
 | 
						|
			//If possible, try checking the nodes neighboring the last successful search
 | 
						|
			//(often nodes searched for in sequence, so this will boost search speeds).
 | 
						|
			int prevC = mLastChildFound-1;
 | 
						|
			if (prevC < 0)
 | 
						|
				prevC = 0;
 | 
						|
			else if (prevC >= childCount)
 | 
						|
				prevC = childCount-1;
 | 
						|
			int nextC = mLastChildFound+1;
 | 
						|
			if (nextC < 0)
 | 
						|
				nextC = 0;
 | 
						|
			else if (nextC >= childCount)
 | 
						|
				nextC = childCount-1;
 | 
						|
 | 
						|
			for (indx = prevC; indx <= nextC; ++indx)
 | 
						|
			{
 | 
						|
				ScriptNode *node = mChildren[indx];
 | 
						|
				if (node->mName == name)
 | 
						|
				{
 | 
						|
					mLastChildFound = indx;
 | 
						|
					return node;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			//If not found that way, search for the node from start to finish, avoiding the
 | 
						|
			//already searched area above.
 | 
						|
			for (indx = nextC + 1; indx < childCount; ++indx)
 | 
						|
			{
 | 
						|
				ScriptNode *node = mChildren[indx];
 | 
						|
				if (node->mName == name) {
 | 
						|
					mLastChildFound = indx;
 | 
						|
					return node;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			for (indx = 0; indx < prevC; ++indx)
 | 
						|
			{
 | 
						|
				ScriptNode *node = mChildren[indx];
 | 
						|
				if (node->mName == name) {
 | 
						|
					mLastChildFound = indx;
 | 
						|
					return node;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			//Search for the node from start to finish
 | 
						|
			for (indx = 0; indx < childCount; ++indx){
 | 
						|
				ScriptNode *node = mChildren[indx];
 | 
						|
				if (node->mName == name) {
 | 
						|
					mLastChildFound = indx;
 | 
						|
					return node;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		//If not found, search child nodes (if recursive == true)
 | 
						|
		if (recursive)
 | 
						|
		{
 | 
						|
			for (indx = 0; indx < childCount; ++indx)
 | 
						|
			{
 | 
						|
				mChildren[indx]->findChild(name, recursive);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		//Not found anywhere
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	void ScriptNode::setParent(ScriptNode *newParent)
 | 
						|
	{
 | 
						|
		//Remove self from current parent
 | 
						|
		mParent->mChildren.erase(mIter);
 | 
						|
 | 
						|
		//Set new parent
 | 
						|
		mParent = newParent;
 | 
						|
 | 
						|
		//Add self to new parent
 | 
						|
		mParent->mChildren.push_back(this);
 | 
						|
		mIter = --(mParent->mChildren.end());
 | 
						|
	}
 | 
						|
}
 |