summaryrefslogtreecommitdiffstats
path: root/source/FastNBT.h
blob: fd60fc671d6b3169203adddfaf6543a9ea93dfbc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234

// FastNBT.h

// Interfaces to the fast NBT parser and writer

/*
The fast parser parses the data into a vector of cFastNBTTag structures. These structures describe the NBT tree,
but themselves are allocated in a vector, thus minimizing reallocation. 
The structures have a minimal constructor, setting all member "pointers" to "invalid".

The fast writer doesn't need a NBT tree structure built beforehand, it is commanded to open, append and close tags
(just like XML); it keeps the internal tag stack and reports errors in usage. 
It directly outputs a string containing the serialized NBT data.
*/





#pragma once

#include "Endianness.h"





enum eTagType
{
	TAG_Min       = 0,  // The minimum value for a tag type
	TAG_End       = 0,
	TAG_Byte      = 1,
	TAG_Short     = 2,
	TAG_Int       = 3,
	TAG_Long      = 4,
	TAG_Float     = 5,
	TAG_Double    = 6,
	TAG_ByteArray = 7,
	TAG_String    = 8,
	TAG_List      = 9,
	TAG_Compound  = 10,
	TAG_IntArray  = 11,
	TAG_Max       = 11,  // The maximum value for a tag type
} ;





/** This structure is used for all NBT tags.
It contains indices to the parent array of tags, building the NBT tree this way.
Also contains indices into the data stream being parsed, used for values;
NO dynamically allocated memory is used!
Structure (all with the tree structure it describes) supports moving in memory (std::vector reallocation)
*/
struct cFastNBTTag
{
public:
	
	eTagType m_Type;
	
	// The following members are indices into the data stream. m_DataLength == 0 if no data available
	// They must not be pointers, because the datastream may be copied into another AString object in the meantime.
	int m_NameStart;
	int m_NameLength;
	int m_DataStart;
	int m_DataLength;
	
	// The following members are indices into the array returned; -1 if not valid
	// They must not be pointers, because pointers would not survive std::vector reallocation
	int m_Parent;
	int m_PrevSibling;
	int m_NextSibling;
	int m_FirstChild;
	int m_LastChild;
	
	cFastNBTTag(eTagType a_Type, int a_Parent) :
		m_Type(a_Type),
		m_NameLength(0),
		m_DataLength(0),
		m_Parent(a_Parent),
		m_PrevSibling(-1),
		m_NextSibling(-1),
		m_FirstChild(-1),
		m_LastChild(-1)
	{
	}

	cFastNBTTag(eTagType a_Type, int a_Parent, int a_PrevSibling) :
		m_Type(a_Type),
		m_NameLength(0),
		m_DataLength(0),
		m_Parent(a_Parent),
		m_PrevSibling(a_PrevSibling),
		m_NextSibling(-1),
		m_FirstChild(-1),
		m_LastChild(-1)
	{
	}
} ;





/** Parses and contains the parsed data
Also implements data accessor functions for tree traversal and value getters
The data pointer passed in the constructor is assumed to be valid throughout the object's life. Care must be taken not to initialize from a temporary.
*/
class cParsedNBT
{
public:
	cParsedNBT(const char * a_Data, int a_Length);
	
	bool IsValid(void) const {return m_IsValid; }
	
	int GetRoot(void) const {return 0; }
	int GetFirstChild (int a_Tag) const { return m_Tags[a_Tag].m_FirstChild; }
	int GetLastChild  (int a_Tag) const { return m_Tags[a_Tag].m_LastChild; }
	int GetNextSibling(int a_Tag) const { return m_Tags[a_Tag].m_NextSibling; }
	int GetPrevSibling(int a_Tag) const { return m_Tags[a_Tag].m_PrevSibling; }
	int GetDataLength (int a_Tag) const { return m_Tags[a_Tag].m_DataLength; }

	const char * GetData(int a_Tag) const
	{
		ASSERT(m_Tags[a_Tag].m_Type != TAG_List);
		ASSERT(m_Tags[a_Tag].m_Type != TAG_Compound);
		return m_Data + m_Tags[a_Tag].m_DataStart;
	}
	
	int FindChildByName(int a_Tag, const AString & a_Name) const
	{
		return FindChildByName(a_Tag, a_Name.c_str(), a_Name.length());
	}
	
	int FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength = 0) const;
	int FindTagByPath  (int a_Tag, const AString & a_Path) const;
	
	eTagType GetType(int a_Tag) const { return m_Tags[a_Tag].m_Type; }
	
	/// Returns the children type for a list tag; undefined on other tags. If list empty, returns TAG_End
	eTagType GetChildrenType(int a_Tag) const
	{
		ASSERT(m_Tags[a_Tag].m_Type == TAG_List);
		return (m_Tags[a_Tag].m_FirstChild < 0) ? TAG_End : m_Tags[m_Tags[a_Tag].m_FirstChild].m_Type;
	}
	
	inline unsigned char GetByte(int a_Tag) const 
	{
		ASSERT(m_Tags[a_Tag].m_Type == TAG_Byte);
		return (unsigned char)(m_Data[m_Tags[a_Tag].m_DataStart]);
	}
	
	inline Int16 GetShort(int a_Tag) const
	{
		ASSERT(m_Tags[a_Tag].m_Type == TAG_Short);
		return ntohs(*((Int16 *)(m_Data + m_Tags[a_Tag].m_DataStart)));
	}

	inline Int32 GetInt(int a_Tag) const
	{
		ASSERT(m_Tags[a_Tag].m_Type == TAG_Int);
		return ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart)));
	}

	inline Int64 GetLong(int a_Tag) const
	{
		ASSERT(m_Tags[a_Tag].m_Type == TAG_Long);
		return NetworkToHostLong8(m_Data + m_Tags[a_Tag].m_DataStart);
	}

	inline float GetFloat(int a_Tag) const
	{
		ASSERT(m_Tags[a_Tag].m_Type == TAG_Float);
		Int32 tmp = ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart)));
		return *((float *)&tmp);
	}
	
	inline double GetDouble(int a_Tag) const
	{
		ASSERT(m_Tags[a_Tag].m_Type == TAG_Double);
		return NetworkToHostDouble8(m_Data + m_Tags[a_Tag].m_DataStart);
	}
	
protected:
	const char *             m_Data;
	int                      m_Length;
	std::vector<cFastNBTTag> m_Tags;
	bool                     m_IsValid;  // True if parsing succeeded

	// Used while parsing:
	int m_Pos;

	bool Parse(void);
	bool ReadString(int & a_StringStart, int & a_StringLen);  // Reads a simple string (2 bytes length + data), sets the string descriptors
	bool ReadCompound(void);  // Reads the latest tag as a compound
	bool ReadList(eTagType a_ChildrenType);  // Reads the latest tag as a list of items of type a_ChildrenType
	bool ReadTag(void);       // Reads the latest tag, depending on its m_Type setting
} ;





class cFastNBTWriter
{
public:
	cFastNBTWriter(void);
	
	void BeginCompound(const AString & a_Name);
	void EndCompound(void);
	
	void BeginList(const AString & a_Name, eTagType a_ChildrenType);
	void EndList(void);
	
	void AddByte     (const AString & a_Name, unsigned char a_Value);
	void AddShort    (const AString & a_Name, unsigned char a_Value);
	void AddInt      (const AString & a_Name, unsigned char a_Value);
	void AddLong     (const AString & a_Name, unsigned char a_Value);
	void AddFloat    (const AString & a_Name, unsigned char a_Value);
	void AddDouble   (const AString & a_Name, unsigned char a_Value);
	void AddString   (const AString & a_Name, unsigned char a_Value);
	void AddByteArray(const AString & a_Name, unsigned char a_Value);
	void AddIntArray (const AString & a_Name, unsigned char a_Value);

	const AString & GetResult(void) const {return m_Result; }
	
protected:
	AString m_Result;
} ;