git-svn-id: http://mc-server.googlecode.com/svn/trunk@484 0a769ca7-a7f5-676a-18bf-c427514a06d6
		
			
				
	
	
		
			531 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			531 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
// FastNBT.cpp
 | 
						|
 | 
						|
// Implements the fast NBT parser and writer
 | 
						|
 | 
						|
#include "Globals.h"
 | 
						|
#include "FastNBT.h"
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#define RETURN_FALSE_IF_FALSE(X) do { if (!X) return false; } while (0)
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
						|
// cParsedNBT:
 | 
						|
 | 
						|
#define NEEDBYTES(N) \
 | 
						|
	if (m_Length - m_Pos < N) \
 | 
						|
	{ \
 | 
						|
		return false; \
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
cParsedNBT::cParsedNBT(const char * a_Data, int a_Length) :
 | 
						|
	m_Data(a_Data),
 | 
						|
	m_Length(a_Length),
 | 
						|
	m_Pos(0)
 | 
						|
{
 | 
						|
	m_IsValid = Parse();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
bool cParsedNBT::Parse(void)
 | 
						|
{
 | 
						|
	if (m_Length < 3)
 | 
						|
	{
 | 
						|
		// Data too short
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	if (m_Data[0] != TAG_Compound)
 | 
						|
	{
 | 
						|
		// The top-level tag must be a Compound
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	
 | 
						|
	m_Tags.reserve(200);
 | 
						|
	
 | 
						|
	m_Tags.push_back(cFastNBTTag(TAG_Compound, -1));
 | 
						|
	
 | 
						|
	m_Pos = 1;
 | 
						|
	
 | 
						|
	RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength)); 
 | 
						|
	RETURN_FALSE_IF_FALSE(ReadCompound());
 | 
						|
	
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
bool cParsedNBT::ReadString(int & a_StringStart, int & a_StringLen)
 | 
						|
{
 | 
						|
	NEEDBYTES(2);
 | 
						|
	a_StringStart = m_Pos + 2;
 | 
						|
	a_StringLen = ntohs(*((short *)(m_Data + m_Pos)));
 | 
						|
	if (a_StringLen < 0)
 | 
						|
	{
 | 
						|
		// Invalid string length
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	m_Pos += 2 + a_StringLen;
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
bool cParsedNBT::ReadCompound(void)
 | 
						|
{
 | 
						|
	// Reads the latest tag as a compound
 | 
						|
	int ParentIdx = m_Tags.size() - 1;
 | 
						|
	int PrevSibling = -1;
 | 
						|
	while (true)
 | 
						|
	{
 | 
						|
		NEEDBYTES(1);
 | 
						|
		eTagType TagType = (eTagType)(m_Data[m_Pos]);
 | 
						|
		m_Pos++;
 | 
						|
		if (TagType == TAG_End)
 | 
						|
		{
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		m_Tags.push_back(cFastNBTTag(TagType, ParentIdx, PrevSibling));
 | 
						|
		if (PrevSibling >= 0)
 | 
						|
		{
 | 
						|
			m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1;
 | 
						|
		}
 | 
						|
		PrevSibling = m_Tags.size() - 1;
 | 
						|
		RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength));
 | 
						|
		RETURN_FALSE_IF_FALSE(ReadTag());
 | 
						|
	}  // while (true)
 | 
						|
	m_Tags[ParentIdx].m_LastChild = PrevSibling;
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
bool cParsedNBT::ReadList(eTagType a_ChildrenType)
 | 
						|
{
 | 
						|
	// Reads the latest tag as a list of items of type a_ChildrenType
 | 
						|
	
 | 
						|
	// Read the count:
 | 
						|
	NEEDBYTES(4);
 | 
						|
	int Count = ntohl(*((int *)(m_Data + m_Pos)));
 | 
						|
	m_Pos += 4;
 | 
						|
	if (Count < 0)
 | 
						|
	{
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	// Read items:
 | 
						|
	int ParentIdx = m_Tags.size() - 1;
 | 
						|
	int PrevSibling = -1;
 | 
						|
	for (int i = 0; i < Count; i++)
 | 
						|
	{
 | 
						|
		m_Tags.push_back(cFastNBTTag(a_ChildrenType, ParentIdx, PrevSibling));
 | 
						|
		if (PrevSibling >= 0)
 | 
						|
		{
 | 
						|
			m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1;
 | 
						|
		}
 | 
						|
		PrevSibling = m_Tags.size() - 1;
 | 
						|
		RETURN_FALSE_IF_FALSE(ReadTag());
 | 
						|
	}  // for (i)
 | 
						|
	m_Tags[ParentIdx].m_LastChild = PrevSibling;
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#define CASE_SIMPLE_TAG(TAGTYPE, LEN) \
 | 
						|
	case TAG_##TAGTYPE: \
 | 
						|
	{ \
 | 
						|
		NEEDBYTES(LEN); \
 | 
						|
		Tag.m_DataStart = m_Pos; \
 | 
						|
		Tag.m_DataLength = LEN; \
 | 
						|
		m_Pos += LEN; \
 | 
						|
		return true; \
 | 
						|
	}
 | 
						|
	
 | 
						|
bool cParsedNBT::ReadTag(void)
 | 
						|
{
 | 
						|
	cFastNBTTag & Tag = m_Tags.back();
 | 
						|
	switch (Tag.m_Type)
 | 
						|
	{
 | 
						|
		CASE_SIMPLE_TAG(Byte,   1)
 | 
						|
		CASE_SIMPLE_TAG(Short,  2)
 | 
						|
		CASE_SIMPLE_TAG(Int,    4)
 | 
						|
		CASE_SIMPLE_TAG(Long,   8)
 | 
						|
		CASE_SIMPLE_TAG(Float,  4)
 | 
						|
		CASE_SIMPLE_TAG(Double, 8)
 | 
						|
		
 | 
						|
		case TAG_String:
 | 
						|
		{
 | 
						|
			return ReadString(Tag.m_DataStart, Tag.m_DataLength);
 | 
						|
		}
 | 
						|
		
 | 
						|
		case TAG_ByteArray:
 | 
						|
		{
 | 
						|
			NEEDBYTES(4);
 | 
						|
			int len = ntohl(*((int *)(m_Data + m_Pos)));
 | 
						|
			m_Pos += 4;
 | 
						|
			if (len < 0)
 | 
						|
			{
 | 
						|
				// Invalid length
 | 
						|
				return false;
 | 
						|
			}
 | 
						|
			NEEDBYTES(len);
 | 
						|
			Tag.m_DataLength = len;
 | 
						|
			Tag.m_DataStart = m_Pos;
 | 
						|
			m_Pos += len;
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
		
 | 
						|
		case TAG_List:
 | 
						|
		{
 | 
						|
			NEEDBYTES(1);
 | 
						|
			eTagType ItemType = (eTagType)m_Data[m_Pos];
 | 
						|
			m_Pos++;
 | 
						|
			RETURN_FALSE_IF_FALSE(ReadList(ItemType));
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
		
 | 
						|
		case TAG_Compound:
 | 
						|
		{
 | 
						|
			RETURN_FALSE_IF_FALSE(ReadCompound());
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
		
 | 
						|
		case TAG_IntArray:
 | 
						|
		{
 | 
						|
			NEEDBYTES(4);
 | 
						|
			int len = ntohl(*((int *)(m_Data + m_Pos)));
 | 
						|
			m_Pos += 4;
 | 
						|
			if (len < 0)
 | 
						|
			{
 | 
						|
				// Invalid length
 | 
						|
				return false;
 | 
						|
			}
 | 
						|
			len *= 4;
 | 
						|
			NEEDBYTES(len);
 | 
						|
			Tag.m_DataLength = len;
 | 
						|
			Tag.m_DataStart = m_Pos;
 | 
						|
			m_Pos += len;
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
		
 | 
						|
		default:
 | 
						|
		{
 | 
						|
			ASSERT(!"Unhandled NBT tag type");
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}  // switch (iType)
 | 
						|
}
 | 
						|
 | 
						|
#undef CASE_SIMPLE_TAG
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength) const
 | 
						|
{
 | 
						|
	if (a_Tag < 0)
 | 
						|
	{
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	if (m_Tags[a_Tag].m_Type != TAG_Compound)
 | 
						|
	{
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	
 | 
						|
	if (a_NameLength == 0)
 | 
						|
	{
 | 
						|
		a_NameLength = strlen(a_Name);
 | 
						|
	}
 | 
						|
	for (int Child = m_Tags[a_Tag].m_FirstChild; Child != -1; Child = m_Tags[Child].m_NextSibling)
 | 
						|
	{
 | 
						|
		if (
 | 
						|
			(m_Tags[Child].m_NameLength == a_NameLength) &&
 | 
						|
			(memcmp(m_Data + m_Tags[Child].m_NameStart, a_Name, a_NameLength) == 0)
 | 
						|
		)
 | 
						|
		{
 | 
						|
			return Child;
 | 
						|
		}
 | 
						|
	}  // for Child - children of a_Tag
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
int cParsedNBT::FindTagByPath(int a_Tag, const AString & a_Path) const
 | 
						|
{
 | 
						|
	if (a_Tag < 0)
 | 
						|
	{
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	size_t Begin = 0;
 | 
						|
	size_t Length = a_Path.length();
 | 
						|
	int Tag = a_Tag;
 | 
						|
	for (size_t i = 0; i < Length; i++)
 | 
						|
	{
 | 
						|
		if (a_Path[i] != '\\')
 | 
						|
		{
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		Tag = FindChildByName(Tag, a_Path.c_str() + Begin, i - Begin - 1);
 | 
						|
		if (Tag < 0)
 | 
						|
		{
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
		Begin = i + 1;
 | 
						|
	}  // for i - a_Path[]
 | 
						|
	
 | 
						|
	if (Begin < Length)
 | 
						|
	{
 | 
						|
		Tag = FindChildByName(Tag, a_Path.c_str() + Begin, Length - Begin);
 | 
						|
	}
 | 
						|
	return Tag;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
						|
// cFastNBTWriter:
 | 
						|
 | 
						|
cFastNBTWriter::cFastNBTWriter(void) :
 | 
						|
	m_CurrentStack(0)
 | 
						|
{
 | 
						|
	m_Stack[0].m_Type = TAG_Compound;
 | 
						|
	m_Result.reserve(100 * 1024);
 | 
						|
	m_Result.push_back(TAG_Compound);
 | 
						|
	WriteString("", 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::BeginCompound(const AString & a_Name)
 | 
						|
{
 | 
						|
	if (m_CurrentStack >= MAX_STACK)
 | 
						|
	{
 | 
						|
		ASSERT(!"Stack overflow");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	
 | 
						|
	TagCommon(a_Name, TAG_Compound);
 | 
						|
	
 | 
						|
	++m_CurrentStack;
 | 
						|
	m_Stack[m_CurrentStack].m_Type = TAG_Compound;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::EndCompound(void)
 | 
						|
{
 | 
						|
	ASSERT(m_CurrentStack > 0);
 | 
						|
	ASSERT(IsStackTopCompound());
 | 
						|
	
 | 
						|
	m_Result.push_back(TAG_End);
 | 
						|
	--m_CurrentStack;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::BeginList(const AString & a_Name, eTagType a_ChildrenType)
 | 
						|
{
 | 
						|
	if (m_CurrentStack >= MAX_STACK)
 | 
						|
	{
 | 
						|
		ASSERT(!"Stack overflow");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	
 | 
						|
	TagCommon(a_Name, TAG_List);
 | 
						|
		
 | 
						|
	m_Result.push_back((char)a_ChildrenType);
 | 
						|
	m_Result.append(4, (char)0);
 | 
						|
	
 | 
						|
	++m_CurrentStack;
 | 
						|
	m_Stack[m_CurrentStack].m_Type  = TAG_List;
 | 
						|
	m_Stack[m_CurrentStack].m_Pos   = m_Result.size() - 4;
 | 
						|
	m_Stack[m_CurrentStack].m_Count = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::EndList(void)
 | 
						|
{
 | 
						|
	ASSERT(m_CurrentStack > 0);
 | 
						|
	ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List);
 | 
						|
	
 | 
						|
	// Update the list count:
 | 
						|
	*((int *)(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos)) = htonl(m_Stack[m_CurrentStack].m_Count);
 | 
						|
 | 
						|
	--m_CurrentStack;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value)
 | 
						|
{
 | 
						|
	TagCommon(a_Name, TAG_Byte);
 | 
						|
	m_Result.push_back(a_Value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value)
 | 
						|
{
 | 
						|
	TagCommon(a_Name, TAG_Short);
 | 
						|
	Int16 Value = htons(a_Value);
 | 
						|
	m_Result.append((const char *)&Value, 2);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value)
 | 
						|
{
 | 
						|
	TagCommon(a_Name, TAG_Int);
 | 
						|
	Int32 Value = htonl(a_Value);
 | 
						|
	m_Result.append((const char *)&Value, 4);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value)
 | 
						|
{
 | 
						|
	TagCommon(a_Name, TAG_Long);
 | 
						|
	Int64 Value = HostToNetwork8(&a_Value);
 | 
						|
	m_Result.append((const char *)&Value, 8);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value)
 | 
						|
{
 | 
						|
	TagCommon(a_Name, TAG_Float);
 | 
						|
	Int32 Value = HostToNetwork4(&a_Value);
 | 
						|
	m_Result.append((const char *)&Value, 4);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value)
 | 
						|
{
 | 
						|
	TagCommon(a_Name, TAG_Double);
 | 
						|
	Int64 Value = HostToNetwork8(&a_Value);
 | 
						|
	m_Result.append((const char *)&Value, 8);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value)
 | 
						|
{
 | 
						|
	TagCommon(a_Name, TAG_String);
 | 
						|
	Int16 len = htons((short)(a_Value.size()));
 | 
						|
	m_Result.append((const char *)&len, 2);
 | 
						|
	m_Result.append(a_Value.c_str(), a_Value.size());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements)
 | 
						|
{
 | 
						|
	TagCommon(a_Name, TAG_ByteArray);
 | 
						|
	Int32 len = htonl(a_NumElements);
 | 
						|
	m_Result.append((const char *)&len, 4);
 | 
						|
	m_Result.append(a_Value, a_NumElements);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::AddIntArray(const AString & a_Name, const int * a_Value, size_t a_NumElements)
 | 
						|
{
 | 
						|
	TagCommon(a_Name, TAG_IntArray);
 | 
						|
	Int32 len = htonl(a_NumElements);
 | 
						|
	m_Result.append((const char *)&len, 2);
 | 
						|
	int * Elements = (int *)(m_Result.data() + m_Result.size());
 | 
						|
	m_Result.append(a_NumElements * 4, (char)0);
 | 
						|
	for (size_t i = 0; i < a_NumElements; i++)
 | 
						|
	{
 | 
						|
		Elements[i] = htonl(a_Value[i]);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::Finish(void)
 | 
						|
{
 | 
						|
	ASSERT(m_CurrentStack == 0);
 | 
						|
	m_Result.push_back(TAG_End);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void cFastNBTWriter::WriteString(const char * a_Data, short a_Length)
 | 
						|
{
 | 
						|
	Int16 Len = htons(a_Length);
 | 
						|
	m_Result.append((const char *)&Len, 2);
 | 
						|
	m_Result.append(a_Data, a_Length);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 |