Click here to Skip to main content
15,885,953 members
Articles / Mobile Apps / Android

Texture Atlas Maker

Rate me:
Please Sign up or sign in to vote.
4.94/5 (16 votes)
1 Apr 2012BSD6 min read 142.2K   7K   55  
A utility to create texture atlases for 2D OpenGL games
#include "PreCompile.h"
#include "XParser.h"

void XParser::Open(const string& fileName)
{
	FILE* pFile;
	pFile = fopen(fileName.c_str(), "rb");
	if (pFile == NULL)
		throw RacException("cannot open ", fileName);
	Open(pFile);
}

void XParser::Open(FILE* pFile)
{
	m_currLine = 0;
	fseek(pFile, 0, 2 /* SEEK_END */);
	m_fileSize = (int)ftell(pFile);
	m_currPos = 0;
	m_mapAttrib.clear();
	if (m_fileSize == 0) return;
	fseek(pFile, 0, 0 /* SEEK_SET */);
	m_buffer.Init(m_fileSize);
	int bytesRead = (int)fread(m_buffer.GetCache(), m_fileSize, 1, pFile);	// read length and type
	fclose(pFile);
	if (bytesRead != 1)
		throw RacException("error reading file");
	m_currChar = m_buffer.Get(m_currPos);
	m_mapElement.clear();
}

void XParser::ReadNextElement()
{
	if (m_fileSize == 0) return;
	m_mapAttrib.clear();
	EatWhiteSpace();
	if (EndFileReached() || m_currChar != '<')
		throw RacException("fucked up xml element after ", m_elementName);
	CheckComment();
	m_currPos++;
	m_currChar = m_buffer.Get(m_currPos);
	if (m_currChar == '/')
	{
		m_currPos++;
		m_currChar = m_buffer.Get(m_currPos);
		GetNextWord(m_elementName);
		fmap<string, int>::iterator iter;
		iter = m_mapElement.find(m_elementName);
		if (iter == m_mapElement.end())
		{
			throw RacException("end tag without any opening tag:", m_elementName);
		}
		else
		{
			iter->second--;
			if (iter->second < 0)
				throw RacException("end tag without any start:", m_elementName);
		}
		if (m_currChar != '>' || EndFileReached())
			throw RacException("fucked up end tag:", m_elementName);
		m_currPos++;
		m_currChar = m_buffer.Get(m_currPos);
		m_elementName.clear();
		return;
	}
	GetNextWord(m_elementName);
	if (EndFileReached() || m_elementName.empty())
		throw RacException("fucked up xml element");
	fmap<string, int>::iterator iter;
	iter = m_mapElement.find(m_elementName);
	if (iter == m_mapElement.end())
	{
		m_mapElement[m_elementName] = 1;
	}
	else
	{
		iter->second++;
	}
	string attribName, attribValue;
	while(m_currChar != '>')
	{
		GetNextWord(attribName);
		if (m_currChar != '=' || attribName.empty() || EndFileReached())
			throw RacException("fucked up attribute in ", m_elementName);
		m_currPos++;
		m_currChar = m_buffer.Get(m_currPos);
		GetNextValue(attribValue);
		if (EndFileReached())
			throw RacException("missing end to element ", m_elementName);
		m_mapAttrib[attribName] = attribValue;
		if (m_currChar == '/')
		{
			iter = m_mapElement.find(m_elementName);

			if (iter == m_mapElement.end())
			{
				throw RacException("end tag without any opening tag:", m_elementName);
			}
			else
			{
				iter->second--;
			}
			m_currPos++;
			m_currChar = m_buffer.Get(m_currPos);
		}
	}
	m_currPos++;
	m_currChar = m_buffer.Get(m_currPos);
}

// gets the next word delimited by whitespace
// if the word has format "keyx=valuex" then it will return the key and value
// seperated, otherwise value will be empty string
// returns false if there is a file read error
void XParser::GetNextWord(string& token) 
{
	int startPos = m_currPos;
	while (m_currChar != '\n' && m_currChar != '\r' && m_currChar != ' ' && 
		m_currChar != '\t' && m_currChar != '>' && m_currChar != '=' && 
		m_currPos < m_fileSize && m_currChar != '\"' && m_currChar != '/')
	{
		m_currPos++;
		m_currChar = m_buffer.Get(m_currPos);
		if (m_currChar == '\n') m_currLine++; 
	}	// end while
	if (m_currPos == m_fileSize)
		token.assign(m_buffer.GetPtr(startPos), m_currPos-startPos);
	else
		token.assign(m_buffer.GetPtr(startPos), m_currPos-startPos);
	EatWhiteSpace(); 
}	//	getNextStr

void XParser::GetNextValue(string& token) 
{
	EatWhiteSpace();
	if (EndFileReached())
		throw RacException("fucked up attribute value in ", m_elementName);
	int startPos;
	if (m_currChar == '\"' )
	{
		m_currPos++;
		m_currChar = m_buffer.Get(m_currPos);
		startPos = m_currPos;
		while (m_currChar != '\"' && m_currPos < m_fileSize)
		{
			m_currPos++;
			m_currChar = m_buffer.Get(m_currPos);
		}	// end while
		if (m_currPos == m_fileSize)
			throw RacException("fucked up attribute value in ", m_elementName);
		token.assign(m_buffer.GetPtr(startPos), m_currPos-startPos);
		m_currPos++;
		m_currChar = m_buffer.Get(m_currPos);
	}
	else	// value is not enclosed in quotes
	{
		startPos = m_currPos;
		while (m_currChar != '\n' && m_currChar != '\r' && m_currChar != ' ' && 
			m_currChar != '\t' && m_currChar != '>' && m_currChar != '=' && 
			m_currPos < m_fileSize && m_currChar != '\"' && m_currChar != '/')
		{
			m_currPos++;
			m_currChar = m_buffer.Get(m_currPos);
		}	// end while
		token.assign(m_buffer.GetPtr(startPos), m_currPos-startPos);
	}
	EatWhiteSpace(); 
}	

void XParser::EatWhiteSpace() 
{
	// leading whitespace is ignored
	while ((m_currChar == '\n' || m_currChar == '\r' || m_currChar == ' ' 
		|| m_currChar == '\t') && !EndFileReached())
	{
		m_currPos++;
		m_currChar = m_buffer.Get(m_currPos);
		if (m_currChar == '\n') m_currLine++; 
	} 
}

void XParser::CheckComment() 
{
	if (m_buffer.Get(m_currPos+1) != '!' 
		|| m_buffer.Get(m_currPos+2) != '-' || m_buffer.Get(m_currPos+3) != '-')
		return;
	m_currPos += 4;
	// leading whitespace is ignored
	while (m_buffer.Get(m_currPos) != '-' || m_buffer.Get(m_currPos+1) != '-'
		|| m_buffer.Get(m_currPos+2) != '>')
	{
		m_currPos++;
		if (EndFileReached())
			throw RacException("file ended in middle of comment ");
	} 
	m_currPos += 3;
	m_currChar = m_buffer.Get(m_currPos);
	EatWhiteSpace();
	if (m_currChar != '<')
		throw RacException("fucked up xml element");
}


const string& XParser::GetValue(const char* szKey) const
{
	//string key(szKey);
	//to_lower(key);
	fmap<string, string>::const_iterator iter;
	iter = m_mapAttrib.find(szKey);
	if (iter == m_mapAttrib.end())
	{
		string strErr = "missing key ";
		strErr += szKey;
		strErr += " in ";
		strErr += m_elementName;
		throw RacException(strErr);
	}
	return iter->second;
}


bool XParser::GetOptionalValue(const char* szKey, string& value) const
{
	//string key(szKey);
	//to_lower(key);
	fmap<string, string>::const_iterator iter;
	iter = m_mapAttrib.find(szKey);
	if (iter == m_mapAttrib.end())
	{
		return false;
	}
	value = iter->second;
	return true;
}

int XParser::GetOptionalInt(const char* szKey, int defaultValue) const
{
	//string key(szKey);
	//to_lower(key);
	fmap<string, string>::const_iterator iter;
	iter = m_mapAttrib.find(szKey);
	if (iter == m_mapAttrib.end())
	{
		return defaultValue;
	}
	return atoi(iter->second.c_str());
}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Software Developer Astronautz
Spain Spain
After working in the software industry for many years, I've started my own games company that specialises in strategy games for mobile platforms.

Comments and Discussions