MCI18n/Utilities/SnbtWriter.cs

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");
}
}
}