using SharpNBT; using SharpNBT.SNBT; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Numerics; using System.Text; namespace MCI18n.Utilities { /// /// 提供用于将CompoundTag保存为SNBT格式文件的工具方法 /// public static class SnbtWriter { /// /// 将CompoundTag保存为SNBT格式文件 /// /// 要保存的CompoundTag /// 文件路径 /// 是否进行美化格式输出 /// 当tag为null时抛出 /// 写入文件失败时抛出 public static void SaveToFile(CompoundTag tag, string filePath, bool prettyPrint = true) { if (tag == null) throw new ArgumentNullException(nameof(tag)); try { string snbt = ConvertToSnbt(tag, prettyPrint); File.WriteAllText(filePath, snbt, Encoding.UTF8); } catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or DirectoryNotFoundException) { throw new IOException($"保存SNBT文件失败 '{filePath}': {ex.Message}", ex); } } /// /// 将CompoundTag转换为SNBT格式字符串 /// /// 要转换的CompoundTag /// 是否美化输出格式 /// SNBT格式字符串 public static string ConvertToSnbt(CompoundTag tag, bool prettyPrint = true) { if (prettyPrint) return FormatCompound(tag, 0); else return tag.Stringify(true); } private static string FormatCompound(CompoundTag tag, int indentLevel) { if (tag.Count == 0) return "{}"; var sb = new StringBuilder(); sb.AppendLine("{"); int i = 0; foreach (var child in tag) { sb.Append('\t', indentLevel + 1); sb.Append($"{EscapeName(child.Name)}: "); sb.Append(FormatTag(child, indentLevel + 1)); //if (i < tag.Count - 1) // sb.AppendLine(","); //else // sb.AppendLine(); sb.AppendLine(); i++; } sb.Append('\t', indentLevel); sb.Append("}"); return sb.ToString(); } private static string FormatTag(Tag tag, int indentLevel) { switch (tag) { case CompoundTag compoundTag: return FormatCompound(compoundTag, indentLevel); case ListTag listTag: return FormatList(listTag, indentLevel); case ByteArrayTag byteArrayTag: return FormatArray(byteArrayTag, 'B', 'b', indentLevel); case IntArrayTag intArrayTag: return FormatArray(intArrayTag, 'I', null, indentLevel); case LongArrayTag longArrayTag: return FormatArray(longArrayTag, 'L', 'L', indentLevel); case StringTag stringTag: return $"\"{EscapeString(stringTag.Value)}\""; case ByteTag byteTag: //return $"{byteTag.Value}b"; return byteTag.Bool ? "true" : "false"; case ShortTag shortTag: return $"{shortTag.Value}s"; case IntTag intTag: return intTag.Value.ToString(); case LongTag longTag: return $"{longTag.Value}L"; case FloatTag floatTag: return $"{floatTag.Value.ToString("0.#####", CultureInfo.InvariantCulture)}f"; case DoubleTag doubleTag: return $"{doubleTag.Value.ToString("0.#####", CultureInfo.InvariantCulture)}d"; default: return tag.Stringify(false); } } private static string FormatList(ListTag listTag, int indentLevel) { if (listTag.Count == 0) return "[]"; var sb = new StringBuilder(); sb.AppendLine("["); for (int i = 0; i < listTag.Count; i++) { sb.Append('\t', indentLevel + 1); sb.Append(FormatTag(listTag[i], indentLevel + 1)); //if (i < listTag.Count - 1) // sb.AppendLine(","); //else // sb.AppendLine(); sb.AppendLine(); } sb.Append('\t', indentLevel); sb.Append("]"); return sb.ToString(); } private static string FormatArray(ArrayTag arrayTag, char prefix, char? suffix, int indentLevel) where T : unmanaged, INumber { if (arrayTag.Count == 0) return $"[{prefix};]"; var sb = new StringBuilder(); sb.Append($"[{prefix};"); // 对于数组类型,我们通常不使用多行格式 for (int i = 0; i < arrayTag.Count; i++) { if (i > 0) sb.AppendLine(); //sb.Append("\r\n"); sb.Append(arrayTag[i]); if (suffix != null) sb.Append(suffix.Value); } sb.Append("]"); return sb.ToString(); } private static string EscapeName(string name) { // 如果名称包含特殊字符,需要用引号括起来 if (string.IsNullOrEmpty(name) || NeedsQuotes(name)) return $"\"{EscapeString(name)}\""; return name; } private static bool NeedsQuotes(string s) { foreach (char c in s) { if (!char.IsLetterOrDigit(c) && c != '_' && c != '-' && c != '.') return true; } return false; } private static string EscapeString(string str) { if (string.IsNullOrEmpty(str)) return str; return str.Replace("\\", "\\\\") .Replace("\"", "\\\"") .Replace("\n", "\\n") .Replace("\r", "\\r") .Replace("\t", "\\t"); } } }