Fast NBT Parser (loading a chunk is now about 10 times faster)
git-svn-id: http://mc-server.googlecode.com/svn/trunk@481 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
parent
b73fba7c08
commit
35c8bc4c58
@ -572,14 +572,6 @@
|
|||||||
RelativePath="..\source\MersenneTwister.h"
|
RelativePath="..\source\MersenneTwister.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
|
||||||
RelativePath="..\source\NBT.cpp"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="..\source\NBT.h"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
<File
|
<File
|
||||||
RelativePath="..\source\StackWalker.cpp"
|
RelativePath="..\source\StackWalker.cpp"
|
||||||
>
|
>
|
||||||
@ -1629,6 +1621,22 @@
|
|||||||
<Filter
|
<Filter
|
||||||
Name="World storage"
|
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
|
<File
|
||||||
RelativePath="..\source\WorldStorage.cpp"
|
RelativePath="..\source\WorldStorage.cpp"
|
||||||
>
|
>
|
||||||
|
@ -390,6 +390,7 @@
|
|||||||
<ClCompile Include="..\source\cWorldGenerator_Test.cpp" />
|
<ClCompile Include="..\source\cWorldGenerator_Test.cpp" />
|
||||||
<ClCompile Include="..\source\cZombie.cpp" />
|
<ClCompile Include="..\source\cZombie.cpp" />
|
||||||
<ClCompile Include="..\source\cZombiepigman.cpp" />
|
<ClCompile Include="..\source\cZombiepigman.cpp" />
|
||||||
|
<ClCompile Include="..\source\FastNBT.cpp" />
|
||||||
<ClCompile Include="..\source\Globals.cpp">
|
<ClCompile Include="..\source\Globals.cpp">
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug with optimized Noise|Win32'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug with optimized Noise|Win32'">Create</PrecompiledHeader>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||||
@ -575,6 +576,7 @@
|
|||||||
<ClInclude Include="..\source\cZombie.h" />
|
<ClInclude Include="..\source\cZombie.h" />
|
||||||
<ClInclude Include="..\source\cZombiepigman.h" />
|
<ClInclude Include="..\source\cZombiepigman.h" />
|
||||||
<ClInclude Include="..\source\Endianness.h" />
|
<ClInclude Include="..\source\Endianness.h" />
|
||||||
|
<ClInclude Include="..\source\FastNBT.h" />
|
||||||
<ClInclude Include="..\Source\FileDefine.h" />
|
<ClInclude Include="..\Source\FileDefine.h" />
|
||||||
<ClInclude Include="..\source\Globals.h" />
|
<ClInclude Include="..\source\Globals.h" />
|
||||||
<ClInclude Include="..\source\LeakFinder.h" />
|
<ClInclude Include="..\source\LeakFinder.h" />
|
||||||
|
@ -445,6 +445,9 @@
|
|||||||
<Filter Include="Simulator\cSimulator\cRedstoneSimulator">
|
<Filter Include="Simulator\cSimulator\cRedstoneSimulator">
|
||||||
<UniqueIdentifier>{4b3b7b43-8e8b-4823-b112-2259fdfff7d3}</UniqueIdentifier>
|
<UniqueIdentifier>{4b3b7b43-8e8b-4823-b112-2259fdfff7d3}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter Include="Storage">
|
||||||
|
<UniqueIdentifier>{038cf4bd-108e-44e2-bdcb-9d48fb26676e}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\source\cServer.cpp">
|
<ClCompile Include="..\source\cServer.cpp">
|
||||||
@ -887,20 +890,31 @@
|
|||||||
<ClCompile Include="..\source\cSocketThreads.cpp" />
|
<ClCompile Include="..\source\cSocketThreads.cpp" />
|
||||||
<ClCompile Include="..\source\LeakFinder.cpp" />
|
<ClCompile Include="..\source\LeakFinder.cpp" />
|
||||||
<ClCompile Include="..\source\StackWalker.cpp" />
|
<ClCompile Include="..\source\StackWalker.cpp" />
|
||||||
<ClCompile Include="..\source\WorldStorage.cpp" />
|
|
||||||
<ClCompile Include="..\source\WSSCompact.cpp" />
|
|
||||||
<ClCompile Include="..\source\StringCompression.cpp" />
|
<ClCompile Include="..\source\StringCompression.cpp" />
|
||||||
<ClCompile Include="..\source\LightingThread.cpp" />
|
<ClCompile Include="..\source\LightingThread.cpp" />
|
||||||
<ClCompile Include="..\source\cRedstoneSimulator.cpp">
|
<ClCompile Include="..\source\cRedstoneSimulator.cpp">
|
||||||
<Filter>Simulator\cSimulator\cRedstoneSimulator</Filter>
|
<Filter>Simulator\cSimulator\cRedstoneSimulator</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\source\ChunkSender.cpp" />
|
<ClCompile Include="..\source\ChunkSender.cpp" />
|
||||||
<ClCompile Include="..\source\WSSAnvil.cpp" />
|
|
||||||
<ClCompile Include="..\source\NBT.cpp" />
|
|
||||||
<ClCompile Include="..\source\WGFlat.cpp" />
|
<ClCompile Include="..\source\WGFlat.cpp" />
|
||||||
<ClCompile Include="..\source\packets\cPacket_Player.cpp">
|
<ClCompile Include="..\source\packets\cPacket_Player.cpp">
|
||||||
<Filter>Packets</Filter>
|
<Filter>Packets</Filter>
|
||||||
</ClCompile>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\source\cServer.h">
|
<ClInclude Include="..\source\cServer.h">
|
||||||
@ -1379,23 +1393,34 @@
|
|||||||
<ClInclude Include="..\source\StringUtils.h" />
|
<ClInclude Include="..\source\StringUtils.h" />
|
||||||
<ClInclude Include="..\source\cIsThread.h" />
|
<ClInclude Include="..\source\cIsThread.h" />
|
||||||
<ClInclude Include="..\source\cSocketThreads.h" />
|
<ClInclude Include="..\source\cSocketThreads.h" />
|
||||||
<ClInclude Include="..\source\WSSCompact.h" />
|
|
||||||
<ClInclude Include="..\source\LeakFinder.h" />
|
<ClInclude Include="..\source\LeakFinder.h" />
|
||||||
<ClInclude Include="..\source\StackWalker.h" />
|
<ClInclude Include="..\source\StackWalker.h" />
|
||||||
<ClInclude Include="..\source\WorldStorage.h" />
|
|
||||||
<ClInclude Include="..\source\StringCompression.h" />
|
<ClInclude Include="..\source\StringCompression.h" />
|
||||||
<ClInclude Include="..\source\LightingThread.h" />
|
<ClInclude Include="..\source\LightingThread.h" />
|
||||||
<ClInclude Include="..\source\cRedstoneSimulator.h">
|
<ClInclude Include="..\source\cRedstoneSimulator.h">
|
||||||
<Filter>Simulator\cSimulator\cRedstoneSimulator</Filter>
|
<Filter>Simulator\cSimulator\cRedstoneSimulator</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\source\ChunkSender.h" />
|
<ClInclude Include="..\source\ChunkSender.h" />
|
||||||
<ClInclude Include="..\source\WSSAnvil.h" />
|
|
||||||
<ClInclude Include="..\source\NBT.h" />
|
|
||||||
<ClInclude Include="..\source\WGFlat.h" />
|
<ClInclude Include="..\source\WGFlat.h" />
|
||||||
<ClInclude Include="..\source\ChunkDef.h" />
|
<ClInclude Include="..\source\ChunkDef.h" />
|
||||||
<ClInclude Include="..\source\packets\cPacket_Player.h">
|
<ClInclude Include="..\source\packets\cPacket_Player.h">
|
||||||
<Filter>Packets</Filter>
|
<Filter>Packets</Filter>
|
||||||
</ClInclude>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\source\AllToLua.pkg">
|
<None Include="..\source\AllToLua.pkg">
|
||||||
|
320
source/FastNBT.cpp
Normal file
320
source/FastNBT.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
234
source/FastNBT.h
Normal file
234
source/FastNBT.h
Normal file
@ -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;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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
|
// 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
|
// This should be used in the private: declarations for any class that shouldn't allow copying itself
|
||||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||||
|
@ -13,14 +13,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef long long Int64;
|
|
||||||
typedef int Int32;
|
|
||||||
typedef short Int16;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Representation classes:
|
// Representation classes:
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "cEntity.h"
|
#include "cEntity.h"
|
||||||
#include "cBlockEntity.h"
|
#include "cBlockEntity.h"
|
||||||
#include "cMakeDir.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())));
|
Data->Add(new cNBTInt(Data, "SpawnZ", (int)(a_World->GetSpawnZ())));
|
||||||
AString Uncompressed;
|
AString Uncompressed;
|
||||||
cNBTSerializer::Serialize(Root.get(), Uncompressed);
|
cNBTSerializer::Serialize(Root.get(), Uncompressed);
|
||||||
|
|
||||||
gzFile gz = gzopen(fnam.c_str(), "wb");
|
gzFile gz = gzopen(fnam.c_str(), "wb");
|
||||||
if (gz != NULL)
|
if (gz != NULL)
|
||||||
{
|
{
|
||||||
@ -304,14 +306,15 @@ bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString &
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the NBT data:
|
// Parse the NBT data:
|
||||||
std::auto_ptr<cNBTTree> Tree(cNBTParser::Parse(Uncompressed, strm.total_out));
|
cParsedNBT NBT(Uncompressed, strm.total_out);
|
||||||
if (Tree.get() == NULL)
|
if (!NBT.IsValid())
|
||||||
{
|
{
|
||||||
|
// NBT Parsing failed
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the data from NBT:
|
// 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)
|
// The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data)
|
||||||
BLOCKTYPE BlockData[cChunkDef::BlockDataSize];
|
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
|
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:
|
// Load the blockdata, blocklight and skylight:
|
||||||
cNBTList * Sections = (cNBTList *)a_NBT.FindChildByPath("Level\\Sections");
|
int Level = a_NBT.FindChildByName(0, "Level");
|
||||||
if ((Sections == NULL) || (Sections->GetType() != cNBTTag::TAG_List) || (Sections->GetChildrenType() != cNBTTag::TAG_Compound))
|
if (Level < 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const cNBTTags & LevelSections = Sections->GetChildren();
|
int Sections = a_NBT.FindChildByName(Level, "Sections");
|
||||||
for (cNBTTags::const_iterator itr = LevelSections.begin(); itr != LevelSections.end(); ++itr)
|
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;
|
int y = 0;
|
||||||
cNBTByte * SectionY = (cNBTByte *)((*itr)->FindChildByName("Y"));
|
int SectionY = a_NBT.FindChildByName(Child, "Y");
|
||||||
if ((SectionY == NULL) || (SectionY->GetType() != cNBTTag::TAG_Byte) || (SectionY->m_Value < 0) || (SectionY->m_Value > 15))
|
if ((SectionY < 0) || (a_NBT.GetType(SectionY) != TAG_Byte))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
y = SectionY->m_Value;
|
y = a_NBT.GetByte(SectionY);
|
||||||
cNBTByteArray * baBlocks = (cNBTByteArray *)((*itr)->FindChildByName("Blocks"));
|
if ((y < 0) || (y > 15))
|
||||||
if ((baBlocks != NULL) && (baBlocks->GetType() == cNBTTag::TAG_ByteArray) && (baBlocks->m_Value.size() == 4096))
|
|
||||||
{
|
{
|
||||||
memcpy(&(BlockData[y * 4096]), baBlocks->m_Value.data(), 4096);
|
continue;
|
||||||
}
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
memcpy(&(BlockLight[y * 2048]), baBlockLight->m_Value.data(), 2048);
|
|
||||||
}
|
}
|
||||||
|
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[]
|
} // for itr - LevelSections[]
|
||||||
|
|
||||||
cEntityList Entities;
|
cEntityList Entities;
|
||||||
cBlockEntityList BlockEntities;
|
cBlockEntityList BlockEntities;
|
||||||
|
|
||||||
// Load the entities from NBT:
|
// Load the entities from NBT:
|
||||||
LoadEntitiesFromNBT (Entities, (cNBTList *)(a_NBT.FindChildByPath("Level\\Entities")));
|
LoadEntitiesFromNBT (Entities, a_NBT, a_NBT.FindChildByName(Level, "Entities"));
|
||||||
LoadBlockEntitiesFromNBT(BlockEntities, (cNBTList *)(a_NBT.FindChildByPath("Level\\TileEntities")));
|
LoadBlockEntitiesFromNBT(BlockEntities, a_NBT, a_NBT.FindChildByName(Level, "TileEntities"));
|
||||||
|
|
||||||
#if (AXIS_ORDER == AXIS_ORDER_YZX)
|
#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:
|
// 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)
|
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
|
// 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;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
cNBTString * sID = (cNBTString *)((*itr)->FindChildByName("id"));
|
int sID = a_NBT.FindChildByName(Child, "id");
|
||||||
if (sID == NULL)
|
if (sID < 0)
|
||||||
{
|
{
|
||||||
continue;
|
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
|
// 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(a_TagIdx) == TAG_Compound);
|
||||||
ASSERT(a_NBT->GetType() == cNBTTag::TAG_Compound);
|
|
||||||
int x, y, z;
|
int x, y, z;
|
||||||
if (!GetBlockEntityNBTPos(a_NBT, x, y, z))
|
if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cNBTList * Items = (cNBTList *)(a_NBT->FindChildByName("Items"));
|
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
|
||||||
if ((Items == NULL) || (Items->GetType() != cNBTTag::TAG_List))
|
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));
|
std::auto_ptr<cChestEntity> Chest(new cChestEntity(x, y, z, m_World));
|
||||||
const cNBTTags & ItemDefs = Items->GetChildren();
|
for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child))
|
||||||
for (cNBTTags::const_iterator itr = ItemDefs.begin(); itr != ItemDefs.end(); ++itr)
|
|
||||||
{
|
{
|
||||||
cNBTByte * Slot = (cNBTByte *)((*itr)->FindChildByName("Slot"));
|
int Slot = a_NBT.FindChildByName(Child, "Slot");
|
||||||
if ((Slot == NULL) || (Slot->GetType() != cNBTTag::TAG_Byte))
|
if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
cItem Item;
|
cItem Item;
|
||||||
cNBTShort * ID = (cNBTShort *)((*itr)->FindChildByName("id"));
|
int ID = a_NBT.FindChildByName(Child, "id");
|
||||||
if ((ID == NULL) || (ID->GetType() != cNBTTag::TAG_Short))
|
if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Item.m_ItemID = (ENUM_ITEM_ID)(ID->m_Value);
|
Item.m_ItemID = (ENUM_ITEM_ID)(a_NBT.GetShort(ID));
|
||||||
cNBTShort * Damage = (cNBTShort *)((*itr)->FindChildByName("Damage"));
|
int Damage = a_NBT.FindChildByName(Child, "Damage");
|
||||||
if ((Damage == NULL) || (Damage->GetType() != cNBTTag::TAG_Short))
|
if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Item.m_ItemHealth = Damage->m_Value;
|
Item.m_ItemHealth = a_NBT.GetShort(Damage);
|
||||||
cNBTByte * Count = (cNBTByte *)((*itr)->FindChildByName("Count"));
|
int Count = a_NBT.FindChildByName(Child, "Count");
|
||||||
if ((Count == NULL) || (Count->GetType() != cNBTTag::TAG_Byte))
|
if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Item.m_ItemCount = Count->m_Value;
|
Item.m_ItemCount = a_NBT.GetByte(Count);
|
||||||
Chest->SetSlot(Slot->m_Value, Item);
|
Chest->SetSlot(a_NBT.GetByte(Slot), Item);
|
||||||
} // for itr - ItemDefs[]
|
} // for itr - ItemDefs[]
|
||||||
a_BlockEntities.push_back(Chest.release());
|
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"));
|
int x = a_NBT.FindChildByName(a_TagIdx, "x");
|
||||||
if ((x == NULL) || (x->GetType() != cNBTTag::TAG_Int))
|
if ((x < 0) || (a_NBT.GetType(x) != TAG_Int))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
cNBTInt * y = (cNBTInt *)(a_NBT->FindChildByName("y"));
|
int y = a_NBT.FindChildByName(a_TagIdx, "y");
|
||||||
if ((y == NULL) || (y->GetType() != cNBTTag::TAG_Int))
|
if ((y < 0) || (a_NBT.GetType(y) != TAG_Int))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
cNBTInt * z = (cNBTInt *)(a_NBT->FindChildByName("z"));
|
int z = a_NBT.FindChildByName(a_TagIdx, "z");
|
||||||
if ((z == NULL) || (z->GetType() != cNBTTag::TAG_Int))
|
if ((z < 0) || (a_NBT.GetType(z) != TAG_Int))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
a_X = x->m_Value;
|
a_X = a_NBT.GetInt(x);
|
||||||
a_Y = y->m_Value;
|
a_Y = a_NBT.GetInt(y);
|
||||||
a_Z = z->m_Value;
|
a_Z = a_NBT.GetInt(z);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "WorldStorage.h"
|
#include "WorldStorage.h"
|
||||||
|
#include "FastNBT.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -99,24 +100,27 @@ protected:
|
|||||||
bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data);
|
bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data);
|
||||||
|
|
||||||
/// Loads the chunk from NBT data (no locking needed)
|
/// 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
|
/// Saves the chunk into NBT data; returns NULL for failure
|
||||||
cNBTTag * SaveChunkToNBT(const cChunkCoords & a_Chunk);
|
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)
|
/// 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 cNBTList * a_NBT);
|
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)
|
/// 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 cNBTList * a_NBT);
|
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
|
/// 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
|
/// 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);
|
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:
|
// cWSSchema overrides:
|
||||||
virtual bool LoadChunk(const cChunkCoords & a_Chunk) override;
|
virtual bool LoadChunk(const cChunkCoords & a_Chunk) override;
|
||||||
|
Loading…
Reference in New Issue
Block a user