using System; using System.Text; namespace ScreenConnect; [Serializable] public class CoreVersion : IComparable { public static readonly CoreVersion None = new CoreVersion(0uL, null); private string? toStringCache; public ulong Value { get; } public ushort Major => (ushort)(Value >> 48); public ushort Minor => (ushort)(Value >> 32); public ushort Build => (ushort)(Value >> 16); public ushort Revision => (ushort)Value; internal CoreVersion(ulong value, string? toStringCache) { Value = value; this.toStringCache = toStringCache; } public static void ParseToValue(string versionString, out ulong value, out bool willRoundtripCorrectly) { int currentPart = 0; ushort currentNumber = 0; int currentDigitCount = 0; ushort major = 0; ushort minor = 0; ushort build = 0; ushort revision = 0; for (int i = 0; i < versionString.Length; i++) { if (versionString[i] == '.') { ProcessCurrentPart(); currentPart++; currentNumber = 0; currentDigitCount = 0; continue; } if (versionString[i] >= '0' && versionString[i] <= '9') { if (currentDigitCount != 0 && currentNumber == 0) { throw new ArgumentException("Version part cannot have leading zeros"); } currentNumber *= 10; currentNumber += (ushort)(versionString[i] - 48); currentDigitCount++; continue; } throw new ArgumentException("Character out of valid range for version: " + versionString[i]); } ProcessCurrentPart(); value = GetValue(major, minor, build, revision); willRoundtripCorrectly = currentPart > 0 && (currentPart == 1 || currentNumber > 0); void ProcessCurrentPart() { if (currentPart == 0) { major = currentNumber; } else if (currentPart == 1) { minor = currentNumber; } else if (currentPart == 2) { build = currentNumber; } else { if (currentPart != 3) { throw new ArgumentException("Too many parts"); } revision = currentNumber; } if (currentDigitCount == 0) { throw new ArgumentException("Empty version part"); } } } public static ulong GetValue(ushort major, ushort minor, ushort build, ushort revision) { return ((ulong)major << 48) | ((ulong)minor << 32) | ((ulong)build << 16) | revision; } public override int GetHashCode() { return ((Major & 0xFF) << 24) | ((Minor & 0xFF) << 16) | ((Build & 0xFF) << 8) | (Revision & 0xFF); } public override bool Equals(object? obj) { if (obj is CoreVersion coreVersion && Major == coreVersion.Major && Minor == coreVersion.Minor && Build == coreVersion.Build) { return Revision == coreVersion.Revision; } return false; } public override string ToString() { return toStringCache ?? (toStringCache = ToString(this)); } private static string ToString(CoreVersion version) { StringBuilder stringBuilder = new StringBuilder(16); stringBuilder.Append(version.Major); stringBuilder.Append("."); stringBuilder.Append(version.Minor); if (version.Build != 0 || version.Revision != 0) { stringBuilder.Append("."); stringBuilder.Append(version.Build); } if (version.Revision != 0) { stringBuilder.Append("."); stringBuilder.Append(version.Revision); } return stringBuilder.ToString(); } [Obsolete("Use ToString instead")] public string ToShortString() { return ToString(); } public static bool operator ==(CoreVersion? v1, CoreVersion? v2) { return v1?.Equals(v2) ?? ((object)v2 == null); } public static bool operator !=(CoreVersion? v1, CoreVersion? v2) { return !(v1 == v2); } public static bool operator >(CoreVersion x, CoreVersion y) { return x.CompareTo(y) > 0; } public static bool operator <(CoreVersion x, CoreVersion y) { return x.CompareTo(y) < 0; } public static bool operator >=(CoreVersion x, CoreVersion y) { return x.CompareTo(y) >= 0; } public static bool operator <=(CoreVersion x, CoreVersion y) { return x.CompareTo(y) <= 0; } public static Version ToVersion(CoreVersion version) { return new Version(version.Major, version.Minor, version.Build, version.Revision); } [Obsolete("Call DataObject.ToVersion(...) instead of relying on implicit conversion to System.Version")] public static implicit operator Version(CoreVersion version) { return ToVersion(version); } public int CompareTo(CoreVersion? other) { if (other == null) { return 1; } int num = Major.CompareTo(other.Major); if (num != 0) { return num; } int num2 = Minor.CompareTo(other.Minor); if (num2 != 0) { return num2; } int num3 = Build.CompareTo(other.Build); if (num3 != 0) { return num3; } return Revision.CompareTo(other.Revision); } }