chenjunfu2-nbt-cpp v2.1.0
一个基于CPP20的NBT(Named Binary Tag)库
载入中...
搜索中...
未找到
NBT_Reader.hpp
浏览该文件的文档.
1#pragma once
2
3#include <new>//std::bad_alloc
4#include <bit>//std::bit_cast
5#include <vector>//字节流
6#include <stdint.h>//类型定义
7#include <stddef.h>//size_t
8#include <stdlib.h>//byte swap
9#include <utility>//std::move
10#include <type_traits>//类型约束
11
12#include "NBT_Print.hpp"//打印输出
13#include "NBT_Node.hpp"//nbt类型
14#include "NBT_Endian.hpp"//字节序
15#include "NBT_IO.hpp"//IO流对象
16
19
20
22class NBT_Reader
23{
25 NBT_Reader(void) = delete;
27 ~NBT_Reader(void) = delete;
28
29protected:
31 enum ErrCode : uint8_t
32 {
33 AllOk = 0,//没有问题
34
35 UnknownError,//其他错误(代码问题)
36 StdException,//标准异常(代码问题)
37 ListElementTypeError,//列表元素类型错误(NBT文件问题)
38 OutOfMemoryError,//内存不足错误(NBT文件问题)
39 StackDepthExceeded,//调用栈深度过深(NBT文件or代码设置问题)
40 NbtTypeTagError,//NBT标签类型错误(NBT文件问题)
41 OutOfRangeError,//(NBT内部长度错误溢出)(NBT文件问题)
42
43 ERRCODE_END,//结束标记,统计负数部分大小
44 };
45
46 constexpr static inline const char *const errReason[] =
47 {
48 "AllOk",
49 "ListElementTypeError",
50 "UnknownError",
51 "StdException",
52 "OutOfMemoryError",
53 "StackDepthExceeded",
54 "NbtTypeTagError",
55 "OutOfRangeError",
56 };
57
58 //记得同步数组!
59 static_assert(sizeof(errReason) / sizeof(errReason[0]) == ERRCODE_END, "errReason array out sync");
60
61 enum WarnCode : uint8_t
62 {
63 NoWarn = 0,
64
65 ElementExistsWarn,
66
67 WARNCODE_END,
68 };
69
70 constexpr static inline const char *const warnReason[] =//正常数组,直接用WarnCode访问
71 {
72 "NoWarn",
73
74 "ElementExistsWarn",
75 };
76
77 //记得同步数组!
78 static_assert(sizeof(warnReason) / sizeof(warnReason[0]) == WARNCODE_END, "warnReason array out sync");
79
80 //error处理
81 //使用变参形参表+vprintf代理复杂输出,给出更多扩展信息
82 //主动检查引发的错误,主动调用eRet = Error报告,然后触发STACK_TRACEBACK,最后返回eRet到上一级
83 //上一级返回的错误通过if (eRet != AllOk)判断的,直接触发STACK_TRACEBACK后返回eRet到上一级
84 //如果是警告值,则不返回值
85 template <typename T, typename InputStream, typename InfoFunc, typename... Args>
86 requires(std::is_same_v<T, ErrCode> || std::is_same_v<T, WarnCode>)
87 static std::conditional_t<std::is_same_v<T, ErrCode>, ErrCode, void> Error
88 (
89 const T &code,
90 const InputStream &tData,
91 InfoFunc &funcInfo,
92 const std::format_string<Args...> fmt,
93 Args&&... args
94 ) noexcept
95 {
97
98 //打印错误原因
99 if constexpr (std::is_same_v<T, ErrCode>)
100 {
102 if (code >= ERRCODE_END)
103 {
104 return code;
105 }
106 //上方if保证code不会溢出
107 funcInfo(lvl, "Read Err[{}]: {}\n", (uint8_t)code, errReason[code]);
108 }
109 else if constexpr (std::is_same_v<T, WarnCode>)
110 {
112 if (code >= WARNCODE_END)
113 {
114 return;
115 }
116 //上方if保证code不会溢出
117 funcInfo(lvl, "Read Warn[{}]: {}\n", (uint8_t)code, warnReason[code]);
118 }
119 else
120 {
121 static_assert(false, "Unknown [T code] Type!");
122 }
123
124 //打印扩展信息
125 funcInfo(lvl, "Extra Info: \"");
126 funcInfo(lvl, std::move(fmt), std::forward<Args>(args)...);
127 funcInfo(lvl, "\"\n\n");
128
129 //如果可以,预览szCurrent前后n个字符,否则裁切到边界
130#define VIEW_PRE (4 * 8 + 3)//向前
131#define VIEW_SUF (4 * 8 + 5)//向后
132 size_t rangeBeg = (tData.Index() > VIEW_PRE) ? (tData.Index() - VIEW_PRE) : (0);//上边界裁切
133 size_t rangeEnd = ((tData.Index() + VIEW_SUF) < tData.Size()) ? (tData.Index() + VIEW_SUF) : (tData.Size());//下边界裁切
134#undef VIEW_SUF
135#undef VIEW_PRE
136 //输出信息
137 funcInfo
138 (
139 lvl,
140 "Data Review:\n"\
141 "Current: 0x{:02X}({})\n"\
142 "Data Size: 0x{:02X}({})\n"\
143 "Data Range: [0x{:02X}({}),0x{:02X}({})):\n",
144
145 (uint64_t)tData.Index(), tData.Index(),
146 (uint64_t)tData.Size(), tData.Size(),
147 (uint64_t)rangeBeg, rangeBeg,
148 (uint64_t)rangeEnd, rangeEnd
149 );
150
151 //打数据
152 for (size_t i = rangeBeg; i < rangeEnd; ++i)
153 {
154 if ((i - rangeBeg) % 8 == 0)//输出地址
155 {
156 if (i != rangeBeg)//除去第一个每8个换行
157 {
158 funcInfo(lvl, "\n");
159 }
160 funcInfo(lvl, "0x{:02X}: ", (uint64_t)i);
161 }
162
163 if (i != tData.Index())
164 {
165 funcInfo(lvl, " {:02X} ", (uint8_t)tData[i]);
166 }
167 else//如果是当前出错字节,加方括号框起
168 {
169 funcInfo(lvl, "[{:02X}]", (uint8_t)tData[i]);
170 }
171 }
172
173 //输出提示信息
174 if constexpr (std::is_same_v<T, ErrCode>)
175 {
176 funcInfo(lvl, "\nSkip err data and return...\n\n");
177 }
178 else if constexpr (std::is_same_v<T, WarnCode>)
179 {
180 funcInfo(lvl, "\nSkip warn data and continue...\n\n");
181 }
182 else
183 {
184 static_assert(false, "Unknown [T code] Type!");
185 }
186
187 //警告不返回值
188 if constexpr (std::is_same_v<T, ErrCode>)
189 {
190 return code;
191 }
192 }
193
194
195#define _RP___FUNCTION__ __FUNCTION__//用于编译过程二次替换达到函数内部
196
197#define _RP___LINE__ _RP_STRLING(__LINE__)
198#define _RP_STRLING(l) STRLING(l)
199#define STRLING(l) #l
200
201#define STACK_TRACEBACK(fmt, ...) funcInfo(NBT_Print_Level::Err, "In [{}] Line:[" _RP___LINE__ "]: \n" fmt "\n\n", _RP___FUNCTION__ __VA_OPT__(,) __VA_ARGS__);
202#define CHECK_STACK_DEPTH(Depth) \
203if((Depth) <= 0)\
204{\
205 eRet = Error(StackDepthExceeded, tData, funcInfo, "{}: NBT nesting depth exceeded maximum call stack limit", _RP___FUNCTION__);\
206 STACK_TRACEBACK("(Depth) <= 0");\
207 return eRet;\
208}\
209
210#define MYTRY \
211try\
212{
213
214#define MYCATCH \
215}\
216catch(const std::bad_alloc &e)\
217{\
218 ErrCode eRet = Error(OutOfMemoryError, tData, funcInfo, "{}: Info:[{}]", _RP___FUNCTION__, e.what());\
219 STACK_TRACEBACK("catch(std::bad_alloc)");\
220 return eRet;\
221}\
222catch(const std::exception &e)\
223{\
224 ErrCode eRet = Error(StdException, tData, funcInfo, "{}: Info:[{}]", _RP___FUNCTION__, e.what());\
225 STACK_TRACEBACK("catch(std::exception)");\
226 return eRet;\
227}\
228catch(...)\
229{\
230 ErrCode eRet = Error(UnknownError, tData, funcInfo, "{}: Info:[Unknown Exception]", _RP___FUNCTION__);\
231 STACK_TRACEBACK("catch(...)");\
232 return eRet;\
233}
234
235 //读取大端序数值,bNoCheck为true则不进行任何检查
236 template<bool bNoCheck = false, typename T, typename InputStream, typename InfoFunc>
237 requires std::integral<T>
238 static inline std::conditional_t<bNoCheck, void, ErrCode> ReadBigEndian(InputStream &tData, T &tVal, InfoFunc &funcInfo) noexcept
239 {
240 if constexpr (!bNoCheck)
241 {
242 if (!tData.HasAvailData(sizeof(T)))
243 {
244 ErrCode eRet = Error(OutOfRangeError, tData, funcInfo, "tData size [{}], current index [{}], remaining data size [{}], but try to read [{}]",
245 tData.Size(), tData.Index(), tData.Size() - tData.Index(), sizeof(T));
246 STACK_TRACEBACK("HasAvailData Test");
247 return eRet;
248 }
249 }
250
251 T BigEndianVal{};
252 tData.GetRange((uint8_t *)&BigEndianVal, sizeof(BigEndianVal));
253 tVal = NBT_Endian::BigToNativeAny(BigEndianVal);
254
255 if constexpr (!bNoCheck)
256 {
257 return AllOk;
258 }
259 }
260
261 template<typename InputStream, typename InfoFunc>
262 static ErrCode GetName(InputStream &tData, NBT_Type::String &tName, InfoFunc &funcInfo) noexcept
263 {
264 MYTRY;
265 ErrCode eRet = AllOk;
266 //读取2字节的无符号名称长度
267 NBT_Type::StringLength wStringLength = 0;//w->word=2*byte
268 eRet = ReadBigEndian(tData, wStringLength, funcInfo);
269 if (eRet != AllOk)
270 {
271 STACK_TRACEBACK("wStringLength Read");
272 return eRet;
273 }
274
275 //判断长度是否超过
276 if (!tData.HasAvailData(wStringLength))
277 {
278 ErrCode eRet = Error(OutOfRangeError, tData, funcInfo, "{}:\n(Index[{}] + wStringLength[{}])[{}] > DataSize[{}]", __FUNCTION__,
279 tData.Index(), (size_t)wStringLength, tData.Index() + (size_t)wStringLength, tData.Size());
280 STACK_TRACEBACK("HasAvailData Test");
281 return eRet;
282 }
283
284
285 //解析出名称
286 tName.reserve(wStringLength);//提前分配
287 tName.assign((const NBT_Type::String::value_type *)tData.CurData(), wStringLength);//构造string(如果长度为0则构造0长字符串,合法行为)
288 tData.AddIndex(wStringLength);//移动下标
289
290 return eRet;
291 MYCATCH;
292 }
293
294 template<typename T, typename InputStream, typename InfoFunc>
295 static ErrCode GetBuiltInType(InputStream &tData, T &tBuiltIn, InfoFunc &funcInfo) noexcept
296 {
297 ErrCode eRet = AllOk;
298
299 //读取数据
300 using RAW_DATA_T = NBT_Type::BuiltinRawType_T<T>;//类型映射
301
302 //临时存储,因为可能存在跨类型转换
303 RAW_DATA_T tTmpRawData = 0;
304 eRet = ReadBigEndian(tData, tTmpRawData, funcInfo);
305 if (eRet != AllOk)
306 {
307 STACK_TRACEBACK("tTmpRawData Read");
308 return eRet;
309 }
310
311 //转换并返回
312 tBuiltIn = std::move(std::bit_cast<T>(tTmpRawData));
313 return eRet;
314 }
315
316 template<typename T, typename InputStream, typename InfoFunc>
317 static ErrCode GetArrayType(InputStream &tData, T &tArray, InfoFunc &funcInfo) noexcept
318 {
319 MYTRY;
320 ErrCode eRet = AllOk;
321
322 //获取4字节有符号数,代表数组元素个数
323 NBT_Type::ArrayLength iElementCount = 0;//4byte
324 eRet = ReadBigEndian(tData, iElementCount, funcInfo);
325 if (eRet != AllOk)
326 {
327 STACK_TRACEBACK("iElementCount Read");
328 return eRet;
329 }
330
331 using ValueType = typename T::value_type;
332
333 //判断长度是否超过
334 if (!tData.HasAvailData(iElementCount * sizeof(ValueType)))//保证下方调用安全
335 {
336 eRet = Error(OutOfRangeError, tData, funcInfo, "{}:\n(Index[{}] + iElementCount[{}] * sizeof(T::value_type)[{}])[{}] > DataSize[{}]", __FUNCTION__,
337 tData.Index(), (size_t)iElementCount, sizeof(ValueType), tData.Index() + (size_t)iElementCount * sizeof(typename T::value_type), tData.Size());
338 STACK_TRACEBACK("HasAvailData Test");
339 return eRet;
340 }
341
342 //数组保存
343 tArray.reserve(iElementCount);//提前扩容
344 //读取dElementCount个元素
345 for (NBT_Type::ArrayLength i = 0; i < iElementCount; ++i)
346 {
347 ValueType tTmpData{};
348 ReadBigEndian<true>(tData, tTmpData, funcInfo);//调用需要确保范围安全
349 tArray.emplace_back(std::move(tTmpData));//读取一个插入一个
350 }
351
352 return eRet;
353 MYCATCH;
354 }
355
356 //如果是非根部,有额外检测
357 template<bool bRoot, bool bUnwrapMixedList, typename InputStream, typename InfoFunc>
358 static ErrCode GetCompoundType(InputStream &tData, NBT_Type::Compound &tCompound, size_t szStackDepth, InfoFunc &funcInfo) noexcept
359 {
360 MYTRY;
361 ErrCode eRet = AllOk;
362 CHECK_STACK_DEPTH(szStackDepth);
363
364 //读取
365 while (true)
366 {
367 //处理末尾情况
368 if (!tData.HasAvailData(sizeof(NBT_TAG_RAW_TYPE)))
369 {
370 if constexpr (!bRoot)//非根部情况遇到末尾,则报错
371 {
372 eRet = Error(OutOfRangeError, tData, funcInfo, "{}:\nIndex[{}] >= DataSize()[{}]", __FUNCTION__,
373 tData.Index(), tData.Size());
374 }
375
376 return eRet;//否则直接返回(默认值AllOk)
377 }
378
379 //先读取一下类型
380 NBT_TAG tagNbt = (NBT_TAG)(NBT_TAG_RAW_TYPE)tData.GetNext();
381 if (tagNbt == NBT_TAG::End)//处理End情况
382 {
383 return eRet;//直接返回(默认值AllOk)
384 }
385
386 if (tagNbt >= NBT_TAG::ENUM_END)//确认在范围内
387 {
388 eRet = Error(NbtTypeTagError, tData, funcInfo, "{}:\nNBT Tag switch default: Unknown Type Tag[0x{:02X}({})]", __FUNCTION__,
389 (NBT_TAG_RAW_TYPE)tagNbt, (NBT_TAG_RAW_TYPE)tagNbt);//此处不进行提前返回,往后默认返回处理
390 STACK_TRACEBACK("tagNbt Test");
391 return eRet;//超出范围立刻返回
392 }
393
394 //然后读取名称
395 NBT_Type::String sName{};
396 eRet = GetName(tData, sName, funcInfo);
397 if (eRet != AllOk)
398 {
399 STACK_TRACEBACK("GetName Fail, Type: [NBT_Type::{}]", NBT_Type::GetTypeName(tagNbt));
400 return eRet;//名称读取失败立刻返回
401 }
402
403 //然后根据类型,调用对应的类型读取并返回到tmpNode
404 NBT_Node tmpNode{};
405 eRet = GetSwitch<bUnwrapMixedList>(tData, tmpNode, tagNbt, szStackDepth - 1, funcInfo);
406 if (eRet != AllOk)
407 {
408 STACK_TRACEBACK("GetSwitch Fail, Name: \"{}\", Type: [NBT_Type::{}]", sName.ToCharTypeUTF8(), NBT_Type::GetTypeName(tagNbt));//注意这里ToCharTypeUTF8可能抛异常
409 //return eRet;//注意此处不返回,进行插入,以便分析错误之前的正确数据
410 }
411
412 //sName:tmpNode,插入当前调用栈深度的根节点
413 //根据实际mc java代码得出,如果插入一个已经存在的键,会导致原先的值被替换并丢弃
414 //那么在失败后,手动从迭代器替换当前值,注意,此处必须是try_emplace,因为try_emplace失败后原先的值
415 //tmpNode不会被移动导致丢失,所以也无需拷贝插入以防止移动丢失问题
416 auto [it, bSuccess] = tCompound.try_emplace(std::move(sName), std::move(tmpNode));
417 if (!bSuccess)
418 {
419 //使用当前值替换掉阻止插入的原始值
420 it->second = std::move(tmpNode);
421
422 //发出警告,注意警告不用eRet接返回值
423 Error(ElementExistsWarn, tData, funcInfo, "{}:\nName: \"{}\", Type: [NBT_Type::{}] data already exist!", __FUNCTION__,
424 sName.ToCharTypeUTF8(), NBT_Type::GetTypeName(tagNbt));//注意这里ToCharTypeUTF8可能抛异常
425 }
426
427 //最后判断是否出错
428 if (eRet != AllOk)
429 {
430 STACK_TRACEBACK("While break with an error!");
431 return eRet;//出错返回
432 }
433 }
434
435 return eRet;//返回错误码
436 MYCATCH;
437 }
438
439 template<typename InputStream, typename InfoFunc>
440 static ErrCode GetStringType(InputStream &tData, NBT_Type::String &tString, InfoFunc &funcInfo) noexcept
441 {
442 ErrCode eRet = AllOk;
443
444 //读取字符串
445 eRet = GetName(tData, tString, funcInfo);//因为string与name读取原理一致,直接借用实现
446 if (eRet != AllOk)
447 {
448 STACK_TRACEBACK("GetString");//因为是借用实现,所以这里小小的改个名,防止报错Name误导人
449 return eRet;
450 }
451
452 return eRet;
453 }
454
455 template<bool bUnwrapMixedList, typename InputStream, typename InfoFunc>
456 static ErrCode GetListType(InputStream &tData, NBT_Type::List &tList, size_t szStackDepth, InfoFunc &funcInfo) noexcept
457 {
458 MYTRY;
459 ErrCode eRet = AllOk;
460 CHECK_STACK_DEPTH(szStackDepth);
461
462 //读取1字节的列表元素类型
463 NBT_TAG_RAW_TYPE enListElementTag = 0;//b=byte
464 eRet = ReadBigEndian(tData, enListElementTag, funcInfo);
465 if (eRet != AllOk)
466 {
467 STACK_TRACEBACK("enListElementTag Read");
468 return eRet;
469 }
470
471 //错误的列表元素类型
472 if (enListElementTag >= NBT_TAG::ENUM_END)
473 {
474 eRet = Error(NbtTypeTagError, tData, funcInfo, "{}:\nList NBT Type:Unknown Type Tag[0x{:02X}({})]", __FUNCTION__,
475 (NBT_TAG_RAW_TYPE)enListElementTag, (NBT_TAG_RAW_TYPE)enListElementTag);
476 STACK_TRACEBACK("enListElementTag Test");
477 return eRet;
478 }
479
480 //读取4字节的有符号列表长度
481 NBT_Type::ListLength iListLength = 0;//4byte
482 eRet = ReadBigEndian(tData, iListLength, funcInfo);
483 if (eRet != AllOk)
484 {
485 STACK_TRACEBACK("iListLength Read");
486 return eRet;
487 }
488
489 //检查有符号数大小范围
490 if (iListLength < 0)
491 {
492 eRet = Error(OutOfRangeError, tData, funcInfo, ":\niListLength[{}] < 0", __FUNCTION__, iListLength);
493 STACK_TRACEBACK("iListLength Test");
494 return eRet;
495 }
496
497 //防止重复N个结束标签,带有结束标签的必须是空列表
498 if (enListElementTag == NBT_TAG::End && iListLength != 0)
499 {
500 eRet = Error(ListElementTypeError, tData, funcInfo, "{}:\nThe list with TAG_End[0x00] tag must be empty, but [{}] elements were found", __FUNCTION__,
501 iListLength);
502 STACK_TRACEBACK("enListElementTag And iListLength Test");
503 return eRet;
504 }
505
506 //确保如果长度为0的情况下,列表类型必为End
507 if (iListLength == 0 && enListElementTag != NBT_TAG::End)
508 {
509 enListElementTag = (NBT_TAG_RAW_TYPE)NBT_TAG::End;
510 }
511
512 //提前扩容
513 tList.reserve(iListLength);//已知大小提前分配减少开销
514
515 //根据元素类型,读取n次列表
516 for (NBT_Type::ListLength i = 0; i < iListLength; ++i)
517 {
518 NBT_Node tmpNode{};//列表元素会直接赋值修改
519 eRet = GetSwitch<bUnwrapMixedList>(tData, tmpNode, (NBT_TAG)enListElementTag, szStackDepth - 1, funcInfo);
520 if (eRet != AllOk)//错误处理
521 {
522 STACK_TRACEBACK("GetSwitch Error, Size: [{}] Index: [{}]", iListLength, i);
523 return eRet;
524 }
525
526 //每读取一个往后插入一个
527 if constexpr (!bUnwrapMixedList)//正常插入
528 {
529 tList.emplace_back(std::move(tmpNode));
530 }
531 else//尝试解包插入
532 {
533 auto *pNode = &tmpNode;
534 do
535 {
536 if (enListElementTag != NBT_TAG::Compound)
537 {
538 break;
539 }
540
541 //尝试解包:只有一个无名称根
542 auto &cpdNode = tmpNode.GetCompound();//此处可能抛异常
543 if (cpdNode.Size() != 1)
544 {
545 break;
546 }
547
548 auto *pFind = cpdNode.Has(MU8STR(""));
549 if (pFind == NULL)
550 {
551 break;//没找到,说明是普通Compound
552 }
553
554 pNode = pFind;//找到了!
555 } while (0);
556
557 tList.emplace_back(std::move(*pNode));
558 }
559 }
560
561 return eRet;
562 MYCATCH;
563 }
564
565 //这个函数拦截所有内部调用产生的异常并处理返回,所以此函数绝对不抛出异常,由此调用此函数的函数也可无需catch异常
566 template<bool bUnwrapMixedList, typename InputStream, typename InfoFunc>
567 static ErrCode GetSwitch(InputStream &tData, NBT_Node &nodeNbt, NBT_TAG tagNbt, size_t szStackDepth, InfoFunc &funcInfo) noexcept//选择函数不检查递归层,由函数调用的函数检查
568 {
569 ErrCode eRet = AllOk;
570
571 switch (tagNbt)
572 {
573 case NBT_TAG::Byte:
574 {
576 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
577 }
578 break;
579 case NBT_TAG::Short:
580 {
582 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
583 }
584 break;
585 case NBT_TAG::Int:
586 {
588 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
589 }
590 break;
591 case NBT_TAG::Long:
592 {
594 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
595 }
596 break;
597 case NBT_TAG::Float:
598 {
600 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
601 }
602 break;
603 case NBT_TAG::Double:
604 {
606 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
607 }
608 break;
610 {
612 eRet = GetArrayType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
613 }
614 break;
615 case NBT_TAG::String:
616 {
618 eRet = GetStringType(tData, nodeNbt.Set<CurType>(), funcInfo);
619 }
620 break;
621 case NBT_TAG::List://需要递归调用,列表开头给出标签ID和长度,后续都为一系列同类型标签的有效负载(无标签 ID 或名称)
622 {
624 eRet = GetListType<bUnwrapMixedList>(tData, nodeNbt.Set<CurType>(), szStackDepth, funcInfo);//选择函数不减少递归层
625 }
626 break;
627 case NBT_TAG::Compound://需要递归调用
628 {
630 eRet = GetCompoundType<false, bUnwrapMixedList>(tData, nodeNbt.Set<CurType>(), szStackDepth, funcInfo);//选择函数不减少递归层
631 }
632 break;
634 {
636 eRet = GetArrayType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
637 }
638 break;
640 {
642 eRet = GetArrayType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
643 }
644 break;
645 case NBT_TAG::End://不应该在任何时候遇到此标签,Compound会读取到并消耗掉,不会传入,List遇到此标签不会调用读取,所以遇到即为错误
646 {
647 eRet = Error(NbtTypeTagError, tData, funcInfo, "{}:\nNBT Tag switch error: Unexpected Type Tag NBT_TAG::End[0x00(0)]", __FUNCTION__);
648 }
649 break;
650 default://其它未知标签,如NBT内标数据签错误
651 {
652 eRet = Error(NbtTypeTagError, tData, funcInfo, "{}:\nNBT Tag switch error: Unknown Type Tag[0x{:02X}({})]", __FUNCTION__,
653 (NBT_TAG_RAW_TYPE)tagNbt, (NBT_TAG_RAW_TYPE)tagNbt);//此处不进行提前返回,往后默认返回处理
654 }
655 break;
656 }
657
658 if (eRet != AllOk)//如果出错,打一下栈回溯
659 {
660 STACK_TRACEBACK("Tag[0x{:02X}({})] read error!",
661 (NBT_TAG_RAW_TYPE)tagNbt, (NBT_TAG_RAW_TYPE)tagNbt);
662 }
663
664 return eRet;//传递返回值
665 }
667
668public:
669 /*
670 备注:此函数读取nbt时,会创建一个默认根,然后把nbt内所有数据集合到此默认根上,
671 也就是哪怕按照mojang的nbt标准,默认根是无名Compound,也会被挂接到返回值里的
672 NBT_Type::Compound中。遍历函数返回的NBT_Type::Compound即可得到所有NBT数据,
673 这么做的目的是为了方便读写例程且不用在某些地方部分实现mojang的无名compound的
674 特殊处理,这种情况下可以在一定程度上甚至比mojang标准支持更多的NBT文件情况,
675 比如文件内并不是Compound开始的,而是单纯的几个不同类型且带有名字的NBT,那么也能
676 正常读取到并全部挂在NBT_Type::Compound中,就好像nbt文件本身就是一个大的无名
677 NBT_Type::Compound一样,相对的,写出函数也能支持写出此种情况,所以写出函数
678 WriteNBT在写出的时候,传入的值也是一个内含NBT_Type::Compound的NBT_Node,
679 然后传入的NBT_Type::Compound本身不会被以任何形式写入NBT文件,而是内部数据,
680 也就是挂接在下面的内容会被写入,这样既能保证兼容mojang的nbt文件,也能一定程度上
681 扩展nbt文件内可以存储的内容(允许nbt文件直接存储多个键值对而不是必须先挂在一个
682 无名称的Compound下)
683 */
684
685 //szStackDepth 控制栈深度,递归层检查仅由可嵌套的可能进行递归的函数进行,栈深度递减仅由对选择函数的调用进行
686 //注意此函数不会清空tCompound,所以可以对一个tCompound通过不同的tData多次调用来读取多个nbt片段并合并到一起
687 //如果指定了szDataStartIndex则会忽略tData中长度为szDataStartIndex的数据
688
701 template<bool bUnwrapMixedList = true, typename InputStream, typename InfoFunc = NBT_Print>
702 static bool ReadNBT(InputStream &IptStream, NBT_Type::Compound &tCompound, size_t szStackDepth = 512, InfoFunc funcInfo = NBT_Print{}) noexcept//从data中读取nbt
703 {
704 return GetCompoundType<true, bUnwrapMixedList>(IptStream, tCompound, szStackDepth, funcInfo) == AllOk;//从data中获取nbt数据到nRoot中,只有此调用为根部调用(模板true),用于处理特殊情况
705 }
706
718 template<bool bUnwrapMixedList = true, typename DataType = std::vector<uint8_t>, typename InfoFunc = NBT_Print>
719 static bool ReadNBT(const DataType &tDataInput, size_t szStartIdx, NBT_Type::Compound &tCompound, size_t szStackDepth = 512, InfoFunc funcInfo = NBT_Print{}) noexcept//从data中读取nbt
720 {
721 NBT_IO::DefaultInputStream<DataType> IptStream(tDataInput, szStartIdx);
722 return GetCompoundType<true, bUnwrapMixedList>(IptStream, tCompound, szStackDepth, funcInfo) == AllOk;
723 }
724
725
726#undef MYTRY
727#undef MYCATCH
728#undef CHECK_STACK_DEPTH
729#undef STACK_TRACEBACK
730#undef STRLING
731#undef _RP_STRLING
732#undef _RP___LINE__
733#undef _RP___FUNCTION__
734};
端序工具集
IO工具集
NBT节点类型,支持存储所有NBT类型的变体
#define MU8STR(charLiteralString)
从C风格字符串获取M-UTF-8的字符串
定义 NBT_Node.hpp:21
用于处理NBT信息打印的默认实现
NBT_Print_Level
用于指示信息打印等级的枚举
定义 NBT_Print.hpp:11
@ Warn
警告信息
定义 NBT_Print.hpp:13
@ Err
错误信息
定义 NBT_Print.hpp:14
uint8_t NBT_TAG_RAW_TYPE
NBT_TAG的原始类型,用于二进制读写或判断等
定义 NBT_TAG.hpp:12
NBT_TAG
枚举NBT类型对应的类型标签值
定义 NBT_TAG.hpp:17
@ Int
对应NBT_Type::Int
定义 NBT_TAG.hpp:21
@ Float
对应NBT_Type::Float
定义 NBT_TAG.hpp:23
@ Compound
对应NBT_Type::Compound
定义 NBT_TAG.hpp:28
@ String
对应NBT_Type::String
定义 NBT_TAG.hpp:26
@ ByteArray
对应NBT_Type::ByteArray
定义 NBT_TAG.hpp:25
@ Short
对应NBT_Type::Short
定义 NBT_TAG.hpp:20
@ List
对应NBT_Type::List
定义 NBT_TAG.hpp:27
@ Long
对应NBT_Type::Long
定义 NBT_TAG.hpp:22
@ End
对应NBT_Type::End
定义 NBT_TAG.hpp:18
@ LongArray
对应NBT_Type::LongArray
定义 NBT_TAG.hpp:30
@ Byte
对应NBT_Type::Byte
定义 NBT_TAG.hpp:19
@ IntArray
对应NBT_Type::IntArray
定义 NBT_TAG.hpp:29
@ Double
对应NBT_Type::Double
定义 NBT_TAG.hpp:24
@ ENUM_END
枚举结束标记,用于计算enum元素个数,范围判断等
定义 NBT_TAG.hpp:31
Compound::mapped_type * Has(const typename Compound::key_type &sTagName) noexcept
搜索标签是否存在
定义 NBT_Compound.hpp:316
static T BigToNativeAny(T data) noexcept
从大端字节序转换到当前平台字节序,自动匹配位数
定义 NBT_Endian.hpp:209
默认输入流类,用于从标准库容器中读取数据
定义 NBT_IO.hpp:45
NBT节点,用于存储NBT格式的各种数据类型
定义 NBT_Node.hpp:37
T & Set(Args &&... args)
原位放置新对象并替换当前对象
定义 NBT_Node.hpp:100
const NBT_Type::Compound & GetCompound() const
获取当前对象中存储的 Compound 类型的数据
定义 NBT_Node.hpp:285
一个用于打印信息到指定的C文件对象的工具类,作为库内大部分存在信息输出接口的默认实现。 实际可被使用此类为默认值参数的函数的调用方,以类似此类的仿函数参数重写的其它类型替换, 比如调用方实现了一个My_...
定义 NBT_Print.hpp:24
static bool ReadNBT(const DataType &tDataInput, size_t szStartIdx, NBT_Type::Compound &tCompound, size_t szStackDepth=512, InfoFunc funcInfo=NBT_Print{}) noexcept
从数据容器中读取NBT数据到NBT_Type::Compound对象中
定义 NBT_Reader.hpp:719
static bool ReadNBT(InputStream &IptStream, NBT_Type::Compound &tCompound, size_t szStackDepth=512, InfoFunc funcInfo=NBT_Print{}) noexcept
从输入流中读取NBT数据到NBT_Type::Compound对象中
定义 NBT_Reader.hpp:702
auto ToCharTypeUTF8(void) const
转换到UTF-8字符编码,但是返回为char类型而非char8_t类型
定义 NBT_String.hpp:283
typename TagToType< Tag >::type TagToType_T
从NBT_TAG获取对应的类型:编译期从NBT_TAG的enum值获取类型
定义 NBT_Type.hpp:250
int32_t ArrayLength
数组长度类型
定义 NBT_Type.hpp:136
NBT_String< std::basic_string< uint8_t >, std::basic_string_view< uint8_t > > String
字符串类型,存储Java M-UTF-8字符串
定义 NBT_Type.hpp:64
NBT_List< std::vector< NBT_Node > > List
列表类型,可顺序存储任意相同的NBT类型
定义 NBT_Type.hpp:68
NBT_Compound< std::unordered_map< String, NBT_Node > > Compound
集合类型,可存储任意不同的NBT类型,通过名称映射值
定义 NBT_Type.hpp:72
typename BuiltinRawType< T >::Type BuiltinRawType_T
映射内建类型到方便读写的raw类型:编译期获得内建类型到可以进行二进制读写的原始类型
定义 NBT_Type.hpp:341
int32_t ListLength
列表长度类型
定义 NBT_Type.hpp:138
uint16_t StringLength
字符串长度类型
定义 NBT_Type.hpp:137
static constexpr const char * GetTypeName(NBT_TAG tag) noexcept
通过类型标签获取类型名称
定义 NBT_Type.hpp:123