using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace Spine
{
///
/// Spine 运行时版本标识类
///
public sealed class SpineVersion : IEquatable, IComparable
{
public static readonly SpineVersion V21 = new(typeof(SpineRuntime21.Skeleton));
public static readonly SpineVersion V34 = new(typeof(SpineRuntime34.Skeleton));
public static readonly SpineVersion V35 = new(typeof(SpineRuntime35.Skeleton));
public static readonly SpineVersion V36 = new(typeof(SpineRuntime36.Skeleton));
public static readonly SpineVersion V37 = new(typeof(SpineRuntime37.Skeleton));
public static readonly SpineVersion V38 = new(typeof(SpineRuntime38.Skeleton));
public static readonly SpineVersion V40 = new(typeof(SpineRuntime40.Skeleton));
public static readonly SpineVersion V41 = new(typeof(SpineRuntime41.Skeleton));
public static readonly SpineVersion V42 = new(typeof(SpineRuntime42.Skeleton));
///
/// 所有可用的版本
///
public static readonly ImmutableArray RegisteredVersions;
///
/// tag 到版本的映射
///
private static readonly FrozenDictionary _tagToRegisteredVersion;
static SpineVersion()
{
List registeredVersions = [];
Dictionary tagToRegisteredVersion = [];
// 通过反射获取所有 public static 字段,类型为 SpineVersion
var props = typeof(SpineVersion).GetFields(BindingFlags.Public | BindingFlags.Static).Where(p => p.FieldType == typeof(SpineVersion));
foreach (var prop in props)
{
if (prop.GetValue(null) is SpineVersion spVer)
{
registeredVersions.Add(spVer);
tagToRegisteredVersion[spVer.Tag] = spVer;
}
}
RegisteredVersions = registeredVersions.ToImmutableArray();
_tagToRegisteredVersion = tagToRegisteredVersion.ToFrozenDictionary();
}
///
/// 从骨骼文件获取版本
///
/// 文件路径
/// 没有检测到有效的版本字符串
/// 无效的字符串格式
/// 未注册的版本号
public static SpineVersion GetVersion(string skelPath)
{
string? versionString = null;
byte[] data = File.ReadAllBytes(skelPath);
using var input = new MemoryStream(data);
var binaryReader = new Utils.BinaryReader(input);
var jsonReader = new Utf8JsonReader(data, true, default);
// try json format
if (Utils.Utf8Validator.IsUtf8(data))
{
try
{
if (JsonDocument.TryParseValue(ref jsonReader, out var doc))
{
var root = doc.RootElement;
if (root.ValueKind == JsonValueKind.Object &&
root.TryGetProperty("skeleton", out var skeleton) &&
skeleton.ValueKind == JsonValueKind.Object &&
skeleton.TryGetProperty("spine", out var _version) &&
_version.ValueKind == JsonValueKind.String)
{
versionString = _version.GetString();
}
}
}
catch { }
}
// try v4 binary format
if (versionString is null)
{
try
{
input.Position = 0;
var hash = binaryReader.ReadLong();
var versionPosition = input.Position;
var versionByteCount = binaryReader.ReadVarInt();
input.Position = versionPosition;
if (versionByteCount <= 13)
versionString = binaryReader.ReadString();
}
catch { }
}
// try v3 binary format
if (versionString is null)
{
try
{
input.Position = 0;
var hash = binaryReader.ReadString();
versionString = binaryReader.ReadString();
}
catch { }
}
if (versionString is null)
throw new InvalidDataException($"No verison detected: '{skelPath}'");
var parts = versionString.Trim().Split(".", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (parts.Length < 2) throw new ArgumentException($"Unknown verison: {versionString}");
var tag = $"{parts[0]}.{parts[1]}";
if (_tagToRegisteredVersion.TryGetValue(tag, out var version))
return version;
throw new KeyNotFoundException($"Unregistered verison: {versionString}");
}
///
/// 私有构造函数
///
/// 运行时库的 Skeleton 类型对象, 用于查找程序集信息
private SpineVersion(Type skeletonType)
{
var version = skeletonType.Assembly.GetCustomAttribute()?.InformationalVersion;
ArgumentNullException.ThrowIfNull(version, skeletonType.AssemblyQualifiedName);
// 此处假设自己填的版本号都是规范的 x.y.x
var parts = version.Trim().Split('.', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
Major = int.Parse(parts[0]);
Minor = int.Parse(parts[1]);
Tag = $"{Major}.{Minor}";
RuntimeVersion = version;
}
///
/// 主版本号
///
public int Major { get; }
///
/// 次版本号
///
public int Minor { get; }
///
/// 仅包含主次版本号的字符串, 例如 3.8
///
public string Tag { get; }
///
/// 带有补丁号的完整运行时版本号, 例如 3.8.99
///
public string RuntimeVersion { get; }
public override string ToString() => Tag;
#region IEquatable 接口实现
public bool Equals(SpineVersion? other)
{
if (ReferenceEquals(this, other)) return true;
if (other is null) return false;
return Major == other.Major && Minor == other.Minor;
}
public override bool Equals(object? obj) => obj is SpineVersion other && Equals(other);
public override int GetHashCode() => Tag.GetHashCode();
public static bool operator ==(SpineVersion a, SpineVersion b) => a is null ? b is null : a.Equals(b);
public static bool operator !=(SpineVersion a, SpineVersion b) => !(a == b);
#endregion
#region IComparable 接口实现
public int CompareTo(SpineVersion? other)
{
if (other is null) return 1;
int majorDiff = Major.CompareTo(other.Major);
return majorDiff != 0 ? majorDiff : Minor.CompareTo(other.Minor);
}
public static bool operator <(SpineVersion a, SpineVersion b) => a is null ? b is not null : a.CompareTo(b) < 0;
public static bool operator >(SpineVersion a, SpineVersion b) => a is not null && a.CompareTo(b) > 0;
public static bool operator <=(SpineVersion a, SpineVersion b) => a is null || a.CompareTo(b) <= 0;
public static bool operator >=(SpineVersion a, SpineVersion b) => a is null ? b is null : a.CompareTo(b) >= 0;
#endregion
}
}