MCI18n/Utilities/SnbtWriter.cs

211 lines
6.7 KiB
C#
Raw Normal View History

2025-06-22 11:33:58 +08:00
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>
/// <20><EFBFBD><E1B9A9><EFBFBD>ڽ<EFBFBD>CompoundTag<61><67><EFBFBD><EFBFBD>ΪSNBT<42><54>ʽ<EFBFBD>ļ<EFBFBD><C4BC>Ĺ<EFBFBD><C4B9>߷<EFBFBD><DFB7><EFBFBD>
/// </summary>
public static class SnbtWriter
{
/// <summary>
/// <20><>CompoundTag<61><67><EFBFBD><EFBFBD>ΪSNBT<42><54>ʽ<EFBFBD>ļ<EFBFBD>
/// </summary>
/// <param name="tag">Ҫ<><D2AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD>CompoundTag</param>
/// <param name="filePath"><3E>ļ<EFBFBD>·<EFBFBD><C2B7></param>
/// <param name="prettyPrint"><3E>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD></param>
/// <exception cref="ArgumentNullException"><3E><>tagΪnullʱ<6C>׳<EFBFBD></exception>
/// <exception cref="IOException">д<><D0B4><EFBFBD>ļ<EFBFBD>ʧ<EFBFBD><CAA7>ʱ<EFBFBD>׳<EFBFBD></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));
2025-06-22 11:33:58 +08:00
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or DirectoryNotFoundException)
{
throw new IOException($"<22><><EFBFBD><EFBFBD>SNBT<42>ļ<EFBFBD>ʧ<EFBFBD><CAA7> '{filePath}': {ex.Message}", ex);
}
}
/// <summary>
/// <20><>CompoundTagת<67><D7AA>ΪSNBT<42><54>ʽ<EFBFBD>ַ<EFBFBD><D6B7><EFBFBD>
/// </summary>
/// <param name="tag">Ҫת<D2AA><D7AA><EFBFBD><EFBFBD>CompoundTag</param>
/// <param name="prettyPrint"><3E>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ</param>
/// <returns>SNBT<42><54>ʽ<EFBFBD>ַ<EFBFBD><D6B7><EFBFBD></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};");
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͣ<EFBFBD><CDA3><EFBFBD><EFBFBD><EFBFBD>ͨ<EFBFBD><CDA8><EFBFBD><EFBFBD>ʹ<EFBFBD>ö<EFBFBD><C3B6>и<EFBFBD>ʽ
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)
{
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ư<EFBFBD><C6B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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");
}
}
}