using System.Collections; using System.Text; using Nbt.Type; namespace Nbt; internal static class NbtUtils { public const char ESCAPE_CHAR = '\\'; public const char SINGLE_QUOTE_CHAR = '\''; public const char DOUBLE_QUOTE_CHAR = '"'; public static readonly INbtRegistry Registry = new NbtRegistry(); static NbtUtils() { Registry.AddAll(NbtByteType.Value, NbtShortType.Value, NbtIntType.Value, NbtLongType.Value, NbtFloatType.Value, NbtDoubleType.Value, NbtStringType.Value, NbtListType.Value, NbtCompoundType.Value, NbtEndType.Value, NbtByteArrayType.Value, NbtIntArrayType.Value, NbtLongArrayType.Value); } public static INbtType GetTagType(NbtTagType tagType) => Registry.TryGet(tagType, out var value) ? value : throw new UnknownTagTypeException(tagType, $"Unknown tag type: {tagType}"); public static NbtTagType GetValueTagType() => GetValueTagType(typeof(T)); public static NbtTagType GetValueTagType(System.Type type) => type == typeof(sbyte) ? NbtTagType.Byte : type == typeof(short) ? NbtTagType.Short : type == typeof(int) ? NbtTagType.Int : type == typeof(long) ? NbtTagType.Long : type == typeof(float) ? NbtTagType.Float : type == typeof(double) ? NbtTagType.Double : type == typeof(string) ? NbtTagType.String : NbtTagType.Unknown; public static NbtTagType GetArrayTagType() => GetArrayTagType(typeof(T)); public static NbtTagType GetArrayTagType(System.Type type) => type == typeof(sbyte) ? NbtTagType.ByteArray : type == typeof(int) ? NbtTagType.IntArray : type == typeof(long) ? NbtTagType.LongArray : NbtTagType.Unknown; public static NbtTagType EnsureValueType() { var type = typeof(T); var tagType = GetValueTagType(type); return tagType is NbtTagType.Unknown ? throw new UnknownTagTypeException(tagType, $"Unknown value type: {type.Name}") : tagType; } public static NbtTagType EnsureArrayType() { var type = typeof(T); var tagType = GetArrayTagType(type); return tagType is NbtTagType.Unknown ? throw new UnknownTagTypeException(tagType, $"Unknown array type: {type.Name}") : tagType; } public static NbtTagType ToArrayType(this NbtTagType tagType) => tagType switch { NbtTagType.Byte => NbtTagType.ByteArray, NbtTagType.Int => NbtTagType.IntArray, NbtTagType.Long => NbtTagType.LongArray, _ => NbtTagType.Unknown }; public static NbtTagType ToValueType(this NbtTagType tagType) => tagType switch { NbtTagType.ByteArray => NbtTagType.Byte, NbtTagType.IntArray => NbtTagType.Int, NbtTagType.LongArray => NbtTagType.Long, _ => NbtTagType.Unknown }; public static string QuoteString(string s, bool onlyIfNeeded = false) { if (s.Length == 0) { return $"{DOUBLE_QUOTE_CHAR}{DOUBLE_QUOTE_CHAR}"; } var sb = new StringBuilder(); var needQuotes = false; var quoteChar = '\0'; foreach (var c in s) { needQuotes |= QuoteString(sb, c, ref quoteChar); } if (!onlyIfNeeded || needQuotes) { quoteChar = quoteChar == DOUBLE_QUOTE_CHAR ? SINGLE_QUOTE_CHAR : DOUBLE_QUOTE_CHAR; sb.Insert(0, quoteChar); sb.Append(quoteChar); } return sb.ToString(); } private static bool QuoteString(StringBuilder sb, char c, ref char quoteChar) { var needQuotes = false; var escape = false; switch (c) { case '\t': escape = true; c = 't'; break; case '\r': escape = true; c = 'r'; break; case '\n': escape = true; c = 'n'; break; case ESCAPE_CHAR: escape = true; break; case SINGLE_QUOTE_CHAR: case DOUBLE_QUOTE_CHAR: if (quoteChar == '\0') { quoteChar = c; } else if (c != quoteChar) { escape = true; } needQuotes = true; break; case '.': case '_': case '-': case '+': case >= 'a' and <= 'z' or >= 'A' and <= 'Z' or >= '0' and <= '9': break; default: needQuotes = true; break; } if (escape) { sb.Append(ESCAPE_CHAR); } sb.Append(c); return needQuotes || escape; } }