211 lines
6.7 KiB
C#
211 lines
6.7 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// 提供用于将CompoundTag保存为SNBT格式文件的工具方法
|
|
/// </summary>
|
|
public static class SnbtWriter
|
|
{
|
|
/// <summary>
|
|
/// 将CompoundTag保存为SNBT格式文件
|
|
/// </summary>
|
|
/// <param name="tag">要保存的CompoundTag</param>
|
|
/// <param name="filePath">文件路径</param>
|
|
/// <param name="prettyPrint">是否进行美化格式输出</param>
|
|
/// <exception cref="ArgumentNullException">当tag为null时抛出</exception>
|
|
/// <exception cref="IOException">写入文件失败时抛出</exception>
|
|
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, new UTF8Encoding(false));
|
|
}
|
|
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or DirectoryNotFoundException)
|
|
{
|
|
throw new IOException($"保存SNBT文件失败 '{filePath}': {ex.Message}", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将CompoundTag转换为SNBT格式字符串
|
|
/// </summary>
|
|
/// <param name="tag">要转换的CompoundTag</param>
|
|
/// <param name="prettyPrint">是否美化输出格式</param>
|
|
/// <returns>SNBT格式字符串</returns>
|
|
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<T>(ArrayTag<T> arrayTag, char prefix, char? suffix, int indentLevel)
|
|
where T : unmanaged, INumber<T>
|
|
{
|
|
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");
|
|
}
|
|
}
|
|
} |