chenjunfu2-nbt-cpp v2.1.3
一个基于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 "UnknownError",
50 "StdException",
51 "ListElementTypeError",
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((void *)&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 using ValueType = NBT_Type::String::value_type;
277 size_t szStringLength = (size_t)wStringLength;
278 size_t szStringSize = szStringLength * sizeof(ValueType);
279
280 //判断长度是否超过
281 if (!tData.HasAvailData(szStringSize))
282 {
283 ErrCode eRet = Error(OutOfRangeError, tData, funcInfo, "{}:\n(Index[{}] + szStringLength[{}])[{}] > DataSize[{}]", __FUNCTION__,
284 tData.Index(), szStringLength, tData.Index() + szStringLength, tData.Size());
285 STACK_TRACEBACK("HasAvailData Test");
286 return eRet;
287 }
288
289 //解析出名称
290 tName.resize(szStringLength);//设置大小
291 tData.GetRange((void *)tName.data(), szStringSize);//构造string(如果长度为0则构造0长字符串,合法行为)
292
293 return eRet;
294 MYCATCH;
295 }
296
297 template<typename T, typename InputStream, typename InfoFunc>
298 static ErrCode GetBuiltInType(InputStream &tData, T &tBuiltIn, InfoFunc &funcInfo) noexcept
299 {
300 ErrCode eRet = AllOk;
301
302 //读取数据
303 using RAW_DATA_T = NBT_Type::BuiltinRawType_T<T>;//类型映射
304
305 //临时存储,因为可能存在跨类型转换
306 RAW_DATA_T tTmpRawData = 0;
307 eRet = ReadBigEndian(tData, tTmpRawData, funcInfo);
308 if (eRet != AllOk)
309 {
310 STACK_TRACEBACK("tTmpRawData Read");
311 return eRet;
312 }
313
314 //转换并返回
315 tBuiltIn = std::move(std::bit_cast<T>(tTmpRawData));
316 return eRet;
317 }
318
319 template<typename T, typename InputStream, typename InfoFunc>
320 static ErrCode GetArrayType(InputStream &tData, T &tArray, InfoFunc &funcInfo) noexcept
321 {
322 MYTRY;
323 ErrCode eRet = AllOk;
324
325 //获取4字节有符号数,代表数组元素个数
326 NBT_Type::ArrayLength iArrayLength = 0;//4byte
327 eRet = ReadBigEndian(tData, iArrayLength, funcInfo);
328 if (eRet != AllOk)
329 {
330 STACK_TRACEBACK("iArrayLength Read");
331 return eRet;
332 }
333
334 //检查有符号数大小范围
335 if (iArrayLength < 0)
336 {
337 eRet = Error(OutOfRangeError, tData, funcInfo, ":\niArrayLength[{}] < 0", __FUNCTION__, iArrayLength);
338 STACK_TRACEBACK("iArrayLength Test");
339 return eRet;
340 }
341
342 //验证完成,类型转换
343 using ValueType = typename T::value_type;
344 size_t szArrayLength = (size_t)iArrayLength;
345 size_t szArraySize = szArrayLength * sizeof(ValueType);
346
347 //判断长度是否超过
348 if (!tData.HasAvailData(szArraySize))//保证下方调用安全
349 {
350 eRet = Error(OutOfRangeError, tData, funcInfo, "{}:\n(Index[{}] + szArraySize[{}])[{}] > DataSize[{}]", __FUNCTION__,
351 tData.Index(), szArrayLength, tData.Index() + szArraySize, tData.Size());
352 STACK_TRACEBACK("HasAvailData Test");
353 return eRet;
354 }
355
356 //数组保存
357 tArray.reserve(szArrayLength);//提前扩容
358
359 //读取dElementCount个元素
360 for (size_t i = 0; i < szArrayLength; ++i)
361 {
362 ValueType tTmpData{};
363 ReadBigEndian<true>(tData, tTmpData, funcInfo);//调用需要确保范围安全
364 tArray.emplace_back(std::move(tTmpData));//读取一个插入一个
365 }
366
367 return eRet;
368 MYCATCH;
369 }
370
371 //如果是非根部,有额外检测
372 template<bool bRoot, bool bUnwrapMixedList, typename InputStream, typename InfoFunc>
373 static ErrCode GetCompoundType(InputStream &tData, NBT_Type::Compound &tCompound, size_t szStackDepth, InfoFunc &funcInfo) noexcept
374 {
375 MYTRY;
376 ErrCode eRet = AllOk;
377 CHECK_STACK_DEPTH(szStackDepth);
378
379 //读取
380 while (true)
381 {
382 //处理末尾情况
383 if (!tData.HasAvailData(sizeof(NBT_TAG_RAW_TYPE)))
384 {
385 if constexpr (!bRoot)//非根部情况遇到末尾,则报错
386 {
387 eRet = Error(OutOfRangeError, tData, funcInfo, "{}:\nIndex[{}] >= DataSize()[{}]", __FUNCTION__,
388 tData.Index(), tData.Size());
389 STACK_TRACEBACK("HasAvailData Test");
390 }
391
392 return eRet;//否则直接返回(默认值AllOk)
393 }
394
395 //先读取一下类型
396 NBT_TAG_RAW_TYPE u8CompoundEntryTag = (NBT_TAG_RAW_TYPE)tData.GetNext();
397 if (u8CompoundEntryTag == NBT_TAG::End)//处理End情况
398 {
399 return eRet;//直接返回(默认值AllOk)
400 }
401
402 if (u8CompoundEntryTag >= NBT_TAG::ENUM_END)//确认在范围内
403 {
404 eRet = Error(NbtTypeTagError, tData, funcInfo, "{}:\nNBT Tag switch default: Unknown Type Tag[0x{:02X}({})]", __FUNCTION__,
405 u8CompoundEntryTag, u8CompoundEntryTag);
406 STACK_TRACEBACK("u8CompoundEntryTag Test");
407 return eRet;//超出范围立刻返回
408 }
409
410 //验证完成,类型转换
411 NBT_TAG enCompoundEntryTag = (NBT_TAG)u8CompoundEntryTag;
412
413 //然后读取名称
414 NBT_Type::String sName{};
415 eRet = GetName(tData, sName, funcInfo);
416 if (eRet != AllOk)
417 {
418 STACK_TRACEBACK("GetName Error, Type: [NBT_Type::{}]", NBT_Type::GetTypeName(enCompoundEntryTag));
419 return eRet;//名称读取失败立刻返回
420 }
421
422 //然后根据类型,调用对应的类型读取并返回到tmpNode
423 NBT_Node tmpNode{};
424 eRet = GetSwitch<bUnwrapMixedList>(tData, tmpNode, enCompoundEntryTag, szStackDepth - 1, funcInfo);
425 if (eRet != AllOk)
426 {
427 STACK_TRACEBACK("GetSwitch Error, Name: \"{}\", Type: [NBT_Type::{}]", sName.ToCharTypeUTF8(), NBT_Type::GetTypeName(enCompoundEntryTag));//注意这里ToCharTypeUTF8可能抛异常
428 //return eRet;//注意此处不返回,进行插入,以便分析错误之前的正确数据
429 }
430
431 //sName:tmpNode,插入当前调用栈深度的根节点
432 //根据实际mc java代码得出,如果插入一个已经存在的键,会导致原先的值被替换并丢弃
433 //那么在失败后,手动从迭代器替换当前值,注意,此处必须是try_emplace,因为try_emplace失败后原先的值
434 //tmpNode不会被移动导致丢失,所以也无需拷贝插入以防止移动丢失问题
435 auto [it, bSuccess] = tCompound.try_emplace(std::move(sName), std::move(tmpNode));
436 if (!bSuccess)
437 {
438 //使用当前值替换掉阻止插入的原始值
439 it->second = std::move(tmpNode);
440
441 //发出警告,注意警告不用eRet接返回值
442 Error(ElementExistsWarn, tData, funcInfo, "{}:\nName: \"{}\", Type: [NBT_Type::{}] data already exist!", __FUNCTION__,
443 sName.ToCharTypeUTF8(), NBT_Type::GetTypeName(enCompoundEntryTag));//注意这里ToCharTypeUTF8可能抛异常
444 }
445
446 //最后判断是否出错
447 if (eRet != AllOk)
448 {
449 STACK_TRACEBACK("While break with an error!");
450 return eRet;//出错返回
451 }
452 }
453
454 return eRet;//返回错误码
455 MYCATCH;
456 }
457
458 template<typename InputStream, typename InfoFunc>
459 static ErrCode GetStringType(InputStream &tData, NBT_Type::String &tString, InfoFunc &funcInfo) noexcept
460 {
461 ErrCode eRet = AllOk;
462
463 //读取字符串
464 eRet = GetName(tData, tString, funcInfo);//因为string与name读取原理一致,直接借用实现
465 if (eRet != AllOk)
466 {
467 STACK_TRACEBACK("GetString");//因为是借用实现,所以这里小小的改个名,防止报错Name误导人
468 return eRet;
469 }
470
471 return eRet;
472 }
473
474 template<bool bUnwrapMixedList, typename InputStream, typename InfoFunc>
475 static ErrCode GetListType(InputStream &tData, NBT_Type::List &tList, size_t szStackDepth, InfoFunc &funcInfo) noexcept
476 {
477 MYTRY;
478 ErrCode eRet = AllOk;
479 CHECK_STACK_DEPTH(szStackDepth);
480
481 //读取1字节的列表元素类型
482 NBT_TAG_RAW_TYPE u8ListElementTag = 0;//b=byte
483 eRet = ReadBigEndian(tData, u8ListElementTag, funcInfo);
484 if (eRet != AllOk)
485 {
486 STACK_TRACEBACK("u8ListElementTag Read");
487 return eRet;
488 }
489
490 //错误的列表元素类型
491 if (u8ListElementTag >= NBT_TAG::ENUM_END)
492 {
493 eRet = Error(NbtTypeTagError, tData, funcInfo, "{}:\nList NBT Type:Unknown Type Tag[0x{:02X}({})]", __FUNCTION__,
494 (NBT_TAG_RAW_TYPE)u8ListElementTag, (NBT_TAG_RAW_TYPE)u8ListElementTag);
495 STACK_TRACEBACK("u8ListElementTag Test");
496 return eRet;
497 }
498
499 //验证完成,类型转换
500 NBT_TAG enListElementTag = (NBT_TAG)u8ListElementTag;
501
502 //读取4字节的有符号列表长度
503 NBT_Type::ListLength iListLength = 0;//4byte
504 eRet = ReadBigEndian(tData, iListLength, funcInfo);
505 if (eRet != AllOk)
506 {
507 STACK_TRACEBACK("iListLength Read");
508 return eRet;
509 }
510
511 //检查有符号数大小范围
512 if (iListLength < 0)
513 {
514 eRet = Error(OutOfRangeError, tData, funcInfo, ":\niListLength[{}] < 0", __FUNCTION__, iListLength);
515 STACK_TRACEBACK("iListLength Test");
516 return eRet;
517 }
518
519 //验证完成,类型转换
520 size_t szListLength = (size_t)iListLength;
521
522 //防止重复N个结束标签,带有结束标签的必须是空列表
523 if (enListElementTag == NBT_TAG::End && szListLength != 0)
524 {
525 eRet = Error(ListElementTypeError, tData, funcInfo, "{}:\nThe list with TAG_End[0x00] tag must be empty, but [{}] elements were found", __FUNCTION__,
526 szListLength);
527 STACK_TRACEBACK("enListElementTag And szListLength Test");
528 return eRet;
529 }
530
531 //确保如果长度为0的情况下,列表类型必为End
532 if (szListLength == 0 && enListElementTag != NBT_TAG::End)
533 {
534 enListElementTag = NBT_TAG::End;
535 }
536
537 //提前扩容
538 tList.reserve(szListLength);//已知大小提前分配减少开销
539
540 //根据元素类型,读取n次列表
541 for (size_t i = 0; i < szListLength; ++i)
542 {
543 NBT_Node tmpNode{};//列表元素会直接赋值修改
544 eRet = GetSwitch<bUnwrapMixedList>(tData, tmpNode, enListElementTag, szStackDepth - 1, funcInfo);
545 if (eRet != AllOk)//错误处理
546 {
547 STACK_TRACEBACK("GetSwitch Error, Size: [{}] Index: [{}]", szListLength, i);
548 return eRet;
549 }
550
551 //每读取一个往后插入一个
552 if constexpr (!bUnwrapMixedList)//正常插入
553 {
554 tList.emplace_back(std::move(tmpNode));
555 }
556 else//尝试解包插入
557 {
558 auto *pNode = &tmpNode;
559 do
560 {
561 if (enListElementTag != NBT_TAG::Compound)
562 {
563 break;
564 }
565
566 //尝试解包:只有一个无名称根
567 auto &cpdNode = tmpNode.GetCompound();//此处可能抛异常
568 if (cpdNode.Size() != 1)
569 {
570 break;
571 }
572
573 auto *pFind = cpdNode.Has(MU8STR(""));
574 if (pFind == NULL)
575 {
576 break;//没找到,说明是普通Compound
577 }
578
579 pNode = pFind;//找到了!
580 } while (0);
581
582 tList.emplace_back(std::move(*pNode));
583 }
584 }
585
586 return eRet;
587 MYCATCH;
588 }
589
590 //这个函数拦截所有内部调用产生的异常并处理返回,所以此函数绝对不抛出异常,由此调用此函数的函数也可无需catch异常
591 template<bool bUnwrapMixedList, typename InputStream, typename InfoFunc>
592 static ErrCode GetSwitch(InputStream &tData, NBT_Node &nodeNbt, NBT_TAG tagNbt, size_t szStackDepth, InfoFunc &funcInfo) noexcept//选择函数不检查递归层,由函数调用的函数检查
593 {
594 ErrCode eRet = AllOk;
595
596 switch (tagNbt)
597 {
598 case NBT_TAG::Byte:
599 {
601 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
602 }
603 break;
604 case NBT_TAG::Short:
605 {
607 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
608 }
609 break;
610 case NBT_TAG::Int:
611 {
613 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
614 }
615 break;
616 case NBT_TAG::Long:
617 {
619 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
620 }
621 break;
622 case NBT_TAG::Float:
623 {
625 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
626 }
627 break;
628 case NBT_TAG::Double:
629 {
631 eRet = GetBuiltInType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
632 }
633 break;
635 {
637 eRet = GetArrayType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
638 }
639 break;
640 case NBT_TAG::String:
641 {
643 eRet = GetStringType(tData, nodeNbt.Set<CurType>(), funcInfo);
644 }
645 break;
646 case NBT_TAG::List://需要递归调用,列表开头给出标签ID和长度,后续都为一系列同类型标签的有效负载(无标签 ID 或名称)
647 {
649 eRet = GetListType<bUnwrapMixedList>(tData, nodeNbt.Set<CurType>(), szStackDepth, funcInfo);//选择函数不减少递归层
650 }
651 break;
652 case NBT_TAG::Compound://需要递归调用
653 {
655 eRet = GetCompoundType<false, bUnwrapMixedList>(tData, nodeNbt.Set<CurType>(), szStackDepth, funcInfo);//选择函数不减少递归层
656 }
657 break;
659 {
661 eRet = GetArrayType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
662 }
663 break;
665 {
667 eRet = GetArrayType<CurType>(tData, nodeNbt.Set<CurType>(), funcInfo);
668 }
669 break;
670 case NBT_TAG::End://不应该在任何时候遇到此标签,Compound会读取到并消耗掉,不会传入,List遇到此标签不会调用读取,所以遇到即为错误
671 {
672 eRet = Error(NbtTypeTagError, tData, funcInfo, "{}:\nNBT Tag switch error: Unexpected Type Tag NBT_TAG::End[0x00(0)]", __FUNCTION__);
673 }
674 break;
675 default://其它未知标签,如NBT内标数据签错误
676 {
677 eRet = Error(NbtTypeTagError, tData, funcInfo, "{}:\nNBT Tag switch error: Unknown Type Tag[0x{:02X}({})]", __FUNCTION__,
678 (NBT_TAG_RAW_TYPE)tagNbt, (NBT_TAG_RAW_TYPE)tagNbt);//此处不进行提前返回,往后默认返回处理
679 }
680 break;
681 }
682
683 if (eRet != AllOk)//如果出错,打一下栈回溯
684 {
685 STACK_TRACEBACK("Tag[0x{:02X}({})] read error!",
686 (NBT_TAG_RAW_TYPE)tagNbt, (NBT_TAG_RAW_TYPE)tagNbt);
687 }
688
689 return eRet;//传递返回值
690 }
692
693public:
694 /*
695 备注:此函数读取nbt时,会创建一个默认根,然后把nbt内所有数据集合到此默认根上,
696 也就是哪怕按照mojang的nbt标准,默认根是无名Compound,也会被挂接到返回值里的
697 NBT_Type::Compound中。遍历函数返回的NBT_Type::Compound即可得到所有NBT数据,
698 这么做的目的是为了方便读写例程且不用在某些地方部分实现mojang的无名compound的
699 特殊处理,这种情况下可以在一定程度上甚至比mojang标准支持更多的NBT文件情况,
700 比如文件内并不是Compound开始的,而是单纯的几个不同类型且带有名字的NBT,那么也能
701 正常读取到并全部挂在NBT_Type::Compound中,就好像nbt文件本身就是一个大的无名
702 NBT_Type::Compound一样,相对的,写出函数也能支持写出此种情况,所以写出函数
703 WriteNBT在写出的时候,传入的值也是一个内含NBT_Type::Compound的NBT_Node,
704 然后传入的NBT_Type::Compound本身不会被以任何形式写入NBT文件,而是内部数据,
705 也就是挂接在下面的内容会被写入,这样既能保证兼容mojang的nbt文件,也能一定程度上
706 扩展nbt文件内可以存储的内容(允许nbt文件直接存储多个键值对而不是必须先挂在一个
707 无名称的Compound下)
708 */
709
710 //szStackDepth 控制栈深度,递归层检查仅由可嵌套的可能进行递归的函数进行,栈深度递减仅由对选择函数的调用进行
711 //注意此函数不会清空tCompound,所以可以对一个tCompound通过不同的tData多次调用来读取多个nbt片段并合并到一起
712 //如果指定了szDataStartIndex则会忽略tData中长度为szDataStartIndex的数据
713
726 template<bool bUnwrapMixedList = true, typename InputStream, typename InfoFunc = NBT_Print>
727 static bool ReadNBT(InputStream &IptStream, NBT_Type::Compound &tCompound, size_t szStackDepth = 512, InfoFunc funcInfo = InfoFunc{}) noexcept//从data中读取nbt
728 {
729 return GetCompoundType<true, bUnwrapMixedList>(IptStream, tCompound, szStackDepth, funcInfo) == AllOk;//从data中获取nbt数据到nRoot中,只有此调用为根部调用(模板true),用于处理特殊情况
730 }
731
743 template<bool bUnwrapMixedList = true, typename DataType = std::vector<uint8_t>, typename InfoFunc = NBT_Print>
744 static bool ReadNBT(const DataType &tDataInput, size_t szStartIdx, NBT_Type::Compound &tCompound, size_t szStackDepth = 512, InfoFunc funcInfo = InfoFunc{}) noexcept//从data中读取nbt
745 {
746 NBT_IO::DefaultInputStream<DataType> IptStream(tDataInput, szStartIdx);
747 return GetCompoundType<true, bUnwrapMixedList>(IptStream, tCompound, szStackDepth, funcInfo) == AllOk;
748 }
749
750#ifdef CJF2_NBT_CPP_USE_ZLIB
751
760 template <typename InfoFunc = NBT_Print>
761 static bool SimpleReadNbtFile(const std::filesystem::path &pathFileName, NBT_Type::Compound &tCompound, InfoFunc funcInfo = InfoFunc{}) noexcept
762 {
763 //读取文件
764 std::vector<uint8_t> vFileData;
765 if (!NBT_IO::ReadFile(pathFileName, vFileData, funcInfo))
766 {
767 funcInfo(NBT_Print_Level::Err, "Error: Cannot read file [{}].\n", pathFileName.string());
768 return false;
769 }
770
771 //尝试解压,失败则视作未压缩数据
772 std::vector<uint8_t> vNbtData;
773 if (!NBT_IO::DecompressDataNoThrow(vNbtData, vFileData, funcInfo))
774 {
775 funcInfo(NBT_Print_Level::Warn, "Warning: Decompression failed, assuming uncompressed data.\n");
776 vNbtData = std::move(vFileData);
777 }
778
779 //清理数据
780 vFileData.clear();
781 vFileData.shrink_to_fit();
782
783 //读取
784 if (!ReadNBT(vNbtData, 0, tCompound, 512, funcInfo))
785 {
786 funcInfo(NBT_Print_Level::Err, "Error: ReadNBT failed.\n");
787 return false;
788 }
789
790 return true;
791 }
792
793#endif
794
795#undef MYTRY
796#undef MYCATCH
797#undef CHECK_STACK_DEPTH
798#undef STACK_TRACEBACK
799#undef STRLING
800#undef _RP_STRLING
801#undef _RP___LINE__
802#undef _RP___FUNCTION__
803};
端序工具集
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:279
static T BigToNativeAny(T data) noexcept
从大端字节序转换到当前平台字节序,自动匹配位数
定义 NBT_Endian.hpp:209
默认输入流类,用于从标准库容器中读取数据
定义 NBT_IO.hpp:45
static bool DecompressDataNoThrow(O &oData, const I &iData, InfoFunc funcInfo=InfoFunc{}) noexcept
解压数据,但是不抛出异常,而是通过funcInfo打印异常信息并返回成功与否
定义 NBT_IO.hpp:804
static bool ReadFile(const std::filesystem::path &pathFileName, T &tData, InfoFunc funcInfo=InfoFunc{}) noexcept
从指定文件名的文件中读取字节流数据到任意顺序容器中
定义 NBT_IO.hpp:391
NBT节点,用于存储NBT格式的各种数据类型
定义 NBT_Node.hpp:37
T & Set(Args &&... args)
原位放置新对象并替换当前对象
定义 NBT_Node.hpp:94
const NBT_Type::Compound & GetCompound() const
获取当前对象中存储的 Compound 类型的数据
定义 NBT_Node.hpp:275
static bool SimpleReadNbtFile(const std::filesystem::path &pathFileName, NBT_Type::Compound &tCompound, InfoFunc funcInfo=InfoFunc{}) noexcept
从可能被压缩的文件中读取 NBT 数据到 NBT_Type::Compound 对象中
定义 NBT_Reader.hpp:761
static bool ReadNBT(const DataType &tDataInput, size_t szStartIdx, NBT_Type::Compound &tCompound, size_t szStackDepth=512, InfoFunc funcInfo=InfoFunc{}) noexcept
从数据容器中读取NBT数据到NBT_Type::Compound对象中
定义 NBT_Reader.hpp:744
static bool ReadNBT(InputStream &IptStream, NBT_Type::Compound &tCompound, size_t szStackDepth=512, InfoFunc funcInfo=InfoFunc{}) noexcept
从输入流中读取NBT数据到NBT_Type::Compound对象中
定义 NBT_Reader.hpp:727
auto ToCharTypeUTF8(void) const
转换到UTF-8字符编码,但是返回为char类型而非char8_t类型
定义 NBT_String.hpp:277
typename TagToType< Tag >::type TagToType_T
从NBT_TAG获取对应的类型:编译期从NBT_TAG的enum值获取类型
定义 NBT_Type.hpp:251
int32_t ArrayLength
数组长度类型
定义 NBT_Type.hpp:137
NBT_String< MUTF8_String, MUTF8_String_View > String
字符串类型,存储Java M-UTF-8字符串
定义 NBT_Type.hpp:65
NBT_List< std::vector< NBT_Node > > List
列表类型,可顺序存储任意相同的NBT类型
定义 NBT_Type.hpp:69
NBT_Compound< std::unordered_map< String, NBT_Node > > Compound
集合类型,可存储任意不同的NBT类型,通过名称映射值
定义 NBT_Type.hpp:73
typename BuiltinRawType< T >::Type BuiltinRawType_T
映射内建类型到方便读写的raw类型:编译期获得内建类型到可以进行二进制读写的原始类型
定义 NBT_Type.hpp:345
int32_t ListLength
列表长度类型
定义 NBT_Type.hpp:139
uint16_t StringLength
字符串长度类型
定义 NBT_Type.hpp:138
static constexpr const char * GetTypeName(NBT_TAG tag) noexcept
通过类型标签获取类型名称
定义 NBT_Type.hpp:124