Cuberite
A lightweight, fast and extensible game server for Minecraft
FastNBT.cpp
Go to the documentation of this file.
1 
2 // FastNBT.cpp
3 
4 // Implements the fast NBT parser and writer
5 
6 #include "Globals.h"
7 #include "FastNBT.h"
8 
9 
10 
11 
12 
14 static const int MAX_LIST_ITEMS = 10000;
15 
16 
17 
18 
19 
20 // The number of NBT tags that are reserved when an NBT parsing is started.
21 // You can override this by using a cmdline define
22 #ifndef NBT_RESERVE_SIZE
23  #define NBT_RESERVE_SIZE 200
24 #endif // NBT_RESERVE_SIZE
25 
26 #ifdef _MSC_VER
27  // Dodge a C4127 (conditional expression is constant) for this specific macro usage
28  #define PROPAGATE_ERROR(X) do { auto Err = (X); if (Err != eNBTParseError::npSuccess) return Err; } while ((false, false))
29 #else
30  #define PROPAGATE_ERROR(X) do { auto Err = (X); if (Err != eNBTParseError::npSuccess) return Err; } while (false)
31 #endif
32 
33 
34 
36 // cNBTParseErrorCategory:
37 
38 namespace
39 {
40 
41 class cNBTParseErrorCategory final :
42  public std::error_category
43 {
44  cNBTParseErrorCategory() = default;
45 public:
47  virtual const char * name() const NOEXCEPT override
48  {
49  return "NBT parse error";
50  }
51 
53  virtual AString message(int a_Condition) const override;
54 
56  static const cNBTParseErrorCategory & Get() NOEXCEPT
57  {
58  static cNBTParseErrorCategory Category;
59  return Category;
60  }
61 };
62 
63 
64 
65 
66 
67 AString cNBTParseErrorCategory::message(int a_Condition) const
68 {
69  switch (static_cast<eNBTParseError>(a_Condition))
70  {
72  {
73  return "Parsing succeded";
74  }
76  {
77  return "Expected more data";
78  }
80  {
81  return "No top level compound tag";
82  }
84  {
85  return "Expected a string length but had insufficient data";
86  }
88  {
89  return "String length invalid";
90  }
92  {
93  return "Compound tag was unmatched at end of file";
94  }
96  {
97  return "Expected a list type but had insuffiecient data";
98  }
100  {
101  return "Expected a list length but had insufficient data";
102  }
104  {
105  return "List length invalid";
106  }
108  {
109  return "Expected a numeric type but had insufficient data";
110  }
112  {
113  return "Expected an array length but had insufficient data";
114  }
116  {
117  return "Array length invalid";
118  }
120  {
121  return "Unknown tag";
122  }
123  }
124  UNREACHABLE("Unsupported nbt parse error");
125 }
126 
127 } // namespace (anonymous)
128 
129 
130 
131 
132 
133 std::error_code make_error_code(eNBTParseError a_Err) NOEXCEPT
134 {
135  return { static_cast<int>(a_Err), cNBTParseErrorCategory::Get() };
136 }
137 
138 
139 
140 
141 
143 // cParsedNBT:
144 
145 #define NEEDBYTES(N, ERR) \
146  do { \
147  if (m_Length - m_Pos < static_cast<size_t>(N)) \
148  { \
149  return ERR; \
150  } \
151  } while (false)
152 
153 
154 
155 
156 
157 cParsedNBT::cParsedNBT(const char * a_Data, size_t a_Length) :
158  m_Data(a_Data),
159  m_Length(a_Length),
160  m_Pos(0)
161 {
162  m_Error = Parse();
163 }
164 
165 
166 
167 
168 
170 {
171  if (m_Length < 3)
172  {
173  // Data too short
175  }
176  if (m_Data[0] != TAG_Compound)
177  {
178  // The top-level tag must be a Compound
180  }
181 
182  m_Tags.reserve(NBT_RESERVE_SIZE);
183 
184  m_Tags.emplace_back(TAG_Compound, -1);
185 
186  m_Pos = 1;
187 
188  PROPAGATE_ERROR(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength));
189  return ReadCompound();
190 }
191 
192 
193 
194 
195 
196 eNBTParseError cParsedNBT::ReadString(size_t & a_StringStart, size_t & a_StringLen)
197 {
199  a_StringStart = m_Pos + 2;
200  a_StringLen = static_cast<size_t>(GetBEShort(m_Data + m_Pos));
202  m_Pos += 2 + a_StringLen;
204 }
205 
206 
207 
208 
209 
211 {
212  ASSERT(m_Tags.size() > 0);
213 
214  // Reads the latest tag as a compound
215  size_t ParentIdx = m_Tags.size() - 1;
216  int PrevSibling = -1;
217  for (;;)
218  {
220  const char TagTypeNum = m_Data[m_Pos];
221  if ((TagTypeNum < TAG_Min) || (TagTypeNum > TAG_Max))
222  {
224  }
225  eTagType TagType = static_cast<eTagType>(TagTypeNum);
226  m_Pos++;
227  if (TagType == TAG_End)
228  {
229  break;
230  }
231  m_Tags.emplace_back(TagType, static_cast<int>(ParentIdx), PrevSibling);
232  if (PrevSibling >= 0)
233  {
234  m_Tags[static_cast<size_t>(PrevSibling)].m_NextSibling = static_cast<int>(m_Tags.size()) - 1;
235  }
236  else
237  {
238  m_Tags[ParentIdx].m_FirstChild = static_cast<int>(m_Tags.size()) - 1;
239  }
240  PrevSibling = static_cast<int>(m_Tags.size()) - 1;
241  PROPAGATE_ERROR(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength));
243  } // while (true)
244  m_Tags[ParentIdx].m_LastChild = PrevSibling;
246 }
247 
248 
249 
250 
251 
253 {
254  // Reads the latest tag as a list of items of type a_ChildrenType
255 
256  // Read the count:
258  int Count = GetBEInt(m_Data + m_Pos);
259  m_Pos += 4;
260  if ((Count < 0) || (Count > MAX_LIST_ITEMS))
261  {
263  }
264 
265  // Read items:
266  ASSERT(m_Tags.size() > 0);
267  size_t ParentIdx = m_Tags.size() - 1;
268  int PrevSibling = -1;
269  for (int i = 0; i < Count; i++)
270  {
271  m_Tags.emplace_back(a_ChildrenType, static_cast<int>(ParentIdx), PrevSibling);
272  if (PrevSibling >= 0)
273  {
274  m_Tags[static_cast<size_t>(PrevSibling)].m_NextSibling = static_cast<int>(m_Tags.size()) - 1;
275  }
276  else
277  {
278  m_Tags[ParentIdx].m_FirstChild = static_cast<int>(m_Tags.size()) - 1;
279  }
280  PrevSibling = static_cast<int>(m_Tags.size()) - 1;
282  } // for (i)
283  m_Tags[ParentIdx].m_LastChild = PrevSibling;
285 }
286 
287 
288 
289 
290 
291 #define CASE_SIMPLE_TAG(TAGTYPE, LEN) \
292  case TAG_##TAGTYPE: \
293  { \
294  NEEDBYTES(LEN, eNBTParseError::npSimpleMissing); \
295  Tag.m_DataStart = m_Pos; \
296  Tag.m_DataLength = LEN; \
297  m_Pos += LEN; \
298  return eNBTParseError::npSuccess; \
299  }
300 
302 {
303  cFastNBTTag & Tag = m_Tags.back();
304  switch (Tag.m_Type)
305  {
307  CASE_SIMPLE_TAG(Short, 2)
308  CASE_SIMPLE_TAG(Int, 4)
309  CASE_SIMPLE_TAG(Long, 8)
310  CASE_SIMPLE_TAG(Float, 4)
311  CASE_SIMPLE_TAG(Double, 8)
312 
313  case TAG_String:
314  {
315  return ReadString(Tag.m_DataStart, Tag.m_DataLength);
316  }
317 
318  case TAG_ByteArray:
319  {
321  int len = GetBEInt(m_Data + m_Pos);
322  m_Pos += 4;
323  if (len < 0)
324  {
325  // Invalid length
327  }
329  Tag.m_DataLength = static_cast<size_t>(len);
330  Tag.m_DataStart = m_Pos;
331  m_Pos += static_cast<size_t>(len);
333  }
334 
335  case TAG_List:
336  {
338  eTagType ItemType = static_cast<eTagType>(m_Data[m_Pos]);
339  m_Pos++;
340  PROPAGATE_ERROR(ReadList(ItemType));
342  }
343 
344  case TAG_Compound:
345  {
348  }
349 
350  case TAG_IntArray:
351  {
353  int len = GetBEInt(m_Data + m_Pos);
354  m_Pos += 4;
355  if (len < 0)
356  {
357  // Invalid length
359  }
360  len *= 4;
362  Tag.m_DataLength = static_cast<size_t>(len);
363  Tag.m_DataStart = m_Pos;
364  m_Pos += static_cast<size_t>(len);
366  }
367 
368  case TAG_Min:
369  {
371  }
372  } // switch (iType)
373  UNREACHABLE("Unsupported nbt tag type");
374 }
375 
376 #undef CASE_SIMPLE_TAG
377 
378 
379 
380 
381 
382 int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength) const
383 {
384  if (a_Tag < 0)
385  {
386  return -1;
387  }
388  if (m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_Compound)
389  {
390  return -1;
391  }
392 
393  if (a_NameLength == 0)
394  {
395  a_NameLength = strlen(a_Name);
396  }
397  for (int Child = m_Tags[static_cast<size_t>(a_Tag)].m_FirstChild; Child != -1; Child = m_Tags[static_cast<size_t>(Child)].m_NextSibling)
398  {
399  if (
400  (m_Tags[static_cast<size_t>(Child)].m_NameLength == a_NameLength) &&
401  (memcmp(m_Data + m_Tags[static_cast<size_t>(Child)].m_NameStart, a_Name, a_NameLength) == 0)
402  )
403  {
404  return Child;
405  }
406  } // for Child - children of a_Tag
407  return -1;
408 }
409 
410 
411 
412 
413 
414 int cParsedNBT::FindTagByPath(int a_Tag, const AString & a_Path) const
415 {
416  if (a_Tag < 0)
417  {
418  return -1;
419  }
420  size_t Begin = 0;
421  size_t Length = a_Path.length();
422  int Tag = a_Tag;
423  for (size_t i = 0; i < Length; i++)
424  {
425  if (a_Path[i] != '\\')
426  {
427  continue;
428  }
429  Tag = FindChildByName(Tag, a_Path.c_str() + Begin, i - Begin);
430  if (Tag < 0)
431  {
432  return -1;
433  }
434  Begin = i + 1;
435  } // for i - a_Path[]
436 
437  if (Begin < Length)
438  {
439  Tag = FindChildByName(Tag, a_Path.c_str() + Begin, Length - Begin);
440  }
441  return Tag;
442 }
443 
444 
445 
446 
447 
449 // cFastNBTWriter:
450 
451 cFastNBTWriter::cFastNBTWriter(const AString & a_RootTagName) :
452  m_CurrentStack(0)
453 {
455  m_Result.reserve(100 * 1024);
456  m_Result.push_back(TAG_Compound);
457  WriteString(a_RootTagName.data(), static_cast<UInt16>(a_RootTagName.size()));
458 }
459 
460 
461 
462 
463 
465 {
466  if (m_CurrentStack >= MAX_STACK - 1)
467  {
468  ASSERT(!"Stack overflow");
469  return;
470  }
471 
472  TagCommon(a_Name, TAG_Compound);
473 
474  ++m_CurrentStack;
476 }
477 
478 
479 
480 
481 
483 {
484  ASSERT(m_CurrentStack > 0);
486 
487  m_Result.push_back(TAG_End);
488  --m_CurrentStack;
489 }
490 
491 
492 
493 
494 
495 void cFastNBTWriter::BeginList(const AString & a_Name, eTagType a_ChildrenType)
496 {
497  if (m_CurrentStack >= MAX_STACK - 1)
498  {
499  ASSERT(!"Stack overflow");
500  return;
501  }
502 
503  TagCommon(a_Name, TAG_List);
504 
505  m_Result.push_back(static_cast<char>(a_ChildrenType));
506  m_Result.append(4, static_cast<char>(0));
507 
508  ++m_CurrentStack;
510  m_Stack[m_CurrentStack].m_Pos = static_cast<int>(m_Result.size()) - 4;
512  m_Stack[m_CurrentStack].m_ItemType = a_ChildrenType;
513 }
514 
515 
516 
517 
518 
520 {
521  ASSERT(m_CurrentStack > 0);
523 
524  // Update the list count:
525  SetBEInt(const_cast<char *>(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos), m_Stack[m_CurrentStack].m_Count);
526 
527  --m_CurrentStack;
528 }
529 
530 
531 
532 
533 
534 void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value)
535 {
536  TagCommon(a_Name, TAG_Byte);
537  m_Result.push_back(static_cast<char>(a_Value));
538 }
539 
540 
541 
542 
543 
544 void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value)
545 {
546  TagCommon(a_Name, TAG_Short);
547  UInt16 Value = htons(static_cast<UInt16>(a_Value));
548  m_Result.append(reinterpret_cast<const char *>(&Value), 2);
549 }
550 
551 
552 
553 
554 
555 void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value)
556 {
557  TagCommon(a_Name, TAG_Int);
558  UInt32 Value = htonl(static_cast<UInt32>(a_Value));
559  m_Result.append(reinterpret_cast<const char *>(&Value), 4);
560 }
561 
562 
563 
564 
565 
566 void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value)
567 {
568  TagCommon(a_Name, TAG_Long);
569  UInt64 Value = HostToNetwork8(&a_Value);
570  m_Result.append(reinterpret_cast<const char *>(&Value), 8);
571 }
572 
573 
574 
575 
576 
577 void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value)
578 {
579  TagCommon(a_Name, TAG_Float);
580  UInt32 Value = HostToNetwork4(&a_Value);
581  m_Result.append(reinterpret_cast<const char *>(&Value), 4);
582 }
583 
584 
585 
586 
587 
588 void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value)
589 {
590  TagCommon(a_Name, TAG_Double);
591  UInt64 Value = HostToNetwork8(&a_Value);
592  m_Result.append(reinterpret_cast<const char *>(&Value), 8);
593 }
594 
595 
596 
597 
598 
599 void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value)
600 {
601  TagCommon(a_Name, TAG_String);
602  UInt16 len = htons(static_cast<UInt16>(a_Value.size()));
603  m_Result.append(reinterpret_cast<const char *>(&len), 2);
604  m_Result.append(a_Value.c_str(), a_Value.size());
605 }
606 
607 
608 
609 
610 
611 void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements)
612 {
613  TagCommon(a_Name, TAG_ByteArray);
614  UInt32 len = htonl(static_cast<UInt32>(a_NumElements));
615  m_Result.append(reinterpret_cast<const char *>(&len), 4);
616  m_Result.append(a_Value, a_NumElements);
617 }
618 
619 
620 
621 
622 
623 void cFastNBTWriter::AddIntArray(const AString & a_Name, const int * a_Value, size_t a_NumElements)
624 {
625  TagCommon(a_Name, TAG_IntArray);
626  UInt32 len = htonl(static_cast<UInt32>(a_NumElements));
627  size_t cap = m_Result.capacity();
628  size_t size = m_Result.length();
629  if ((cap - size) < (4 + a_NumElements * 4))
630  {
631  m_Result.reserve(size + 4 + (a_NumElements * 4));
632  }
633  m_Result.append(reinterpret_cast<const char *>(&len), 4);
634  for (size_t i = 0; i < a_NumElements; i++)
635  {
636  UInt32 Element = htonl(static_cast<UInt32>(a_Value[i]));
637  m_Result.append(reinterpret_cast<const char *>(&Element), 4);
638  }
639 }
640 
641 
642 
643 
644 
646 {
647  ASSERT(m_CurrentStack == 0);
648  m_Result.push_back(TAG_End);
649 }
650 
651 
652 
653 
654 
655 void cFastNBTWriter::WriteString(const char * a_Data, UInt16 a_Length)
656 {
657  UInt16 Len = htons(a_Length);
658  m_Result.append(reinterpret_cast<const char *>(&Len), 2);
659  m_Result.append(a_Data, a_Length);
660 }
661 
662 
663 
664 
void TagCommon(const AString &a_Name, eTagType a_Type)
Definition: FastNBT.h:368
UInt64 HostToNetwork8(const void *a_Value)
Definition: Endianness.h:12
eNBTParseError ReadCompound(void)
Definition: FastNBT.cpp:210
void EndList(void)
Definition: FastNBT.cpp:519
void AddFloat(const AString &a_Name, float a_Value)
Definition: FastNBT.cpp:577
This structure is used for all NBT tags.
Definition: FastNBT.h:57
cFastNBTWriter(const AString &a_RootTagName="")
Definition: FastNBT.cpp:451
void AddByteArray(const AString &a_Name, const char *a_Value, size_t a_NumElements)
Definition: FastNBT.cpp:611
size_t m_Length
Definition: FastNBT.h:298
signed short Int16
Definition: Globals.h:109
int m_CurrentStack
Definition: FastNBT.h:360
void AddShort(const AString &a_Name, Int16 a_Value)
Definition: FastNBT.cpp:544
void WriteString(const char *a_Data, UInt16 a_Length)
Definition: FastNBT.cpp:655
AString m_Result
Definition: FastNBT.h:362
cParsedNBT(const char *a_Data, size_t a_Length)
Definition: FastNBT.cpp:157
void AddByte(const AString &a_Name, unsigned char a_Value)
Definition: FastNBT.cpp:534
#define CASE_SIMPLE_TAG(TAGTYPE, LEN)
Definition: FastNBT.cpp:291
size_t m_Pos
Definition: FastNBT.h:303
size_t m_DataStart
Definition: FastNBT.h:67
std::vector< cFastNBTTag > m_Tags
Definition: FastNBT.h:299
eNBTParseError ReadString(size_t &a_StringStart, size_t &a_StringLen)
Definition: FastNBT.cpp:196
eTagType m_Type
Definition: FastNBT.h:61
void SetBEInt(char *a_Mem, Int32 a_Value)
Writes four bytes to the specified memory location so that they interpret as BigEndian int...
void EndCompound(void)
Definition: FastNBT.cpp:482
eNBTParseError ReadTag(void)
Definition: FastNBT.cpp:301
unsigned long long UInt64
Definition: Globals.h:112
short GetBEShort(const char *a_Mem)
Reads two bytes from the specified memory location and interprets them as BigEndian short...
eNBTParseError
Definition: FastNBT.h:111
eTagType
Definition: FastNBT.h:29
#define ASSERT(x)
Definition: Globals.h:335
void AddIntArray(const AString &a_Name, const int *a_Value, size_t a_NumElements)
Definition: FastNBT.cpp:623
#define NEEDBYTES(N, ERR)
Definition: FastNBT.cpp:145
const char * m_Data
Definition: FastNBT.h:297
bool IsStackTopCompound(void) const
Definition: FastNBT.h:364
unsigned short UInt16
Definition: Globals.h:114
std::string AString
Definition: StringUtils.h:13
int FindChildByName(int a_Tag, const AString &a_Name) const
Returns the direct child tag of the specified name, or -1 if no such tag.
Definition: FastNBT.h:199
void AddLong(const AString &a_Name, Int64 a_Value)
Definition: FastNBT.cpp:566
void AddString(const AString &a_Name, const AString &a_Value)
Definition: FastNBT.cpp:599
signed int Int32
Definition: Globals.h:108
#define PROPAGATE_ERROR(X)
Definition: FastNBT.cpp:30
unsigned int UInt32
Definition: Globals.h:113
eMonsterType m_Type
Definition: Monster.cpp:33
void AddInt(const AString &a_Name, Int32 a_Value)
Definition: FastNBT.cpp:555
size_t m_DataLength
Definition: FastNBT.h:68
#define NBT_RESERVE_SIZE
Definition: FastNBT.cpp:23
unsigned char Byte
Definition: Globals.h:117
static const int MAX_STACK
Definition: FastNBT.h:356
std::error_code make_error_code(eNBTParseError a_Err) NOEXCEPT
Definition: FastNBT.cpp:133
void Finish(void)
Definition: FastNBT.cpp:645
eNBTParseError m_Error
Definition: FastNBT.h:300
int FindTagByPath(int a_Tag, const AString &a_Path) const
Returns the child tag of the specified path (Name1 / Name2 / Name3...), or -1 if no such tag...
Definition: FastNBT.cpp:414
void AddDouble(const AString &a_Name, double a_Value)
Definition: FastNBT.cpp:588
void BeginList(const AString &a_Name, eTagType a_ChildrenType)
Definition: FastNBT.cpp:495
int GetBEInt(const char *a_Mem)
Reads four bytes from the specified memory location and interprets them as BigEndian int...
void BeginCompound(const AString &a_Name)
Definition: FastNBT.cpp:464
static const int MAX_LIST_ITEMS
If a list being loaded has more than this number of items, it&#39;s considered corrupted.
Definition: FastNBT.cpp:14
#define UNREACHABLE(x)
Use to mark code that should be impossible to reach.
Definition: Globals.h:344
UInt32 HostToNetwork4(const void *a_Value)
Definition: Endianness.h:24
signed long long Int64
Definition: Globals.h:107
eNBTParseError Parse(void)
Definition: FastNBT.cpp:169
sParent m_Stack[MAX_STACK]
Definition: FastNBT.h:359
eNBTParseError ReadList(eTagType a_ChildrenType)
Definition: FastNBT.cpp:252