2012-06-14 09:06:06 -04:00
|
|
|
|
|
|
|
// FastNBT.cpp
|
|
|
|
|
|
|
|
// Implements the fast NBT parser and writer
|
|
|
|
|
|
|
|
#include "Globals.h"
|
|
|
|
#include "FastNBT.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-07-17 16:50:58 -04:00
|
|
|
// The number of NBT tags that are reserved when an NBT parsing is started.
|
2012-10-05 04:33:25 -04:00
|
|
|
// You can override this by using a cmdline define
|
|
|
|
#ifndef NBT_RESERVE_SIZE
|
|
|
|
#define NBT_RESERVE_SIZE 200
|
|
|
|
#endif // NBT_RESERVE_SIZE
|
|
|
|
|
2014-01-07 08:24:25 -05:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
// Dodge a C4127 (conditional expression is constant) for this specific macro usage
|
2017-07-30 12:55:19 -04:00
|
|
|
#define PROPAGATE_ERROR(X) do { auto Err = (X); if (Err != eNBTParseError::npSuccess) return Err; } while ((false, false))
|
2014-01-07 08:24:25 -05:00
|
|
|
#else
|
2017-07-30 12:55:19 -04:00
|
|
|
#define PROPAGATE_ERROR(X) do { auto Err = (X); if (Err != eNBTParseError::npSuccess) return Err; } while (false)
|
2014-01-07 08:24:25 -05:00
|
|
|
#endif
|
2012-06-14 09:06:06 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-30 12:55:19 -04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// cNBTParseErrorCategory:
|
|
|
|
|
2018-02-04 18:07:12 -05:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
class cNBTParseErrorCategory final :
|
|
|
|
public std::error_category
|
|
|
|
{
|
|
|
|
cNBTParseErrorCategory() = default;
|
|
|
|
public:
|
|
|
|
/** Category name */
|
2020-05-10 12:16:38 -04:00
|
|
|
virtual const char * name() const noexcept override
|
2018-02-04 18:07:12 -05:00
|
|
|
{
|
|
|
|
return "NBT parse error";
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Maps a parse error code to an error message */
|
|
|
|
virtual AString message(int a_Condition) const override;
|
|
|
|
|
|
|
|
/** Returns the canonical error category instance. */
|
2020-05-10 12:16:38 -04:00
|
|
|
static const cNBTParseErrorCategory & Get() noexcept
|
2018-02-04 18:07:12 -05:00
|
|
|
{
|
|
|
|
static cNBTParseErrorCategory Category;
|
|
|
|
return Category;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-30 12:55:19 -04:00
|
|
|
AString cNBTParseErrorCategory::message(int a_Condition) const
|
|
|
|
{
|
|
|
|
switch (static_cast<eNBTParseError>(a_Condition))
|
|
|
|
{
|
|
|
|
case eNBTParseError::npSuccess:
|
|
|
|
{
|
|
|
|
return "Parsing succeded";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npNeedBytes:
|
|
|
|
{
|
|
|
|
return "Expected more data";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npNoTopLevelCompound:
|
|
|
|
{
|
|
|
|
return "No top level compound tag";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npStringMissingLength:
|
|
|
|
{
|
|
|
|
return "Expected a string length but had insufficient data";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npStringInvalidLength:
|
|
|
|
{
|
|
|
|
return "String length invalid";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npCompoundImbalancedTag:
|
|
|
|
{
|
|
|
|
return "Compound tag was unmatched at end of file";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npListMissingType:
|
|
|
|
{
|
|
|
|
return "Expected a list type but had insuffiecient data";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npListMissingLength:
|
|
|
|
{
|
|
|
|
return "Expected a list length but had insufficient data";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npListInvalidLength:
|
|
|
|
{
|
|
|
|
return "List length invalid";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npSimpleMissing:
|
|
|
|
{
|
|
|
|
return "Expected a numeric type but had insufficient data";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npArrayMissingLength:
|
|
|
|
{
|
|
|
|
return "Expected an array length but had insufficient data";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npArrayInvalidLength:
|
|
|
|
{
|
|
|
|
return "Array length invalid";
|
|
|
|
}
|
|
|
|
case eNBTParseError::npUnknownTag:
|
|
|
|
{
|
|
|
|
return "Unknown tag";
|
|
|
|
}
|
2018-02-04 18:07:12 -05:00
|
|
|
}
|
|
|
|
UNREACHABLE("Unsupported nbt parse error");
|
|
|
|
}
|
2017-07-30 12:55:19 -04:00
|
|
|
|
2018-02-04 18:07:12 -05:00
|
|
|
} // namespace (anonymous)
|
2017-07-30 12:55:19 -04:00
|
|
|
|
|
|
|
|
2018-02-04 18:07:12 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2020-05-10 12:16:38 -04:00
|
|
|
std::error_code make_error_code(eNBTParseError a_Err) noexcept
|
2018-02-04 18:07:12 -05:00
|
|
|
{
|
|
|
|
return { static_cast<int>(a_Err), cNBTParseErrorCategory::Get() };
|
2017-07-30 12:55:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-07-17 16:15:34 -04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2012-06-14 09:06:06 -04:00
|
|
|
// cParsedNBT:
|
|
|
|
|
2017-07-30 12:55:19 -04:00
|
|
|
#define NEEDBYTES(N, ERR) \
|
2019-08-11 05:39:43 -04:00
|
|
|
do { \
|
2021-01-11 11:39:43 -05:00
|
|
|
if (m_Data.size() - m_Pos < static_cast<size_t>(N)) \
|
2019-08-11 05:39:43 -04:00
|
|
|
{ \
|
|
|
|
return ERR; \
|
|
|
|
} \
|
|
|
|
} while (false)
|
2012-06-14 09:06:06 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
cParsedNBT::cParsedNBT(const ContiguousByteBufferView a_Data) :
|
2012-06-14 09:06:06 -04:00
|
|
|
m_Data(a_Data),
|
|
|
|
m_Pos(0)
|
|
|
|
{
|
2017-07-30 12:55:19 -04:00
|
|
|
m_Error = Parse();
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-30 12:55:19 -04:00
|
|
|
eNBTParseError cParsedNBT::Parse(void)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
if (m_Data.size() < 3)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
// Data too short
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npNeedBytes;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2021-01-11 11:39:43 -05:00
|
|
|
if (m_Data[0] != std::byte(TAG_Compound))
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
// The top-level tag must be a Compound
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npNoTopLevelCompound;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-10-05 04:33:25 -04:00
|
|
|
m_Tags.reserve(NBT_RESERVE_SIZE);
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2017-07-30 12:55:19 -04:00
|
|
|
m_Tags.emplace_back(TAG_Compound, -1);
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
m_Pos = 1;
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2017-07-30 12:55:19 -04:00
|
|
|
PROPAGATE_ERROR(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength));
|
|
|
|
return ReadCompound();
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-30 12:55:19 -04:00
|
|
|
eNBTParseError cParsedNBT::ReadString(size_t & a_StringStart, size_t & a_StringLen)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2017-07-30 12:55:19 -04:00
|
|
|
NEEDBYTES(2, eNBTParseError::npStringMissingLength);
|
2012-06-14 09:06:06 -04:00
|
|
|
a_StringStart = m_Pos + 2;
|
2021-01-11 11:39:43 -05:00
|
|
|
a_StringLen = static_cast<size_t>(GetBEShort(m_Data.data() + m_Pos));
|
2017-07-30 12:55:19 -04:00
|
|
|
NEEDBYTES(2 + a_StringLen, eNBTParseError::npStringInvalidLength);
|
2012-06-14 09:06:06 -04:00
|
|
|
m_Pos += 2 + a_StringLen;
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npSuccess;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-30 12:55:19 -04:00
|
|
|
eNBTParseError cParsedNBT::ReadCompound(void)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2014-05-08 14:16:35 -04:00
|
|
|
ASSERT(m_Tags.size() > 0);
|
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
// Reads the latest tag as a compound
|
2015-01-20 17:52:53 -05:00
|
|
|
size_t ParentIdx = m_Tags.size() - 1;
|
2012-06-14 09:06:06 -04:00
|
|
|
int PrevSibling = -1;
|
2014-01-07 08:24:25 -05:00
|
|
|
for (;;)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2017-07-30 12:55:19 -04:00
|
|
|
NEEDBYTES(1, eNBTParseError::npCompoundImbalancedTag);
|
2021-01-11 11:39:43 -05:00
|
|
|
const auto TagTypeNum = m_Data[m_Pos];
|
|
|
|
if ((TagTypeNum < std::byte(TAG_Min)) || (TagTypeNum > std::byte(TAG_Max)))
|
2015-12-17 17:34:19 -05:00
|
|
|
{
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npUnknownTag;
|
2015-12-17 17:34:19 -05:00
|
|
|
}
|
|
|
|
eTagType TagType = static_cast<eTagType>(TagTypeNum);
|
2012-06-14 09:06:06 -04:00
|
|
|
m_Pos++;
|
|
|
|
if (TagType == TAG_End)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2017-07-30 12:55:19 -04:00
|
|
|
m_Tags.emplace_back(TagType, static_cast<int>(ParentIdx), PrevSibling);
|
2012-06-14 09:06:06 -04:00
|
|
|
if (PrevSibling >= 0)
|
|
|
|
{
|
2015-07-29 11:04:03 -04:00
|
|
|
m_Tags[static_cast<size_t>(PrevSibling)].m_NextSibling = static_cast<int>(m_Tags.size()) - 1;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-07-29 11:04:03 -04:00
|
|
|
m_Tags[ParentIdx].m_FirstChild = static_cast<int>(m_Tags.size()) - 1;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2015-07-29 11:04:03 -04:00
|
|
|
PrevSibling = static_cast<int>(m_Tags.size()) - 1;
|
2017-07-30 12:55:19 -04:00
|
|
|
PROPAGATE_ERROR(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength));
|
|
|
|
PROPAGATE_ERROR(ReadTag());
|
2012-06-14 09:06:06 -04:00
|
|
|
} // while (true)
|
|
|
|
m_Tags[ParentIdx].m_LastChild = PrevSibling;
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npSuccess;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-30 12:55:19 -04:00
|
|
|
eNBTParseError cParsedNBT::ReadList(eTagType a_ChildrenType)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
// Reads the latest tag as a list of items of type a_ChildrenType
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
// Read the count:
|
2017-07-30 12:55:19 -04:00
|
|
|
NEEDBYTES(4, eNBTParseError::npListMissingLength);
|
2021-01-11 11:39:43 -05:00
|
|
|
int Count = GetBEInt(m_Data.data() + m_Pos);
|
2012-06-14 09:06:06 -04:00
|
|
|
m_Pos += 4;
|
2020-04-30 02:44:49 -04:00
|
|
|
auto MinChildSize = GetMinTagSize(a_ChildrenType);
|
2021-01-11 11:39:43 -05:00
|
|
|
if ((Count < 0) || (Count > static_cast<int>((m_Data.size() - m_Pos) / MinChildSize)))
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npListInvalidLength;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Read items:
|
2015-01-20 17:52:53 -05:00
|
|
|
ASSERT(m_Tags.size() > 0);
|
|
|
|
size_t ParentIdx = m_Tags.size() - 1;
|
2012-06-14 09:06:06 -04:00
|
|
|
int PrevSibling = -1;
|
|
|
|
for (int i = 0; i < Count; i++)
|
|
|
|
{
|
2017-07-30 12:55:19 -04:00
|
|
|
m_Tags.emplace_back(a_ChildrenType, static_cast<int>(ParentIdx), PrevSibling);
|
2012-06-14 09:06:06 -04:00
|
|
|
if (PrevSibling >= 0)
|
|
|
|
{
|
2015-01-20 17:52:53 -05:00
|
|
|
m_Tags[static_cast<size_t>(PrevSibling)].m_NextSibling = static_cast<int>(m_Tags.size()) - 1;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-01-20 17:52:53 -05:00
|
|
|
m_Tags[ParentIdx].m_FirstChild = static_cast<int>(m_Tags.size()) - 1;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2015-01-20 17:52:53 -05:00
|
|
|
PrevSibling = static_cast<int>(m_Tags.size()) - 1;
|
2017-07-30 12:55:19 -04:00
|
|
|
PROPAGATE_ERROR(ReadTag());
|
2012-06-14 09:06:06 -04:00
|
|
|
} // for (i)
|
|
|
|
m_Tags[ParentIdx].m_LastChild = PrevSibling;
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npSuccess;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define CASE_SIMPLE_TAG(TAGTYPE, LEN) \
|
|
|
|
case TAG_##TAGTYPE: \
|
|
|
|
{ \
|
2017-07-30 12:55:19 -04:00
|
|
|
NEEDBYTES(LEN, eNBTParseError::npSimpleMissing); \
|
2012-06-14 09:06:06 -04:00
|
|
|
Tag.m_DataStart = m_Pos; \
|
|
|
|
Tag.m_DataLength = LEN; \
|
|
|
|
m_Pos += LEN; \
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npSuccess; \
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2017-07-30 12:55:19 -04:00
|
|
|
eNBTParseError cParsedNBT::ReadTag(void)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
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)
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
case TAG_String:
|
|
|
|
{
|
|
|
|
return ReadString(Tag.m_DataStart, Tag.m_DataLength);
|
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
case TAG_ByteArray:
|
|
|
|
{
|
2017-07-30 12:55:19 -04:00
|
|
|
NEEDBYTES(4, eNBTParseError::npArrayMissingLength);
|
2021-01-11 11:39:43 -05:00
|
|
|
int len = GetBEInt(m_Data.data() + m_Pos);
|
2012-06-14 09:06:06 -04:00
|
|
|
m_Pos += 4;
|
|
|
|
if (len < 0)
|
|
|
|
{
|
|
|
|
// Invalid length
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npArrayInvalidLength;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2017-07-30 12:55:19 -04:00
|
|
|
NEEDBYTES(len, eNBTParseError::npArrayInvalidLength);
|
2015-01-20 17:52:53 -05:00
|
|
|
Tag.m_DataLength = static_cast<size_t>(len);
|
2012-06-14 09:06:06 -04:00
|
|
|
Tag.m_DataStart = m_Pos;
|
2015-01-20 17:52:53 -05:00
|
|
|
m_Pos += static_cast<size_t>(len);
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npSuccess;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
case TAG_List:
|
|
|
|
{
|
2017-07-30 12:55:19 -04:00
|
|
|
NEEDBYTES(1, eNBTParseError::npListMissingType);
|
2015-01-20 17:52:53 -05:00
|
|
|
eTagType ItemType = static_cast<eTagType>(m_Data[m_Pos]);
|
2012-06-14 09:06:06 -04:00
|
|
|
m_Pos++;
|
2017-07-30 12:55:19 -04:00
|
|
|
PROPAGATE_ERROR(ReadList(ItemType));
|
|
|
|
return eNBTParseError::npSuccess;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
case TAG_Compound:
|
|
|
|
{
|
2017-07-30 12:55:19 -04:00
|
|
|
PROPAGATE_ERROR(ReadCompound());
|
|
|
|
return eNBTParseError::npSuccess;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
case TAG_IntArray:
|
|
|
|
{
|
2017-07-30 12:55:19 -04:00
|
|
|
NEEDBYTES(4, eNBTParseError::npArrayMissingLength);
|
2021-01-11 11:39:43 -05:00
|
|
|
int len = GetBEInt(m_Data.data() + m_Pos);
|
2012-06-14 09:06:06 -04:00
|
|
|
m_Pos += 4;
|
|
|
|
if (len < 0)
|
|
|
|
{
|
|
|
|
// Invalid length
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npArrayInvalidLength;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
len *= 4;
|
2017-07-30 12:55:19 -04:00
|
|
|
NEEDBYTES(len, eNBTParseError::npArrayInvalidLength);
|
2015-01-20 17:52:53 -05:00
|
|
|
Tag.m_DataLength = static_cast<size_t>(len);
|
2012-06-14 09:06:06 -04:00
|
|
|
Tag.m_DataStart = m_Pos;
|
2015-01-20 17:52:53 -05:00
|
|
|
m_Pos += static_cast<size_t>(len);
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npSuccess;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2015-05-19 14:32:10 -04:00
|
|
|
case TAG_Min:
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2017-07-30 12:55:19 -04:00
|
|
|
return eNBTParseError::npUnknownTag;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
} // switch (iType)
|
2018-02-04 18:07:12 -05:00
|
|
|
UNREACHABLE("Unsupported nbt tag type");
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|
2015-01-20 17:52:53 -05:00
|
|
|
if (m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_Compound)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
if (a_NameLength == 0)
|
|
|
|
{
|
|
|
|
a_NameLength = strlen(a_Name);
|
|
|
|
}
|
2015-01-20 17:52:53 -05:00
|
|
|
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)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
if (
|
2015-01-20 17:52:53 -05:00
|
|
|
(m_Tags[static_cast<size_t>(Child)].m_NameLength == a_NameLength) &&
|
2021-01-11 11:39:43 -05:00
|
|
|
(memcmp(m_Data.data() + m_Tags[static_cast<size_t>(Child)].m_NameStart, a_Name, a_NameLength) == 0)
|
2012-06-14 09:06:06 -04:00
|
|
|
)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2014-09-20 17:01:42 -04:00
|
|
|
Tag = FindChildByName(Tag, a_Path.c_str() + Begin, i - Begin);
|
2012-06-14 09:06:06 -04:00
|
|
|
if (Tag < 0)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
Begin = i + 1;
|
|
|
|
} // for i - a_Path[]
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
if (Begin < Length)
|
|
|
|
{
|
|
|
|
Tag = FindChildByName(Tag, a_Path.c_str() + Begin, Length - Begin);
|
|
|
|
}
|
|
|
|
return Tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-04-30 02:44:49 -04:00
|
|
|
size_t cParsedNBT::GetMinTagSize(eTagType a_TagType)
|
|
|
|
{
|
|
|
|
switch (a_TagType)
|
|
|
|
{
|
|
|
|
case TAG_End: return 1;
|
|
|
|
case TAG_Byte: return 1;
|
|
|
|
case TAG_Short: return 2;
|
|
|
|
case TAG_Int: return 4;
|
|
|
|
case TAG_Long: return 8;
|
|
|
|
case TAG_Float: return 4;
|
|
|
|
case TAG_Double: return 8;
|
|
|
|
case TAG_String: return 2; // 2 bytes for the string length
|
|
|
|
case TAG_ByteArray: return 4; // 4 bytes for the count
|
|
|
|
case TAG_List: return 5; // 1 byte list type + 4 bytes count
|
|
|
|
case TAG_Compound: return 1; // Single TAG_End byte
|
|
|
|
case TAG_IntArray: return 4; // 4 bytes for the count
|
|
|
|
}
|
|
|
|
UNREACHABLE("Unsupported nbt tag type");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-07-17 16:15:34 -04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2012-06-14 09:06:06 -04:00
|
|
|
// cFastNBTWriter:
|
|
|
|
|
2013-02-07 05:09:42 -05:00
|
|
|
cFastNBTWriter::cFastNBTWriter(const AString & a_RootTagName) :
|
2012-06-14 09:06:06 -04:00
|
|
|
m_CurrentStack(0)
|
|
|
|
{
|
|
|
|
m_Stack[0].m_Type = TAG_Compound;
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.reserve(100 KiB);
|
|
|
|
m_Result.push_back(std::byte(TAG_Compound));
|
|
|
|
WriteString(a_RootTagName);
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cFastNBTWriter::BeginCompound(const AString & a_Name)
|
|
|
|
{
|
2014-04-18 15:09:44 -04:00
|
|
|
if (m_CurrentStack >= MAX_STACK - 1)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
ASSERT(!"Stack overflow");
|
|
|
|
return;
|
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
TagCommon(a_Name, TAG_Compound);
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
++m_CurrentStack;
|
|
|
|
m_Stack[m_CurrentStack].m_Type = TAG_Compound;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cFastNBTWriter::EndCompound(void)
|
|
|
|
{
|
|
|
|
ASSERT(m_CurrentStack > 0);
|
|
|
|
ASSERT(IsStackTopCompound());
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.push_back(std::byte(TAG_End));
|
2012-06-14 09:06:06 -04:00
|
|
|
--m_CurrentStack;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cFastNBTWriter::BeginList(const AString & a_Name, eTagType a_ChildrenType)
|
|
|
|
{
|
2014-04-18 15:09:44 -04:00
|
|
|
if (m_CurrentStack >= MAX_STACK - 1)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
ASSERT(!"Stack overflow");
|
|
|
|
return;
|
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
TagCommon(a_Name, TAG_List);
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.push_back(std::byte(a_ChildrenType));
|
|
|
|
m_Result.append(4, std::byte(0));
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
++m_CurrentStack;
|
2013-03-09 09:35:43 -05:00
|
|
|
m_Stack[m_CurrentStack].m_Type = TAG_List;
|
2015-07-29 11:04:03 -04:00
|
|
|
m_Stack[m_CurrentStack].m_Pos = static_cast<int>(m_Result.size()) - 4;
|
2013-03-09 09:35:43 -05:00
|
|
|
m_Stack[m_CurrentStack].m_Count = 0;
|
|
|
|
m_Stack[m_CurrentStack].m_ItemType = a_ChildrenType;
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cFastNBTWriter::EndList(void)
|
|
|
|
{
|
|
|
|
ASSERT(m_CurrentStack > 0);
|
|
|
|
ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List);
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
// Update the list count:
|
2021-01-11 11:39:43 -05:00
|
|
|
SetBEInt(m_Result.data() + m_Stack[m_CurrentStack].m_Pos, m_Stack[m_CurrentStack].m_Count);
|
2012-06-14 09:06:06 -04:00
|
|
|
|
|
|
|
--m_CurrentStack;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value)
|
|
|
|
{
|
|
|
|
TagCommon(a_Name, TAG_Byte);
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.push_back(std::byte(a_Value));
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value)
|
|
|
|
{
|
|
|
|
TagCommon(a_Name, TAG_Short);
|
2016-08-24 15:45:03 -04:00
|
|
|
UInt16 Value = htons(static_cast<UInt16>(a_Value));
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(&Value), 2);
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value)
|
|
|
|
{
|
|
|
|
TagCommon(a_Name, TAG_Int);
|
2016-08-24 15:45:03 -04:00
|
|
|
UInt32 Value = htonl(static_cast<UInt32>(a_Value));
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(&Value), 4);
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value)
|
|
|
|
{
|
|
|
|
TagCommon(a_Name, TAG_Long);
|
2015-01-20 17:52:53 -05:00
|
|
|
UInt64 Value = HostToNetwork8(&a_Value);
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(&Value), 8);
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value)
|
|
|
|
{
|
|
|
|
TagCommon(a_Name, TAG_Float);
|
2015-01-20 17:52:53 -05:00
|
|
|
UInt32 Value = HostToNetwork4(&a_Value);
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(&Value), 4);
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value)
|
|
|
|
{
|
|
|
|
TagCommon(a_Name, TAG_Double);
|
2015-01-20 17:52:53 -05:00
|
|
|
UInt64 Value = HostToNetwork8(&a_Value);
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(&Value), 8);
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-08-28 16:42:18 -04:00
|
|
|
void cFastNBTWriter::AddString(const AString & a_Name, const std::string_view a_Value)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
TagCommon(a_Name, TAG_String);
|
2020-08-28 16:42:18 -04:00
|
|
|
const UInt16 Length = htons(static_cast<UInt16>(a_Value.size()));
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(&Length), sizeof(Length));
|
|
|
|
m_Result.append({ reinterpret_cast<const std::byte *>(a_Value.data()), a_Value.size() });
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements)
|
|
|
|
{
|
|
|
|
TagCommon(a_Name, TAG_ByteArray);
|
2016-08-24 15:45:03 -04:00
|
|
|
UInt32 len = htonl(static_cast<UInt32>(a_NumElements));
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(&len), 4);
|
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(a_Value), a_NumElements);
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-03-05 08:03:55 -05:00
|
|
|
void cFastNBTWriter::AddByteArray(const AString & a_Name, size_t a_NumElements, unsigned char a_Value)
|
|
|
|
{
|
|
|
|
TagCommon(a_Name, TAG_ByteArray);
|
|
|
|
UInt32 len = htonl(static_cast<UInt32>(a_NumElements));
|
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(&len), 4);
|
|
|
|
m_Result.append(a_NumElements, std::byte(a_Value));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-07-20 04:56:27 -04:00
|
|
|
void cFastNBTWriter::AddIntArray(const AString & a_Name, const Int32 * a_Value, size_t a_NumElements)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
|
|
|
TagCommon(a_Name, TAG_IntArray);
|
2016-08-24 15:45:03 -04:00
|
|
|
UInt32 len = htonl(static_cast<UInt32>(a_NumElements));
|
2014-03-10 14:24:12 -04:00
|
|
|
size_t cap = m_Result.capacity();
|
|
|
|
size_t size = m_Result.length();
|
|
|
|
if ((cap - size) < (4 + a_NumElements * 4))
|
|
|
|
{
|
2014-03-10 16:18:53 -04:00
|
|
|
m_Result.reserve(size + 4 + (a_NumElements * 4));
|
2014-03-10 14:24:12 -04:00
|
|
|
}
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(&len), sizeof(len));
|
2012-11-16 17:06:12 -05:00
|
|
|
for (size_t i = 0; i < a_NumElements; i++)
|
|
|
|
{
|
2016-08-24 15:45:03 -04:00
|
|
|
UInt32 Element = htonl(static_cast<UInt32>(a_Value[i]));
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(&Element), sizeof(Element));
|
2012-11-16 17:06:12 -05:00
|
|
|
}
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-11-16 17:06:12 -05:00
|
|
|
|
2012-06-14 09:06:06 -04:00
|
|
|
void cFastNBTWriter::Finish(void)
|
|
|
|
{
|
|
|
|
ASSERT(m_CurrentStack == 0);
|
2021-01-11 11:39:43 -05:00
|
|
|
m_Result.push_back(std::byte(TAG_End));
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
void cFastNBTWriter::WriteString(const std::string_view a_Data)
|
2012-06-14 09:06:06 -04:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
// TODO check size <= short max
|
|
|
|
UInt16 Len = htons(static_cast<unsigned short>(a_Data.size()));
|
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(&Len), sizeof(Len));
|
|
|
|
m_Result.append(reinterpret_cast<const std::byte *>(a_Data.data()), a_Data.size());
|
2012-06-14 09:06:06 -04:00
|
|
|
}
|