Files
SpineViewer/SpineViewer/Spine/SkeletonConverter.cs
2025-04-11 00:07:09 +08:00

316 lines
12 KiB
C#

using Microsoft.VisualBasic;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Encodings.Web;
using SpineViewer.Utils;
namespace SpineViewer.Spine
{
/// <summary>
/// SkeletonConverter 基类, 使用静态方法 New 来创建具体版本对象
/// </summary>
public abstract class SkeletonConverter : ImplementationResolver<SkeletonConverter, SpineImplementationAttribute, SpineVersion>
{
/// <summary>
/// 创建特定版本的 SkeletonConverter
/// </summary>
public static SkeletonConverter New(SpineVersion version) => New(version, []);
/// <summary>
/// Json 格式控制
/// </summary>
private static readonly JsonWriterOptions jsonWriterOptions = new()
{
Indented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
/// <summary>
/// 读取二进制骨骼文件并构造 Json 对象
/// </summary>
public abstract JsonObject ReadBinary(string binPath);
/// <summary>
/// 将 Json 对象写入二进制骨骼文件
/// </summary>
public abstract void WriteBinary(JsonObject root, string binPath, bool nonessential = false);
/// <summary>
/// 读取 Json 对象
/// </summary>
public virtual JsonObject ReadJson(string jsonPath)
{
using var input = File.OpenRead(jsonPath);
if (JsonNode.Parse(input) is JsonObject root)
return root;
else
throw new InvalidDataException($"{jsonPath} is not a valid json object");
}
/// <summary>
/// 写入 Json 对象
/// </summary>
public virtual void WriteJson(JsonObject root, string jsonPath)
{
using var output = File.Create(jsonPath);
using var writer = new Utf8JsonWriter(output, jsonWriterOptions);
root.WriteTo(writer);
}
/// <summary>
/// 读取骨骼文件
/// </summary>
public JsonObject Read(string path)
{
try
{
return ReadBinary(path);
}
catch
{
try
{
return ReadJson(path);
}
catch
{
// 都不行就报错
throw new InvalidDataException($"Unknown skeleton file format {path}");
}
}
}
/// <summary>
/// 转换到目标版本
/// </summary>
public abstract JsonObject ToVersion(JsonObject root, SpineVersion version);
/// <summary>
/// 二进制骨骼文件读
/// </summary>
public class BinaryReader
{
protected byte[] buffer = new byte[32];
protected byte[] bytesBigEndian = new byte[8];
public readonly List<string> StringTable = new(32);
protected Stream input;
public BinaryReader(Stream input) { this.input = input; }
public int Read()
{
int val = input.ReadByte();
if (val == -1) throw new EndOfStreamException();
return val;
}
public byte ReadByte() => (byte)Read();
public byte ReadUByte() => (byte)Read();
public sbyte ReadSByte() => (sbyte)ReadByte();
public bool ReadBoolean() => Read() != 0;
public float ReadFloat()
{
if (input.Read(bytesBigEndian, 0, 4) < 4) throw new EndOfStreamException();
buffer[3] = bytesBigEndian[0];
buffer[2] = bytesBigEndian[1];
buffer[1] = bytesBigEndian[2];
buffer[0] = bytesBigEndian[3];
return BitConverter.ToSingle(buffer, 0);
}
public int ReadInt()
{
if (input.Read(bytesBigEndian, 0, 4) < 4) throw new EndOfStreamException();
return (bytesBigEndian[0] << 24)
| (bytesBigEndian[1] << 16)
| (bytesBigEndian[2] << 8)
| bytesBigEndian[3];
}
public long ReadLong()
{
if (input.Read(bytesBigEndian, 0, 8) < 8) throw new EndOfStreamException();
return ((long)(bytesBigEndian[0]) << 56)
| ((long)(bytesBigEndian[1]) << 48)
| ((long)(bytesBigEndian[2]) << 40)
| ((long)(bytesBigEndian[3]) << 32)
| ((long)(bytesBigEndian[4]) << 24)
| ((long)(bytesBigEndian[5]) << 16)
| ((long)(bytesBigEndian[6]) << 8)
| (long)(bytesBigEndian[7]);
}
public int ReadVarInt(bool optimizePositive = true)
{
byte b = ReadByte();
int val = b & 0x7F;
if ((b & 0x80) != 0)
{
b = ReadByte();
val |= (b & 0x7F) << 7;
if ((b & 0x80) != 0)
{
b = ReadByte();
val |= (b & 0x7F) << 14;
if ((b & 0x80) != 0)
{
b = ReadByte();
val |= (b & 0x7F) << 21;
if ((b & 0x80) != 0)
val |= (ReadByte() & 0x7F) << 28;
}
}
}
// 最低位是符号, 根据符号得到全 1 或全 0
// 无符号右移, 符号按原样设置在最高位, 其他位与符号异或
return optimizePositive ? val : (val >>> 1) ^ -(val & 1);
}
public string ReadString()
{
int byteCount = ReadVarInt();
switch (byteCount)
{
case 0: return null;
case 1: return "";
}
byteCount--;
if (buffer.Length < byteCount) buffer = new byte[byteCount];
ReadFully(buffer, 0, byteCount);
return System.Text.Encoding.UTF8.GetString(buffer, 0, byteCount);
}
public string ReadStringRef()
{
int index = ReadVarInt();
return index == 0 ? null : StringTable[index - 1];
}
public void ReadFully(byte[] buffer, int offset, int length)
{
while (length > 0)
{
int count = input.Read(buffer, offset, length);
if (count <= 0) throw new EndOfStreamException();
offset += count;
length -= count;
}
}
}
/// <summary>
/// 二进制骨骼文件写
/// </summary>
protected class BinaryWriter
{
protected byte[] buffer = new byte[32];
protected byte[] bytesBigEndian = new byte[8];
public readonly List<string> StringTable = new(32);
protected Stream output;
public BinaryWriter(Stream output) { this.output = output; }
public void Write(int val) => output.WriteByte((byte)val);
public void WriteByte(byte val) => output.WriteByte(val);
public void WriteUByte(byte val) => output.WriteByte(val);
public void WriteSByte(sbyte val) => output.WriteByte((byte)val);
public void WriteBoolean(bool val) => output.WriteByte((byte)(val ? 1 : 0));
public void WriteFloat(float val)
{
uint v = BitConverter.SingleToUInt32Bits(val);
bytesBigEndian[0] = (byte)(v >> 24);
bytesBigEndian[1] = (byte)(v >> 16);
bytesBigEndian[2] = (byte)(v >> 8);
bytesBigEndian[3] = (byte)v;
output.Write(bytesBigEndian, 0, 4);
}
public void WriteInt(int val)
{
bytesBigEndian[0] = (byte)(val >> 24);
bytesBigEndian[1] = (byte)(val >> 16);
bytesBigEndian[2] = (byte)(val >> 8);
bytesBigEndian[3] = (byte)val;
output.Write(bytesBigEndian, 0, 4);
}
public void WriteLong(long val)
{
bytesBigEndian[0] = (byte)(val >> 56);
bytesBigEndian[1] = (byte)(val >> 48);
bytesBigEndian[2] = (byte)(val >> 40);
bytesBigEndian[3] = (byte)(val >> 32);
bytesBigEndian[4] = (byte)(val >> 24);
bytesBigEndian[5] = (byte)(val >> 16);
bytesBigEndian[6] = (byte)(val >> 8);
bytesBigEndian[7] = (byte)val;
output.Write(bytesBigEndian, 0, 8);
}
public void WriteVarInt(int val, bool optimizePositive = true)
{
// 有符号右移, 会变成全 1 或者全 0 符号
// 其他位与符号异或, 符号按原样设置在最低位
if (!optimizePositive) val = (val << 1) ^ (val >> 31);
byte b = (byte)(val & 0x7F);
val >>>= 7;
if (val != 0)
{
output.WriteByte((byte)(b | 0x80));
b = (byte)(val & 0x7F);
val >>>= 7;
if (val != 0)
{
output.WriteByte((byte)(b | 0x80));
b = (byte)(val & 0x7F);
val >>>= 7;
if (val != 0)
{
output.WriteByte((byte)(b | 0x80));
b = (byte)(val & 0x7F);
val >>>= 7;
if (val != 0)
{
output.WriteByte((byte)(b | 0x80));
b = (byte)(val & 0x7F);
}
}
}
}
output.WriteByte(b);
}
public void WriteString(string val)
{
if (val == null)
{
WriteVarInt(0);
return;
}
if (val.Length == 0)
{
WriteVarInt(1);
return;
}
int byteCount = System.Text.Encoding.UTF8.GetByteCount(val);
WriteVarInt(byteCount + 1);
if (buffer.Length < byteCount) buffer = new byte[byteCount];
System.Text.Encoding.UTF8.GetBytes(val, 0, val.Length, buffer, 0);
WriteFully(buffer, 0, byteCount);
}
public void WriteStringRef(string val)
{
if (val is null)
{
WriteVarInt(0);
return;
}
int index = StringTable.IndexOf(val);
if (index < 0)
{
StringTable.Add(val);
index = StringTable.Count - 1;
}
WriteVarInt(index + 1);
}
public void WriteFully(byte[] buffer, int offset, int length) => output.Write(buffer, offset, length);
}
}
}