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 } }