summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--VC2008/MCServer.vcproj24
-rw-r--r--VC2010/MCServer.vcxproj2
-rw-r--r--VC2010/MCServer.vcxproj.filters41
-rw-r--r--source/FastNBT.cpp320
-rw-r--r--source/FastNBT.h234
-rw-r--r--source/Globals.h9
-rw-r--r--source/NBT.h8
-rw-r--r--source/WSSAnvil.cpp150
-rw-r--r--source/WSSAnvil.h18
9 files changed, 702 insertions, 104 deletions
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj
index 4b8bcdbfb..146d735e1 100644
--- a/VC2008/MCServer.vcproj
+++ b/VC2008/MCServer.vcproj
@@ -573,14 +573,6 @@
>
</File>
<File
- RelativePath="..\source\NBT.cpp"
- >
- </File>
- <File
- RelativePath="..\source\NBT.h"
- >
- </File>
- <File
RelativePath="..\source\StackWalker.cpp"
>
<FileConfiguration
@@ -1630,6 +1622,22 @@
Name="World storage"
>
<File
+ RelativePath="..\source\FastNBT.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\FastNBT.h"
+ >
+ </File>
+ <File
+ RelativePath="..\source\NBT.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\NBT.h"
+ >
+ </File>
+ <File
RelativePath="..\source\WorldStorage.cpp"
>
</File>
diff --git a/VC2010/MCServer.vcxproj b/VC2010/MCServer.vcxproj
index d6ddb3b1c..ca7323f31 100644
--- a/VC2010/MCServer.vcxproj
+++ b/VC2010/MCServer.vcxproj
@@ -390,6 +390,7 @@
<ClCompile Include="..\source\cWorldGenerator_Test.cpp" />
<ClCompile Include="..\source\cZombie.cpp" />
<ClCompile Include="..\source\cZombiepigman.cpp" />
+ <ClCompile Include="..\source\FastNBT.cpp" />
<ClCompile Include="..\source\Globals.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug with optimized Noise|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
@@ -575,6 +576,7 @@
<ClInclude Include="..\source\cZombie.h" />
<ClInclude Include="..\source\cZombiepigman.h" />
<ClInclude Include="..\source\Endianness.h" />
+ <ClInclude Include="..\source\FastNBT.h" />
<ClInclude Include="..\Source\FileDefine.h" />
<ClInclude Include="..\source\Globals.h" />
<ClInclude Include="..\source\LeakFinder.h" />
diff --git a/VC2010/MCServer.vcxproj.filters b/VC2010/MCServer.vcxproj.filters
index 02421c803..dcbf530a9 100644
--- a/VC2010/MCServer.vcxproj.filters
+++ b/VC2010/MCServer.vcxproj.filters
@@ -445,6 +445,9 @@
<Filter Include="Simulator\cSimulator\cRedstoneSimulator">
<UniqueIdentifier>{4b3b7b43-8e8b-4823-b112-2259fdfff7d3}</UniqueIdentifier>
</Filter>
+ <Filter Include="Storage">
+ <UniqueIdentifier>{038cf4bd-108e-44e2-bdcb-9d48fb26676e}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\source\cServer.cpp">
@@ -887,20 +890,31 @@
<ClCompile Include="..\source\cSocketThreads.cpp" />
<ClCompile Include="..\source\LeakFinder.cpp" />
<ClCompile Include="..\source\StackWalker.cpp" />
- <ClCompile Include="..\source\WorldStorage.cpp" />
- <ClCompile Include="..\source\WSSCompact.cpp" />
<ClCompile Include="..\source\StringCompression.cpp" />
<ClCompile Include="..\source\LightingThread.cpp" />
<ClCompile Include="..\source\cRedstoneSimulator.cpp">
<Filter>Simulator\cSimulator\cRedstoneSimulator</Filter>
</ClCompile>
<ClCompile Include="..\source\ChunkSender.cpp" />
- <ClCompile Include="..\source\WSSAnvil.cpp" />
- <ClCompile Include="..\source\NBT.cpp" />
<ClCompile Include="..\source\WGFlat.cpp" />
<ClCompile Include="..\source\packets\cPacket_Player.cpp">
<Filter>Packets</Filter>
</ClCompile>
+ <ClCompile Include="..\source\NBT.cpp">
+ <Filter>Storage</Filter>
+ </ClCompile>
+ <ClCompile Include="..\source\WorldStorage.cpp">
+ <Filter>Storage</Filter>
+ </ClCompile>
+ <ClCompile Include="..\source\WSSAnvil.cpp">
+ <Filter>Storage</Filter>
+ </ClCompile>
+ <ClCompile Include="..\source\WSSCompact.cpp">
+ <Filter>Storage</Filter>
+ </ClCompile>
+ <ClCompile Include="..\source\FastNBT.cpp">
+ <Filter>Storage</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\source\cServer.h">
@@ -1379,23 +1393,34 @@
<ClInclude Include="..\source\StringUtils.h" />
<ClInclude Include="..\source\cIsThread.h" />
<ClInclude Include="..\source\cSocketThreads.h" />
- <ClInclude Include="..\source\WSSCompact.h" />
<ClInclude Include="..\source\LeakFinder.h" />
<ClInclude Include="..\source\StackWalker.h" />
- <ClInclude Include="..\source\WorldStorage.h" />
<ClInclude Include="..\source\StringCompression.h" />
<ClInclude Include="..\source\LightingThread.h" />
<ClInclude Include="..\source\cRedstoneSimulator.h">
<Filter>Simulator\cSimulator\cRedstoneSimulator</Filter>
</ClInclude>
<ClInclude Include="..\source\ChunkSender.h" />
- <ClInclude Include="..\source\WSSAnvil.h" />
- <ClInclude Include="..\source\NBT.h" />
<ClInclude Include="..\source\WGFlat.h" />
<ClInclude Include="..\source\ChunkDef.h" />
<ClInclude Include="..\source\packets\cPacket_Player.h">
<Filter>Packets</Filter>
</ClInclude>
+ <ClInclude Include="..\source\NBT.h">
+ <Filter>Storage</Filter>
+ </ClInclude>
+ <ClInclude Include="..\source\WorldStorage.h">
+ <Filter>Storage</Filter>
+ </ClInclude>
+ <ClInclude Include="..\source\WSSAnvil.h">
+ <Filter>Storage</Filter>
+ </ClInclude>
+ <ClInclude Include="..\source\WSSCompact.h">
+ <Filter>Storage</Filter>
+ </ClInclude>
+ <ClInclude Include="..\source\FastNBT.h">
+ <Filter>Storage</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\source\AllToLua.pkg">
diff --git a/source/FastNBT.cpp b/source/FastNBT.cpp
new file mode 100644
index 000000000..fa3edec52
--- /dev/null
+++ b/source/FastNBT.cpp
@@ -0,0 +1,320 @@
+
+// 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)
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFastNBTParser:
+
+#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;
+}
+
+
+
+
+
diff --git a/source/FastNBT.h b/source/FastNBT.h
new file mode 100644
index 000000000..fd60fc671
--- /dev/null
+++ b/source/FastNBT.h
@@ -0,0 +1,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;
+} ;
+
+
+
+
diff --git a/source/Globals.h b/source/Globals.h
index 99ef55444..3a9c6e9db 100644
--- a/source/Globals.h
+++ b/source/Globals.h
@@ -65,6 +65,15 @@
+// Integral types with predefined sizes:
+typedef long long Int64;
+typedef int Int32;
+typedef short Int16;
+
+
+
+
+
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for any class that shouldn't allow copying itself
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
diff --git a/source/NBT.h b/source/NBT.h
index f63e1fea1..39b3c7252 100644
--- a/source/NBT.h
+++ b/source/NBT.h
@@ -13,14 +13,6 @@
-typedef long long Int64;
-typedef int Int32;
-typedef short Int16;
-
-
-
-
-
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Representation classes:
diff --git a/source/WSSAnvil.cpp b/source/WSSAnvil.cpp
index 1b916a63e..e5400cf4c 100644
--- a/source/WSSAnvil.cpp
+++ b/source/WSSAnvil.cpp
@@ -15,6 +15,7 @@
#include "cEntity.h"
#include "cBlockEntity.h"
#include "cMakeDir.h"
+#include "FastNBT.h"
@@ -143,6 +144,7 @@ cWSSAnvil::cWSSAnvil(cWorld * a_World) :
Data->Add(new cNBTInt(Data, "SpawnZ", (int)(a_World->GetSpawnZ())));
AString Uncompressed;
cNBTSerializer::Serialize(Root.get(), Uncompressed);
+
gzFile gz = gzopen(fnam.c_str(), "wb");
if (gz != NULL)
{
@@ -304,14 +306,15 @@ bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString &
}
// Parse the NBT data:
- std::auto_ptr<cNBTTree> Tree(cNBTParser::Parse(Uncompressed, strm.total_out));
- if (Tree.get() == NULL)
+ cParsedNBT NBT(Uncompressed, strm.total_out);
+ if (!NBT.IsValid())
{
+ // NBT Parsing failed
return false;
}
// Load the data from NBT:
- return LoadChunkFromNBT(a_Chunk, *Tree.get());
+ return LoadChunkFromNBT(a_Chunk, NBT);
}
@@ -335,7 +338,7 @@ bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data)
-bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT)
+bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT)
{
// The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data)
BLOCKTYPE BlockData[cChunkDef::BlockDataSize];
@@ -347,49 +350,41 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT)
memset(SkyLight, 0xff, cChunkDef::NumBlocks / 2); // By default, data not present in the NBT means air, which means full skylight
// Load the blockdata, blocklight and skylight:
- cNBTList * Sections = (cNBTList *)a_NBT.FindChildByPath("Level\\Sections");
- if ((Sections == NULL) || (Sections->GetType() != cNBTTag::TAG_List) || (Sections->GetChildrenType() != cNBTTag::TAG_Compound))
+ int Level = a_NBT.FindChildByName(0, "Level");
+ if (Level < 0)
{
return false;
}
- const cNBTTags & LevelSections = Sections->GetChildren();
- for (cNBTTags::const_iterator itr = LevelSections.begin(); itr != LevelSections.end(); ++itr)
+ int Sections = a_NBT.FindChildByName(Level, "Sections");
+ if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List) || (a_NBT.GetChildrenType(Sections) != TAG_Compound))
+ {
+ return false;
+ }
+ for (int Child = a_NBT.GetFirstChild(Sections); Child >= 0; Child = a_NBT.GetNextSibling(Child))
{
int y = 0;
- cNBTByte * SectionY = (cNBTByte *)((*itr)->FindChildByName("Y"));
- if ((SectionY == NULL) || (SectionY->GetType() != cNBTTag::TAG_Byte) || (SectionY->m_Value < 0) || (SectionY->m_Value > 15))
+ int SectionY = a_NBT.FindChildByName(Child, "Y");
+ if ((SectionY < 0) || (a_NBT.GetType(SectionY) != TAG_Byte))
{
continue;
}
- y = SectionY->m_Value;
- cNBTByteArray * baBlocks = (cNBTByteArray *)((*itr)->FindChildByName("Blocks"));
- if ((baBlocks != NULL) && (baBlocks->GetType() == cNBTTag::TAG_ByteArray) && (baBlocks->m_Value.size() == 4096))
- {
- memcpy(&(BlockData[y * 4096]), baBlocks->m_Value.data(), 4096);
- }
- cNBTByteArray * baMetaData = (cNBTByteArray *)((*itr)->FindChildByName("Data"));
- if ((baMetaData != NULL) && (baMetaData->GetType() == cNBTTag::TAG_ByteArray) && (baMetaData->m_Value.size() == 2048))
- {
- memcpy(&(MetaData[y * 2048]), baMetaData->m_Value.data(), 2048);
- }
- cNBTByteArray * baSkyLight = (cNBTByteArray *)((*itr)->FindChildByName("SkyLight"));
- if ((baSkyLight != NULL) && (baSkyLight->GetType() == cNBTTag::TAG_ByteArray) && (baSkyLight->m_Value.size() == 2048))
- {
- memcpy(&(SkyLight[y * 2048]), baSkyLight->m_Value.data(), 2048);
- }
- cNBTByteArray * baBlockLight = (cNBTByteArray *)((*itr)->FindChildByName("BlockLight"));
- if ((baBlockLight != NULL) && (baBlockLight->GetType() == cNBTTag::TAG_ByteArray) && (baBlockLight->m_Value.size() == 2048))
+ y = a_NBT.GetByte(SectionY);
+ if ((y < 0) || (y > 15))
{
- memcpy(&(BlockLight[y * 2048]), baBlockLight->m_Value.data(), 2048);
+ continue;
}
+ CopyNBTData(a_NBT, Child, "Blocks", &(BlockData[y * 4096]), 4096);
+ CopyNBTData(a_NBT, Child, "Data", &(MetaData[y * 2048]), 2048);
+ CopyNBTData(a_NBT, Child, "SkyLight", &(SkyLight[y * 2048]), 2048);
+ CopyNBTData(a_NBT, Child, "BlockLight", &(BlockLight[y * 2048]), 2048);
} // for itr - LevelSections[]
cEntityList Entities;
cBlockEntityList BlockEntities;
// Load the entities from NBT:
- LoadEntitiesFromNBT (Entities, (cNBTList *)(a_NBT.FindChildByPath("Level\\Entities")));
- LoadBlockEntitiesFromNBT(BlockEntities, (cNBTList *)(a_NBT.FindChildByPath("Level\\TileEntities")));
+ LoadEntitiesFromNBT (Entities, a_NBT, a_NBT.FindChildByName(Level, "Entities"));
+ LoadBlockEntitiesFromNBT(BlockEntities, a_NBT, a_NBT.FindChildByName(Level, "TileEntities"));
#if (AXIS_ORDER == AXIS_ORDER_YZX)
// Reorder the chunk data - walk the MCA-formatted data sequentially and copy it into the right place in the ChunkData:
@@ -466,6 +461,18 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT)
+void cWSSAnvil::CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length)
+{
+ int Child = a_NBT.FindChildByName(a_Tag, a_ChildName);
+ if ((Child >= 0) && (a_NBT.GetType(Child) == TAG_ByteArray) && (a_NBT.GetDataLength(Child) == a_Length))
+ {
+ memcpy(a_Destination, a_NBT.GetData(Child), a_Length);
+ }
+}
+
+
+
+
cNBTTag * cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk)
{
@@ -511,7 +518,7 @@ cNBTTag * cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk)
-void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entitites, const cNBTList * a_NBT)
+void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_TagIdx)
{
// TODO: Load the entities
}
@@ -520,80 +527,77 @@ void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entitites, const cNBTList *
-void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, const cNBTList * a_NBT)
+void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- if ((a_NBT == NULL) || (a_NBT->GetType() != cNBTTag::TAG_List))
+ if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List))
{
return;
}
- const cNBTTags & Tags = a_NBT->GetChildren();
- for (cNBTTags::const_iterator itr = Tags.begin(); itr != Tags.end(); ++itr)
+ for (int Child = a_NBT.GetFirstChild(a_TagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child))
{
- if ((*itr)->GetType() != cNBTTag::TAG_Compound)
+ if (a_NBT.GetType(Child) != TAG_Compound)
{
continue;
}
- cNBTString * sID = (cNBTString *)((*itr)->FindChildByName("id"));
- if (sID == NULL)
+ int sID = a_NBT.FindChildByName(Child, "id");
+ if (sID < 0)
{
continue;
}
- if (sID->m_Value == "Chest")
+ if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0)
{
- LoadChestFromNBT(a_BlockEntities, (cNBTCompound *)(*itr));
+ LoadChestFromNBT(a_BlockEntities, a_NBT, Child);
}
// TODO: Other block entities
- } // for itr - Tags[]
+ } // for Child - tag children
}
-void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cNBTCompound * a_NBT)
+void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- ASSERT(a_NBT != NULL);
- ASSERT(a_NBT->GetType() == cNBTTag::TAG_Compound);
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, x, y, z))
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{
return;
}
- cNBTList * Items = (cNBTList *)(a_NBT->FindChildByName("Items"));
- if ((Items == NULL) || (Items->GetType() != cNBTTag::TAG_List))
+ int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
+ if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
- return; // Make it an empty chest
+ return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
}
std::auto_ptr<cChestEntity> Chest(new cChestEntity(x, y, z, m_World));
- const cNBTTags & ItemDefs = Items->GetChildren();
- for (cNBTTags::const_iterator itr = ItemDefs.begin(); itr != ItemDefs.end(); ++itr)
+ for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child))
{
- cNBTByte * Slot = (cNBTByte *)((*itr)->FindChildByName("Slot"));
- if ((Slot == NULL) || (Slot->GetType() != cNBTTag::TAG_Byte))
+ int Slot = a_NBT.FindChildByName(Child, "Slot");
+ if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte))
{
continue;
}
cItem Item;
- cNBTShort * ID = (cNBTShort *)((*itr)->FindChildByName("id"));
- if ((ID == NULL) || (ID->GetType() != cNBTTag::TAG_Short))
+ int ID = a_NBT.FindChildByName(Child, "id");
+ if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short))
{
continue;
}
- Item.m_ItemID = (ENUM_ITEM_ID)(ID->m_Value);
- cNBTShort * Damage = (cNBTShort *)((*itr)->FindChildByName("Damage"));
- if ((Damage == NULL) || (Damage->GetType() != cNBTTag::TAG_Short))
+ Item.m_ItemID = (ENUM_ITEM_ID)(a_NBT.GetShort(ID));
+ int Damage = a_NBT.FindChildByName(Child, "Damage");
+ if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short))
{
continue;
}
- Item.m_ItemHealth = Damage->m_Value;
- cNBTByte * Count = (cNBTByte *)((*itr)->FindChildByName("Count"));
- if ((Count == NULL) || (Count->GetType() != cNBTTag::TAG_Byte))
+ Item.m_ItemHealth = a_NBT.GetShort(Damage);
+ int Count = a_NBT.FindChildByName(Child, "Count");
+ if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte))
{
continue;
}
- Item.m_ItemCount = Count->m_Value;
- Chest->SetSlot(Slot->m_Value, Item);
+ Item.m_ItemCount = a_NBT.GetByte(Count);
+ Chest->SetSlot(a_NBT.GetByte(Slot), Item);
} // for itr - ItemDefs[]
a_BlockEntities.push_back(Chest.release());
}
@@ -602,26 +606,26 @@ void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cNBTC
-bool cWSSAnvil::GetBlockEntityNBTPos(const cNBTCompound * a_NBT, int & a_X, int & a_Y, int & a_Z)
+bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z)
{
- cNBTInt * x = (cNBTInt *)(a_NBT->FindChildByName("x"));
- if ((x == NULL) || (x->GetType() != cNBTTag::TAG_Int))
+ int x = a_NBT.FindChildByName(a_TagIdx, "x");
+ if ((x < 0) || (a_NBT.GetType(x) != TAG_Int))
{
return false;
}
- cNBTInt * y = (cNBTInt *)(a_NBT->FindChildByName("y"));
- if ((y == NULL) || (y->GetType() != cNBTTag::TAG_Int))
+ int y = a_NBT.FindChildByName(a_TagIdx, "y");
+ if ((y < 0) || (a_NBT.GetType(y) != TAG_Int))
{
return false;
}
- cNBTInt * z = (cNBTInt *)(a_NBT->FindChildByName("z"));
- if ((z == NULL) || (z->GetType() != cNBTTag::TAG_Int))
+ int z = a_NBT.FindChildByName(a_TagIdx, "z");
+ if ((z < 0) || (a_NBT.GetType(z) != TAG_Int))
{
return false;
}
- a_X = x->m_Value;
- a_Y = y->m_Value;
- a_Z = z->m_Value;
+ a_X = a_NBT.GetInt(x);
+ a_Y = a_NBT.GetInt(y);
+ a_Z = a_NBT.GetInt(z);
return true;
}
diff --git a/source/WSSAnvil.h b/source/WSSAnvil.h
index 37d813f28..58259f0e8 100644
--- a/source/WSSAnvil.h
+++ b/source/WSSAnvil.h
@@ -9,6 +9,7 @@
#pragma once
#include "WorldStorage.h"
+#include "FastNBT.h"
@@ -99,24 +100,27 @@ protected:
bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data);
/// Loads the chunk from NBT data (no locking needed)
- bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT);
+ bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT);
/// Saves the chunk into NBT data; returns NULL for failure
cNBTTag * SaveChunkToNBT(const cChunkCoords & a_Chunk);
- /// Loads the chunk's entities from NBT data (a_NBT is the Level\\Entities list tag; may be NULL)
- void LoadEntitiesFromNBT(cEntityList & a_Entitites, const cNBTList * a_NBT);
+ /// Loads the chunk's entities from NBT data (a_Tag is the Level\\Entities list tag; may be -1)
+ void LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_Tag);
- /// Loads the chunk's BlockEntities from NBT data (a_NBT is the Level\\TileEntities list tag; may be NULL)
- void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cNBTList * a_NBT);
+ /// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1)
+ void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag);
- void LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cNBTCompound * a_NBT);
+ void LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
/// Helper function for extracting the X, Y, and Z int subtags of a NBT compound; returns true if successful
- bool GetBlockEntityNBTPos(const cNBTCompound * a_NBT, int & a_X, int & a_Y, int & a_Z);
+ bool GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z);
/// Gets the correct MCA file either from cache or from disk, manages the m_MCAFiles cache; assumes m_CS is locked
cMCAFile * LoadMCAFile(const cChunkCoords & a_Chunk);
+
+ /// Copies a_Length bytes of data from the specified NBT Tag's Child into the a_Destination buffer
+ void CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length);
// cWSSchema overrides:
virtual bool LoadChunk(const cChunkCoords & a_Chunk) override;