namespace AssetRipper.VersionUtilities; /// /// A compact value type for efficiently storing Unity versions inside 24 bits /// public partial struct CompactUnityVersion24 { private const int majorOffset = 3; private const int buildOffset = 9; private const int typeOffset = 6; private const uint bitMask3 = 0x0007U; private const uint bitMask5 = 0x001FU; private const uint bitMask6 = 0x003FU; private const uint bitMask7 = 0x007FU; /// /// 5 bits for the major, /// 3 bits for the minor /// private readonly byte m_MajorMinorByte; /// /// 7 bits for the build, /// 3 bits for the type, /// 6 bits for the type number /// private readonly ushort m_BuildTypeShort; /// /// 2042 /// public const ushort MajorMaxValue = (ushort)(31U + 2011U); /// /// 5 bits /// private byte MajorRaw => unchecked((byte)((m_MajorMinorByte >> majorOffset) & bitMask5)); /// /// The first number in a Unity version string. 5 bits. /// Valid values inclusive: 0 - 5, 2017 - 2042 /// public ushort Major => ConvertMajorRawToNormal(MajorRaw); /// /// The second number in a Unity version string. 3 bits /// public byte Minor => unchecked((byte)(m_MajorMinorByte & bitMask3)); /// /// The third number in a Unity version string. 7 bits /// public byte Build => unchecked((byte)((m_BuildTypeShort >> buildOffset) & bitMask7)); /// /// The letter in a Unity version string. 3 bits /// public UnityVersionType Type => (UnityVersionType)unchecked((byte)((m_BuildTypeShort >> typeOffset) & bitMask3)); /// /// The last number in a Unity version string. 6 bits /// public byte TypeNumber => unchecked((byte)(m_BuildTypeShort & bitMask6)); /// /// The minimum value this type can have /// public static CompactUnityVersion24 MinVersion { get; } = new CompactUnityVersion24((byte)0, (ushort)0); /// /// The maximum value this type can have /// public static CompactUnityVersion24 MaxVersion { get; } = new CompactUnityVersion24(byte.MaxValue, ushort.MaxValue); /// /// Construct a new Unity version /// public CompactUnityVersion24(ushort major) { m_MajorMinorByte = unchecked((byte)(ConvertMajorRawToNormal(major) << majorOffset)); m_BuildTypeShort = 0; } /// /// Construct a new Unity version /// public CompactUnityVersion24(ushort major, byte minor) { m_MajorMinorByte = unchecked((byte)((ConvertMajorRawToNormal(major) << majorOffset) | CastToThreeBits(minor))); m_BuildTypeShort = 0; } /// /// Construct a new Unity version /// public CompactUnityVersion24(ushort major, byte minor, byte build) { unchecked { m_MajorMinorByte = (byte)((ConvertMajorRawToNormal(major) << majorOffset) | CastToThreeBits(minor)); m_BuildTypeShort = (ushort)(CastToSevenBits(build) << buildOffset); } } /// /// Construct a new Unity version /// public CompactUnityVersion24(ushort major, byte minor, byte build, UnityVersionType type) { unchecked { m_MajorMinorByte = (byte)((ConvertMajorRawToNormal(major) << majorOffset) | CastToThreeBits(minor)); m_BuildTypeShort = (ushort)( (CastToSevenBits(build) << buildOffset) | (CastToThreeBits((byte)type) << typeOffset)); } } /// /// Construct a new Unity version /// public CompactUnityVersion24(ushort major, byte minor, byte build, UnityVersionType type, byte typeNumber) { unchecked { m_MajorMinorByte = (byte)((ConvertMajorRawToNormal(major) << majorOffset) | CastToThreeBits(minor)); m_BuildTypeShort = (ushort)( (CastToSevenBits(build) << buildOffset) | (CastToThreeBits((byte)type) << typeOffset) | CastToSixBits(typeNumber)); } } private CompactUnityVersion24(byte majorMinorByte, ushort buildTypeShort) { m_MajorMinorByte = majorMinorByte; m_BuildTypeShort = buildTypeShort; } /// /// Converts this to its binary representation /// public void GetBits(out byte majorMinorByte, out ushort buildTypeShort) { majorMinorByte = m_MajorMinorByte; buildTypeShort = m_BuildTypeShort; } /// /// Converts a binary representation into its respective version /// /// /// /// A new Unity version with the cooresponding bits public static CompactUnityVersion24 FromBits(byte majorMinorByte, ushort buildTypeShort) => new CompactUnityVersion24(majorMinorByte, buildTypeShort); private static ushort ConvertMajorRawToNormal(byte raw) { return raw < 6 ? raw : unchecked((ushort)(raw + 2011U)); } private static byte ConvertMajorRawToNormal(ushort major) { if (major < 6) { return unchecked((byte)major); } else if (major >= 2017 && major <= MajorMaxValue) { return unchecked((byte)(major - 2011U)); } else { throw new ArgumentOutOfRangeException(nameof(major)); } } private static byte CastToThreeBits(byte b) { return b <= bitMask3 ? b : throw new ArgumentOutOfRangeException(nameof(b)); } private static byte CastToSixBits(byte b) { return b <= bitMask6 ? b : throw new ArgumentOutOfRangeException(nameof(b)); } private static byte CastToSevenBits(byte b) { return b <= bitMask7 ? b : throw new ArgumentOutOfRangeException(nameof(b)); } /// /// Serialize the version as a string /// /// A new string like 2019.4.3f1 public override string ToString() { return ((UnityVersion)this).ToString(); } }