mirror of
https://github.com/AssetRipper/AssetRipper.Primitives.git
synced 2026-11-13 04:26:30 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d759b3db98 | ||
|
|
55b3294ff5 | ||
|
|
c3ff29e8d7 | ||
|
|
61f47faf04 | ||
|
|
57912c52f1 | ||
|
|
5ab5d744d6 | ||
|
|
cd7f841845 | ||
|
|
9a5f8bb707 | ||
|
|
089c71d944 | ||
|
|
919fa0d5ab | ||
|
|
df8c5e2879 | ||
|
|
13ece60907 | ||
|
|
3a46414f08 | ||
|
|
bc556b7c72 | ||
|
|
7147a5d5b1 | ||
|
|
776ab543fa | ||
|
|
ca701a43dc | ||
|
|
f478cd8015 | ||
|
|
8cd69e8946 | ||
|
|
3cba893ff2 | ||
|
|
9c4a7d8127 | ||
|
|
fd3590ad52 | ||
|
|
3ef5dae9d4 | ||
|
|
78ecfb7de4 | ||
|
|
5080b7eb29 | ||
|
|
2e8940973f | ||
|
|
64f50b46fb | ||
|
|
fbdda777ac | ||
|
|
2de06bfaf4 | ||
|
|
c7a8932863 | ||
|
|
4bcc823371 | ||
|
|
7630da6da0 | ||
|
|
e5802e39ee | ||
|
|
c663c68124 | ||
|
|
fa2458cfae | ||
|
|
ee923d8e9b | ||
|
|
8b23a0c671 | ||
|
|
f56feaf217 | ||
|
|
bd575f0063 | ||
|
|
0a6b003bf2 | ||
|
|
f4026adfd0 | ||
|
|
8954b81f81 | ||
|
|
856d05dafe | ||
|
|
b88ae7c46f | ||
|
|
4c4289fddd | ||
|
|
379034a536 | ||
|
|
428756101a | ||
|
|
dc15723135 | ||
|
|
6452151c5c | ||
|
|
3bbd130e76 | ||
|
|
2ab6b413c4 | ||
|
|
29fc3f655c | ||
|
|
71efe9e898 | ||
|
|
c51c22a15c | ||
|
|
83d16e30d0 | ||
|
|
0fe68a61ba | ||
|
|
964baafad1 | ||
|
|
264694de87 | ||
|
|
406bc21ad8 | ||
|
|
f852851d24 | ||
|
|
d9b73345a6 | ||
|
|
167d4242b0 | ||
|
|
87a0f969a5 | ||
|
|
f499511551 | ||
|
|
94ad139e22 | ||
|
|
3b8d4eb470 | ||
|
|
a96a32e87e | ||
|
|
e9d121c927 | ||
|
|
eedce8a128 | ||
|
|
f67c61a3d5 | ||
|
|
878aa3d815 | ||
|
|
57c444ba0e | ||
|
|
ff95cc0dc4 | ||
|
|
25385ab739 | ||
|
|
a292fe87a4 | ||
|
|
3320be1312 | ||
|
|
659c382ff7 | ||
|
|
4aed9eb024 | ||
|
|
4e6f668e10 | ||
|
|
dc4e81eb28 | ||
|
|
0e2a37e920 | ||
|
|
b29d4a7a69 | ||
|
|
93ee2b0675 | ||
|
|
ebe6802570 | ||
|
|
97da17c016 | ||
|
|
475c27c28c | ||
|
|
fb0fb3f1c6 | ||
|
|
182fca60ca | ||
|
|
d4609533b2 | ||
|
|
46e53d834f | ||
|
|
99157d5433 | ||
|
|
5ec33f0518 | ||
|
|
c4a5ab77d4 | ||
|
|
ab4df10c87 | ||
|
|
2d00a1259a | ||
|
|
b65056d08f | ||
|
|
42c5957429 | ||
|
|
606e439721 | ||
|
|
b9a57bd765 |
No files matched your search
@@ -7,55 +7,25 @@ on:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
net7:
|
||||
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
dotnet-version: ["9.0", "8.0", "7.0", "6.0"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup .NET
|
||||
uses: actions/[email protected].3
|
||||
uses: actions/setup-dotnet@v4.3.0
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: |
|
||||
6.0.x
|
||||
7.0.x
|
||||
8.0.x
|
||||
9.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --framework net7.0 --no-restore
|
||||
run: dotnet build --framework net${{ matrix.dotnet-version }} --no-restore
|
||||
- name: Test
|
||||
run: dotnet test --framework net7.0 --no-build --verbosity normal
|
||||
|
||||
net6:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/[email protected]
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --framework net6.0 --no-restore
|
||||
- name: Test
|
||||
run: dotnet test --framework net6.0 --no-build --verbosity normal
|
||||
|
||||
net5:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup .NET
|
||||
uses: actions/[email protected]
|
||||
with:
|
||||
dotnet-version: |
|
||||
5.0.x
|
||||
6.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: dotnet build --framework net5.0 --no-restore
|
||||
- name: Test
|
||||
run: dotnet test --framework net5.0 --no-build --verbosity normal
|
||||
run: dotnet test --framework net${{ matrix.dotnet-version }} --no-build --verbosity normal
|
||||
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net35;net40;net48;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="4.1.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssetRipper.Primitives\AssetRipper.Primitives.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,75 @@
|
||||
namespace AssetRipper.Primitives.Tests;
|
||||
|
||||
public class ComparisonTests
|
||||
{
|
||||
private static readonly UnityVersion lowValue = new UnityVersion(2017, 3, 0, UnityVersionType.Patch, 1);
|
||||
private static readonly UnityVersion mediumValue = new UnityVersion(2019, 4, 3, UnityVersionType.Final, 1);
|
||||
private static readonly UnityVersion highValue = new UnityVersion(2020, 2, 0, UnityVersionType.Final, 1);
|
||||
|
||||
[Test]
|
||||
public void LessThanIsTransitive()
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(lowValue, Is.LessThan(mediumValue));
|
||||
Assert.That(mediumValue, Is.LessThan(highValue));
|
||||
Assert.That(lowValue, Is.LessThan(highValue));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GreaterThanIsTransitive()
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(mediumValue, Is.GreaterThan(lowValue));
|
||||
Assert.That(highValue, Is.GreaterThan(mediumValue));
|
||||
Assert.That(highValue, Is.GreaterThan(lowValue));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WeakInequalityHolds()
|
||||
{
|
||||
UnityVersion low = lowValue;
|
||||
Assert.That(lowValue, Is.LessThanOrEqualTo(low));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MinimumValueDefinition()
|
||||
{
|
||||
Assert.That(UnityVersion.MinVersion, Is.EqualTo(new UnityVersion(ushort.MinValue, ushort.MinValue, ushort.MinValue, UnityVersionType.Alpha, byte.MinValue)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MaximumValueDefinition()
|
||||
{
|
||||
Assert.That(UnityVersion.MaxVersion, Is.EqualTo(new UnityVersion(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, (UnityVersionType)byte.MaxValue, byte.MaxValue)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnIncreaseOfTypeNumberMakesInequal()
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(UnityVersion.MinVersion, Is.LessThan(new UnityVersion(ushort.MinValue, ushort.MinValue, ushort.MinValue, UnityVersionType.Alpha, 1)));
|
||||
Assert.That(lowValue, Is.LessThan(new UnityVersion(lowValue.Major, lowValue.Minor, lowValue.Build, lowValue.Type, (byte)(lowValue.TypeNumber + 1))));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AlphaZeroIsTheDefault()
|
||||
{
|
||||
Assert.That(new UnityVersion(2019, 1, 0), Is.LessThan(new UnityVersion(2019, 1, 0, UnityVersionType.Final, 1)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparisonMethodsIgnoreUnspecifiedValues()
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(new UnityVersion(2019, 1, 0, UnityVersionType.Final, 1).GreaterThanOrEquals(2019, 1, 0));
|
||||
Assert.That(new UnityVersion(2019, 1, 0, UnityVersionType.Final, 1).LessThanOrEquals(2019, 1, 0));
|
||||
});
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
namespace AssetRipper.VersionUtilities.Tests;
|
||||
namespace AssetRipper.Primitives.Tests;
|
||||
|
||||
public class DistanceTests
|
||||
{
|
||||
@@ -16,14 +16,14 @@ public class DistanceTests
|
||||
{
|
||||
ulong distance1 = UnityVersion.Distance(version4, version5);
|
||||
ulong distance2 = UnityVersion.Distance(version5, version4);
|
||||
Assert.IsTrue(distance1 == distance2);
|
||||
Assert.That(distance1, Is.EqualTo(distance2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DistanceIsZeroForEquivalentVersions()
|
||||
{
|
||||
ulong distance = UnityVersion.Distance(version4, version4);
|
||||
Assert.IsTrue(distance == 0);
|
||||
Assert.That(distance, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -31,7 +31,7 @@ public class DistanceTests
|
||||
{
|
||||
UnityVersion version = new UnityVersion(6, 1);
|
||||
UnityVersion closest = version.GetClosestVersion(versionArray);
|
||||
Assert.IsTrue(closest == version6);
|
||||
Assert.That(closest, Is.EqualTo(version6));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -39,6 +39,6 @@ public class DistanceTests
|
||||
{
|
||||
UnityVersion version = new UnityVersion(5, 4, 9);
|
||||
UnityVersion closest = version.GetClosestVersion(versionArray);
|
||||
Assert.IsTrue(closest == version5);
|
||||
Assert.That(closest, Is.EqualTo(version5));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
using NUnit.Framework.Internal;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AssetRipper.Primitives.Tests;
|
||||
|
||||
public class ParsingTests
|
||||
{
|
||||
[TestCase("4.2.2f1", 4, 2, 2, UnityVersionType.Final, 1, null, "Normal Unity 4 Version")]
|
||||
[TestCase("4.2.2f1\n1", 4, 2, 2, UnityVersionType.Final, 1, "\n1", "Custom Unity 4 Version")]
|
||||
[TestCase("2343.4.5f7", 2343, 4, 5, UnityVersionType.Final, 7, null, "Fictitious Future Version")]
|
||||
[TestCase("2019.2.2f1c2\n2", 2019, 2, 2, UnityVersionType.China, 2, "\n2", "Fake Custom Chinese Version")]
|
||||
[TestCase("2019", 2019, 0, 0, UnityVersionType.Final, 1, null, "Major Only")]
|
||||
[TestCase("2019.4", 2019, 4, 0, UnityVersionType.Final, 1, null, "Major Minor Only")]
|
||||
[TestCase("2019.4.3", 2019, 4, 3, UnityVersionType.Final, 1, null, "Major Minor Build Only")]
|
||||
[TestCase("0", 0, 0, 0, UnityVersionType.Alpha, 0, null, "Zero, Major Only")]
|
||||
[TestCase("0.0", 0, 0, 0, UnityVersionType.Alpha, 0, null, "Zero, Major Minor Only")]
|
||||
[TestCase("0.0.0", 0, 0, 0, UnityVersionType.Alpha, 0, null, "Zero, Major Minor Build Only")]
|
||||
[TestCase("0.0.0b2", 0, 0, 0, UnityVersionType.Beta, 2, null, "Zero, Full")]
|
||||
[TestCase("2019.2.2f1-letters", 2019, 2, 2, UnityVersionType.Final, 1, "-letters", "Issue #40 - Appended Custom Characters")]
|
||||
public void UnityVersionParsesCorrectly(string versionString, int major, int minor, int build, UnityVersionType type, int typeNumber, string? customEngine, string name)
|
||||
{
|
||||
UnityVersion expected = new UnityVersion((ushort)major, (ushort)minor, (ushort)build, type, (byte)typeNumber);
|
||||
UnityVersion parsedVersion = UnityVersion.Parse(versionString, out string? parsedCustomEngine);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(parsedCustomEngine, Is.EqualTo(customEngine), $"The custom engine boolean is incorrect for '{name}'");
|
||||
Assert.That(parsedVersion, Is.EqualTo(expected), $"The parsed version is incorrect for '{name}'");
|
||||
if (customEngine is not null || typeNumber is not 0 and not 1)
|
||||
{
|
||||
Assert.That(parsedVersion.ToString(UnityVersionFormatFlags.Default, parsedCustomEngine ?? ""), Is.EqualTo(versionString));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(GenerateRandomVersions), new object[] { 20 })]
|
||||
public void UnityVersionToStringParsesCorrectly(UnityVersion version)
|
||||
{
|
||||
string versionString = version.ToString();
|
||||
UnityVersion parsedVersion = UnityVersion.Parse(versionString, out string? customEngine);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(customEngine, Is.Null);
|
||||
Assert.That(parsedVersion, Is.EqualTo(version));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LongChineseVersionFormat()
|
||||
{
|
||||
//This is an actual version from a game.
|
||||
//Versions like 2019.3.0f2 and 2020.3.15f2 don't have corresponding Chinese versions.
|
||||
//Source: https://unity.cn/releases
|
||||
string version = "2021.3.11f1c2";
|
||||
UnityVersion expected = new UnityVersion(2021, 3, 11, UnityVersionType.China, 2);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(UnityVersion.Parse(version), Is.EqualTo(expected));
|
||||
Assert.That(expected.ToString(), Is.EqualTo(version));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShortChineseVersionFormat()
|
||||
{
|
||||
//This is an actual version from a game.
|
||||
//Source: https://github.com/AssetRipper/AssetRipper/issues/841
|
||||
string version = "2017.4.40c1";
|
||||
UnityVersion expected = new UnityVersion(2017, 4, 40, UnityVersionType.China, 1);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(UnityVersion.Parse(version), Is.EqualTo(expected));
|
||||
Assert.That(expected.ToString(UnityVersionFormatFlags.UseShortChineseFormat), Is.EqualTo(version));
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase("g2019")]
|
||||
[TestCase("g2019.4")]
|
||||
[TestCase("g2019.4.3")]
|
||||
[TestCase("g2019.4.3f")]
|
||||
[TestCase("g2019.4.3f1")]
|
||||
[TestCase("g2019.4.3f1c1")]
|
||||
[TestCase("2019g")]
|
||||
[TestCase("2019.4g")]
|
||||
[TestCase("2019.4.3g")]
|
||||
[TestCase("2019.4.3fg")]
|
||||
// "2019.4.3f1g" and "2019.4.3f1c1g" are technically valid because custom engines often append some text to the version string.
|
||||
public void InvalidVersionStringFailsToParse(string versionString)
|
||||
{
|
||||
Assert.That(UnityVersion.TryParse(versionString, out _, out _), Is.False);
|
||||
}
|
||||
|
||||
private static IEnumerable<UnityVersion> GenerateRandomVersions(int count)
|
||||
{
|
||||
Randomizer random = TestContext.CurrentContext.Random;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
yield return new UnityVersion(random.NextUShort(), random.NextUShort(), random.NextUShort(), random.NextEnum<UnityVersionType>(), random.NextByte());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#if NET5_0_OR_GREATER
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace AssetRipper.Primitives.Tests;
|
||||
|
||||
public class UnityGuidTests
|
||||
{
|
||||
private const string randomGuidString = "352a5b3897136ed2702a283243520538";
|
||||
private const string sequentialGuidString = "0123456789abcdef0fedcba987654321";
|
||||
|
||||
[Test]
|
||||
public void MissingReferenceSerializedCorrectly()
|
||||
{
|
||||
Assert.That(UnityGuid.MissingReference.ToString(), Is.EqualTo("0000000deadbeef15deadf00d0000000"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToByteArrayIsConsistentWithConstructorFromByteArray()
|
||||
{
|
||||
UnityGuid guid = UnityGuid.NewGuid();
|
||||
byte[] bytes = guid.ToByteArray();
|
||||
UnityGuid fromBytes = new UnityGuid(bytes);
|
||||
Assert.That(fromBytes, Is.EqualTo(guid));
|
||||
Assert.That(fromBytes.ToString(), Is.EqualTo(guid.ToString()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionFromSystemGuidToUnityGuidProducesSameString()
|
||||
{
|
||||
Guid systemGuid = Guid.NewGuid();
|
||||
UnityGuid unityGUID = new UnityGuid(systemGuid);
|
||||
Assert.That(unityGUID.ToString(), Is.EqualTo(systemGuid.ToString().Replace("-", "")));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsZeroReturnsTrueForTheZeroGuid()
|
||||
{
|
||||
UnityGuid unityGUID = new UnityGuid(0, 0, 0, 0);
|
||||
Assert.That(unityGUID.IsZero, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsZeroReturnsFalseForRandomGuid()
|
||||
{
|
||||
UnityGuid unityGUID = UnityGuid.NewGuid();
|
||||
Assert.That(unityGUID.IsZero, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParsedGuidOutputsSameString()
|
||||
{
|
||||
UnityGuid parsedGUID = UnityGuid.Parse(randomGuidString);
|
||||
string outputedString = parsedGUID.ToString();
|
||||
Assert.That(outputedString, Is.EqualTo(randomGuidString));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConversionsAreInverses()
|
||||
{
|
||||
UnityGuid unityGuid = UnityGuid.NewGuid();
|
||||
Guid systemGuid = (Guid)unityGuid;
|
||||
Assert.That((UnityGuid)systemGuid, Is.EqualTo(unityGuid));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ByteConversionIsItsOwnInverse()
|
||||
{
|
||||
UnityGuid originalGuid = UnityGuid.NewGuid();
|
||||
UnityGuid inverseGuid = new UnityGuid(originalGuid.ToByteArray());
|
||||
UnityGuid equivalentGuid = new UnityGuid(inverseGuid.ToByteArray());
|
||||
Assert.That(equivalentGuid, Is.EqualTo(originalGuid));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnityGuidIsTheSameSizeAsSystemGuid()
|
||||
{
|
||||
Assert.That(Unsafe.SizeOf<UnityGuid>(), Is.EqualTo(Unsafe.SizeOf<Guid>()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToBytesMethod()
|
||||
{
|
||||
Guid originalGuid = Guid.NewGuid();
|
||||
UnityGuid tobyteArrayGuid = new UnityGuid(originalGuid.ToByteArray());
|
||||
UnityGuid memoryMarshallGuid = Unsafe.As<Guid, UnityGuid>(ref originalGuid);
|
||||
Assert.That(memoryMarshallGuid, Is.EqualTo(tobyteArrayGuid));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
global using NUnit.Framework;
|
||||
global using System;
|
||||
@@ -0,0 +1,87 @@
|
||||
#if NET5_0_OR_GREATER
|
||||
namespace AssetRipper.Primitives.Tests;
|
||||
|
||||
public class Utf8StringTests
|
||||
{
|
||||
[Test]
|
||||
public void Utf8StringEqualityWorks()
|
||||
{
|
||||
Utf8String utf8Str = new Utf8String("AssetRipperTestString");
|
||||
Utf8String utf8Str2 = new Utf8String(utf8Str.Data.ToArray());
|
||||
|
||||
Assert.That(utf8Str2, Is.EqualTo(utf8Str));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void StringEmptyIsEquivalentToUtf8StringEmpty()
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(Utf8String.Empty.Data.Length, Is.EqualTo(0));
|
||||
Assert.That(Utf8String.Empty.String, Is.Empty);
|
||||
#pragma warning disable NUnit2010 // Use EqualConstraint for better assertion messages in case of failure
|
||||
Assert.That(Utf8String.Empty == "");
|
||||
Assert.That("" == Utf8String.Empty);
|
||||
#pragma warning restore NUnit2010 // Use EqualConstraint for better assertion messages in case of failure
|
||||
Assert.That(new Utf8String(""), Is.EqualTo(Utf8String.Empty));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-11.0/utf8-string-literals"/>
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Utf8StringMadeFromUtf8LiteralIsTheSameAsMadeFromConstantString()
|
||||
{
|
||||
Utf8String dataString = (Utf8String)"AssetRipperTestString"u8;
|
||||
Utf8String systemString = "AssetRipperTestString";
|
||||
Assert.That(dataString, Is.EqualTo(systemString));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConcatenationBetweenTwoUtf8StringsWorks()
|
||||
{
|
||||
Utf8String dataString = (Utf8String)"AssetRipperTestString"u8;
|
||||
Utf8String systemString = "AssetRipperTestString";
|
||||
Utf8String concatenated = dataString + systemString;
|
||||
Assert.That(concatenated, Is.EqualTo((Utf8String)"AssetRipperTestStringAssetRipperTestString"u8));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConcatenationBetweenThreeUtf8StringsWorksTheSameAsAddition()
|
||||
{
|
||||
Utf8String added = "String1"u8 + new Utf8String("2") + "3"u8;
|
||||
Utf8String concatenated = Utf8String.Concat("String1"u8, "2"u8, "3"u8);
|
||||
Assert.That(added, Is.EqualTo(concatenated));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConcatenationBetweenFourUtf8StringsWorks()
|
||||
{
|
||||
Utf8String string1 = (Utf8String)"hello"u8;
|
||||
Utf8String string2 = (Utf8String)" world"u8;
|
||||
Utf8String string3 = (Utf8String)"!"u8;
|
||||
Utf8String string4 = (Utf8String)", how are you?"u8;
|
||||
Utf8String concatenated = Utf8String.Concat(string1, string2, string3, string4);
|
||||
Assert.That(concatenated, Is.EqualTo((Utf8String)"hello world!, how are you?"u8));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNullOrEmptyReturnsTrueForEmptyString()
|
||||
{
|
||||
Assert.That(Utf8String.IsNullOrEmpty(Utf8String.Empty), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNullOrEmptyReturnsTrueForNullString()
|
||||
{
|
||||
Assert.That(Utf8String.IsNullOrEmpty(null), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNullOrEmptyReturnsFalseForNonEmptyString()
|
||||
{
|
||||
Assert.That(Utf8String.IsNullOrEmpty((Utf8String)"AssetRipperTestString"u8), Is.False);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -3,10 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32112.339
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VersionUtilities", "VersionUtilities\VersionUtilities.csproj", "{5628CFCF-F89B-4191-808C-5EE5FB327B63}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VersionUtilities.Tests", "VersionUtilities.Tests\VersionUtilities.Tests.csproj", "{451F668C-6079-40FD-A79B-96A2CA0C5AAC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36D291AB-E468-4CE7-8DB3-2C46639FE5AE}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.gitignore = .gitignore
|
||||
@@ -24,20 +20,24 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{
|
||||
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.Primitives", "AssetRipper.Primitives\AssetRipper.Primitives.csproj", "{0B77D2BD-812E-4717-8D11-2F6C09167C41}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.Primitives.Tests", "AssetRipper.Primitives.Tests\AssetRipper.Primitives.Tests.csproj", "{545E670D-141B-4F05-9AFD-2F184A59EADC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5628CFCF-F89B-4191-808C-5EE5FB327B63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5628CFCF-F89B-4191-808C-5EE5FB327B63}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5628CFCF-F89B-4191-808C-5EE5FB327B63}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5628CFCF-F89B-4191-808C-5EE5FB327B63}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{451F668C-6079-40FD-A79B-96A2CA0C5AAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{451F668C-6079-40FD-A79B-96A2CA0C5AAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{451F668C-6079-40FD-A79B-96A2CA0C5AAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{451F668C-6079-40FD-A79B-96A2CA0C5AAC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0B77D2BD-812E-4717-8D11-2F6C09167C41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0B77D2BD-812E-4717-8D11-2F6C09167C41}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0B77D2BD-812E-4717-8D11-2F6C09167C41}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0B77D2BD-812E-4717-8D11-2F6C09167C41}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{545E670D-141B-4F05-9AFD-2F184A59EADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{545E670D-141B-4F05-9AFD-2F184A59EADC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{545E670D-141B-4F05-9AFD-2F184A59EADC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{545E670D-141B-4F05-9AFD-2F184A59EADC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -0,0 +1,58 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net35;net40;net461;netstandard2.0;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<Authors>ds5678</Authors>
|
||||
<Company>AssetRipper</Company>
|
||||
<Version>3.2.0</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<PackageId>AssetRipper.Primitives</PackageId>
|
||||
<PackageTags>C# assetripper unity unity3d</PackageTags>
|
||||
<RepositoryUrl>https://github.com/AssetRipper/AssetRipper.Primitives</RepositoryUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageProjectUrl>https://github.com/AssetRipper/AssetRipper.Primitives</PackageProjectUrl>
|
||||
<Copyright>Copyright (c) 2022-2025 ds5678</Copyright>
|
||||
<Description>Managed library for primitive types used in AssetRipper.</Description>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))">
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
|
||||
<IsAotCompatible>true</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="PolySharp" Version="1.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net461'">
|
||||
<PackageReference Include="Microsoft.Bcl.HashCode">
|
||||
<Version>1.1.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Memory">
|
||||
<Version>4.5.5</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
|
||||
<PackageReference Include="System.ValueTuple">
|
||||
<Version>4.5.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace AssetRipper.VersionUtilities.Extensions;
|
||||
namespace AssetRipper.Primitives.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for <see cref="char"/>
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AssetRipper.VersionUtilities.Extensions;
|
||||
namespace AssetRipper.Primitives.Extensions;
|
||||
|
||||
internal static class RegexExtensions
|
||||
{
|
||||
File renamed without changes.
@@ -0,0 +1,278 @@
|
||||
#if NET5_0_OR_GREATER || NETSTANDARD2_0_OR_GREATER || NET461_OR_GREATER
|
||||
using System.Buffers.Binary;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
#if NET5_0_OR_GREATER
|
||||
[System.Text.Json.Serialization.JsonConverter(typeof(UnityGuidJsonConverter))]
|
||||
#endif
|
||||
public readonly record struct UnityGuid
|
||||
#if NET7_0_OR_GREATER
|
||||
: System.Numerics.IEqualityOperators<UnityGuid, UnityGuid, bool>
|
||||
#endif
|
||||
{
|
||||
public UnityGuid(Guid guid)
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
Span<byte> guidData = stackalloc byte[16];
|
||||
bool success = guid.TryWriteBytes(guidData);
|
||||
Debug.Assert(success);
|
||||
#else
|
||||
Span<byte> guidData = guid.ToByteArray();
|
||||
#endif
|
||||
ConvertSystemOrUnityBytes(guidData, guidData);
|
||||
Data0 = ReadUInt32LittleEndian(guidData, 0);
|
||||
Data1 = ReadUInt32LittleEndian(guidData, 1);
|
||||
Data2 = ReadUInt32LittleEndian(guidData, 2);
|
||||
Data3 = ReadUInt32LittleEndian(guidData, 3);
|
||||
}
|
||||
|
||||
public UnityGuid(ReadOnlySpan<byte> guidData)
|
||||
{
|
||||
Data0 = ReadUInt32LittleEndian(guidData, 0);
|
||||
Data1 = ReadUInt32LittleEndian(guidData, 1);
|
||||
Data2 = ReadUInt32LittleEndian(guidData, 2);
|
||||
Data3 = ReadUInt32LittleEndian(guidData, 3);
|
||||
}
|
||||
|
||||
public UnityGuid(uint data0, uint data1, uint data2, uint data3)
|
||||
{
|
||||
Data0 = data0;
|
||||
Data1 = data1;
|
||||
Data2 = data2;
|
||||
Data3 = data3;
|
||||
}
|
||||
|
||||
public static UnityGuid NewGuid()
|
||||
{
|
||||
//This is not an acceptable way to convert between Unity and System Guids.
|
||||
//We only do it this way to efficiently get 16 random bytes.
|
||||
//We don't care about official Guid validity because Unity does not care either.
|
||||
if (Unsafe.SizeOf<Guid>() == Unsafe.SizeOf<UnityGuid>())
|
||||
{
|
||||
Guid guid = Guid.NewGuid();
|
||||
return Unsafe.As<Guid, UnityGuid>(ref guid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ThrowOrReturnDefault();
|
||||
}
|
||||
|
||||
static UnityGuid ThrowOrReturnDefault()
|
||||
{
|
||||
#if DEBUG
|
||||
throw new InvalidCastException($"{nameof(UnityGuid)} struct size does not match {nameof(Guid)}.");
|
||||
#else
|
||||
return default;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public static explicit operator UnityGuid(Guid systemGuid) => new UnityGuid(systemGuid);
|
||||
|
||||
public static explicit operator Guid(UnityGuid unityGuid)
|
||||
{
|
||||
Span<byte> span = stackalloc byte[16];
|
||||
unityGuid.Write(span);
|
||||
ConvertSystemOrUnityBytes(span, span);
|
||||
#if NET5_0_OR_GREATER
|
||||
return new Guid(span);
|
||||
#else
|
||||
return new Guid(span.ToArray());
|
||||
#endif
|
||||
}
|
||||
|
||||
private void Write(Span<byte> span)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(span.Slice(0 * sizeof(uint), sizeof(uint)), Data0);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(span.Slice(1 * sizeof(uint), sizeof(uint)), Data1);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(span.Slice(2 * sizeof(uint), sizeof(uint)), Data2);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(span.Slice(3 * sizeof(uint), sizeof(uint)), Data3);
|
||||
}
|
||||
|
||||
public byte[] ToByteArray()
|
||||
{
|
||||
byte[] result = new byte[16];
|
||||
Write(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
Span<char> span = stackalloc char[32];
|
||||
ToString(span);
|
||||
return CreateString(span);
|
||||
|
||||
static string CreateString(Span<char> span)
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
return new string(span);
|
||||
#else
|
||||
return new string(span.ToArray());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public void ToString(Span<char> buffer)
|
||||
{
|
||||
ReadOnlySpan<char> span = stackalloc char[32]
|
||||
{
|
||||
GetHexChar(Data0, 0),
|
||||
GetHexChar(Data0, 4),
|
||||
GetHexChar(Data0, 8),
|
||||
GetHexChar(Data0, 12),
|
||||
GetHexChar(Data0, 16),
|
||||
GetHexChar(Data0, 20),
|
||||
GetHexChar(Data0, 24),
|
||||
GetHexChar(Data0, 28),
|
||||
GetHexChar(Data1, 0),
|
||||
GetHexChar(Data1, 4),
|
||||
GetHexChar(Data1, 8),
|
||||
GetHexChar(Data1, 12),
|
||||
GetHexChar(Data1, 16),
|
||||
GetHexChar(Data1, 20),
|
||||
GetHexChar(Data1, 24),
|
||||
GetHexChar(Data1, 28),
|
||||
GetHexChar(Data2, 0),
|
||||
GetHexChar(Data2, 4),
|
||||
GetHexChar(Data2, 8),
|
||||
GetHexChar(Data2, 12),
|
||||
GetHexChar(Data2, 16),
|
||||
GetHexChar(Data2, 20),
|
||||
GetHexChar(Data2, 24),
|
||||
GetHexChar(Data2, 28),
|
||||
GetHexChar(Data3, 0),
|
||||
GetHexChar(Data3, 4),
|
||||
GetHexChar(Data3, 8),
|
||||
GetHexChar(Data3, 12),
|
||||
GetHexChar(Data3, 16),
|
||||
GetHexChar(Data3, 20),
|
||||
GetHexChar(Data3, 24),
|
||||
GetHexChar(Data3, 28),
|
||||
};
|
||||
|
||||
span.CopyTo(buffer);
|
||||
|
||||
static char GetHexChar(uint value, int offset)
|
||||
{
|
||||
int index = unchecked((int)((value >> offset) & 0xF));
|
||||
return "0123456789abcdef"[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read little-endian <see cref="uint"/> from <see cref="ReadOnlySpan{T}"/>
|
||||
/// </summary>
|
||||
/// <param name="byteSpan">A span of bytes.</param>
|
||||
/// <param name="index">The ith <see cref="uint"/> in <paramref name="byteSpan"/>.</param>
|
||||
/// <returns></returns>
|
||||
private static uint ReadUInt32LittleEndian(ReadOnlySpan<byte> byteSpan, int index)
|
||||
{
|
||||
return BinaryPrimitives.ReadUInt32LittleEndian(byteSpan.Slice(index * sizeof(uint), sizeof(uint)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts system bytes to unity bytes, or the reverse
|
||||
/// </summary>
|
||||
/// <param name="input">A 16 byte input span</param>
|
||||
/// <param name="output">A 16 byte output span</param>
|
||||
/// <returns>The same span: <paramref name="input"/></returns>
|
||||
/// <exception cref="ArgumentException">Span doesn't have 16 elements</exception>
|
||||
private static void ConvertSystemOrUnityBytes(scoped ReadOnlySpan<byte> input, scoped Span<byte> output)
|
||||
{
|
||||
if (input.Length != 16)
|
||||
{
|
||||
throw new ArgumentException($"Invalid length: {input.Length}", nameof(input));
|
||||
}
|
||||
if (output.Length != 16)
|
||||
{
|
||||
throw new ArgumentException($"Invalid length: {output.Length}", nameof(output));
|
||||
}
|
||||
|
||||
//Unity Guid's are in big endian, so the bytes have to be flipped for multibyte fields
|
||||
(output[0], output[1], output[2], output[3]) = (input[3], input[2], input[1], input[0]);
|
||||
(output[4], output[5]) = (input[5], input[4]);
|
||||
(output[6], output[7]) = (input[7], input[6]);
|
||||
input.Slice(8).CopyTo(output.Slice(8));
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
//AB becomes BA
|
||||
uint value = output[i];
|
||||
output[i] = (byte)(unchecked(value << 4 & 0xF0) | unchecked(value >> 4 & 0xF));
|
||||
}
|
||||
}
|
||||
|
||||
public static UnityGuid Parse(string guidString) => new UnityGuid(Guid.Parse(guidString));
|
||||
|
||||
/// <summary>
|
||||
/// Make a guid by MD5 hashing a string
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The returned guid is most likely not "valid" by official standards. However, Unity doesn't seem to care.
|
||||
/// </remarks>
|
||||
/// <param name="str">Input string. Can be any length</param>
|
||||
/// <returns>A stable guid corresponding to the input string</returns>
|
||||
public static UnityGuid Md5Hash(string str) => Md5Hash(Encoding.UTF8.GetBytes(str));
|
||||
|
||||
/// <summary>
|
||||
/// Make a guid by MD5 hashing some input data
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The returned guid is most likely not "valid" by official standards. However, Unity doesn't seem to care.
|
||||
/// </remarks>
|
||||
/// <param name="input">Input data. Can be any length</param>
|
||||
/// <returns>A stable guid corresponding to the <paramref name="input"/>.</returns>
|
||||
public static UnityGuid Md5Hash(scoped ReadOnlySpan<byte> input)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
HashData(input, buffer);
|
||||
ConvertSystemOrUnityBytes(buffer, buffer);
|
||||
return new UnityGuid(buffer);
|
||||
}
|
||||
|
||||
private static void HashData(ReadOnlySpan<byte> data, Span<byte> buffer)
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
MD5.HashData(data, buffer);
|
||||
#else
|
||||
MD5.Create().ComputeHash(data.ToArray()).AsSpan().CopyTo(buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static UnityGuid Md5Hash(scoped ReadOnlySpan<byte> assemblyName, scoped ReadOnlySpan<byte> @namespace, scoped ReadOnlySpan<byte> className)
|
||||
{
|
||||
int length = assemblyName.Length + @namespace.Length + className.Length;
|
||||
Span<byte> input = length < 1024
|
||||
? stackalloc byte[length]
|
||||
#if NET5_0_OR_GREATER
|
||||
: GC.AllocateUninitializedArray<byte>(length);
|
||||
#else
|
||||
: new byte[length];
|
||||
#endif
|
||||
assemblyName.CopyTo(input);
|
||||
@namespace.CopyTo(input.Slice(assemblyName.Length));
|
||||
className.CopyTo(input.Slice(assemblyName.Length + @namespace.Length));
|
||||
return Md5Hash(input);
|
||||
}
|
||||
|
||||
public bool IsZero => Data0 == 0 && Data1 == 0 && Data2 == 0 && Data3 == 0;
|
||||
|
||||
public uint Data0 { get; }
|
||||
public uint Data1 { get; }
|
||||
public uint Data2 { get; }
|
||||
public uint Data3 { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 0x0000000DEADBEEF15DEADF00D0000000
|
||||
/// </summary>
|
||||
public static UnityGuid MissingReference { get; } = new UnityGuid(0xD0000000, 0x1FEEBDAE, 0x00FDAED5, 0x0000000D);
|
||||
|
||||
public static UnityGuid Zero => default;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#if NET5_0_OR_GREATER
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
public sealed class UnityGuidJsonConverter : JsonConverter<UnityGuid>
|
||||
{
|
||||
public override UnityGuid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return UnityGuid.Parse(reader.GetString() ?? throw new JsonException("String was read as null"));
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, UnityGuid value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
public readonly partial struct UnityVersion
|
||||
{
|
||||
private const ulong subMajorMask = 0x0000FFFFFFFFFFFFUL;
|
||||
private const ulong subMinorMask = 0x00000000FFFFFFFFUL;
|
||||
private const ulong subBuildMask = 0x000000000000FFFFUL;
|
||||
private const ulong subTypeMask = 0x00000000000000FFUL;
|
||||
|
||||
public bool Equals(ushort major) => this == From(major);
|
||||
public bool Equals(ushort major, ushort minor) => this == From(major, minor);
|
||||
public bool Equals(ushort major, ushort minor, ushort build) => this == From(major, minor, build);
|
||||
public bool Equals(ushort major, ushort minor, ushort build, UnityVersionType type) => this == From(major, minor, build, type);
|
||||
public bool Equals(ushort major, ushort minor, ushort build, UnityVersionType type, byte typeNumber) => this == new UnityVersion(major, minor, build, type, typeNumber);
|
||||
public bool Equals(string version) => this == Parse(version);
|
||||
|
||||
public bool LessThan(ushort major) => this < From(major);
|
||||
public bool LessThan(ushort major, ushort minor) => this < From(major, minor);
|
||||
public bool LessThan(ushort major, ushort minor, ushort build) => this < From(major, minor, build);
|
||||
public bool LessThan(ushort major, ushort minor, ushort build, UnityVersionType type) => this < From(major, minor, build, type);
|
||||
public bool LessThan(ushort major, ushort minor, ushort build, UnityVersionType type, byte typeNumber) => this < new UnityVersion(major, minor, build, type, typeNumber);
|
||||
public bool LessThan(string version) => this < Parse(version);
|
||||
|
||||
public bool LessThanOrEquals(ushort major) => this <= From(major);
|
||||
public bool LessThanOrEquals(ushort major, ushort minor) => this <= From(major, minor);
|
||||
public bool LessThanOrEquals(ushort major, ushort minor, ushort build) => this <= From(major, minor, build);
|
||||
public bool LessThanOrEquals(ushort major, ushort minor, ushort build, UnityVersionType type) => this <= From(major, minor, build, type);
|
||||
public bool LessThanOrEquals(ushort major, ushort minor, ushort build, UnityVersionType type, byte typeNumber) => this <= new UnityVersion(major, minor, build, type, typeNumber);
|
||||
public bool LessThanOrEquals(string version) => this <= Parse(version);
|
||||
|
||||
public bool GreaterThan(ushort major) => this > From(major);
|
||||
public bool GreaterThan(ushort major, ushort minor) => this > From(major, minor);
|
||||
public bool GreaterThan(ushort major, ushort minor, ushort build) => this > From(major, minor, build);
|
||||
public bool GreaterThan(ushort major, ushort minor, ushort build, UnityVersionType type) => this > From(major, minor, build, type);
|
||||
public bool GreaterThan(ushort major, ushort minor, ushort build, UnityVersionType type, byte typeNumber) => this > new UnityVersion(major, minor, build, type, typeNumber);
|
||||
public bool GreaterThan(string version) => this > Parse(version);
|
||||
|
||||
public bool GreaterThanOrEquals(ushort major) => this >= From(major);
|
||||
public bool GreaterThanOrEquals(ushort major, ushort minor) => this >= From(major, minor);
|
||||
public bool GreaterThanOrEquals(ushort major, ushort minor, ushort build) => this >= From(major, minor, build);
|
||||
public bool GreaterThanOrEquals(ushort major, ushort minor, ushort build, UnityVersionType type) => this >= From(major, minor, build, type);
|
||||
public bool GreaterThanOrEquals(ushort major, ushort minor, ushort build, UnityVersionType type, byte typeNumber) => this >= new UnityVersion(major, minor, build, type, typeNumber);
|
||||
public bool GreaterThanOrEquals(string version) => this >= Parse(version);
|
||||
|
||||
private UnityVersion From(ushort major)
|
||||
{
|
||||
ulong data = ((ulong)major << majorOffset) | subMajorMask & m_data;
|
||||
return new UnityVersion(data);
|
||||
}
|
||||
private UnityVersion From(ushort major, ushort minor)
|
||||
{
|
||||
ulong data = ((ulong)major << majorOffset) | ((ulong)minor << minorOffset) | subMinorMask & m_data;
|
||||
return new UnityVersion(data);
|
||||
}
|
||||
private UnityVersion From(ushort major, ushort minor, ushort build)
|
||||
{
|
||||
ulong data = ((ulong)major << majorOffset) | ((ulong)minor << minorOffset) | ((ulong)build << buildOffset) |
|
||||
subBuildMask & m_data;
|
||||
return new UnityVersion(data);
|
||||
}
|
||||
private UnityVersion From(ushort major, ushort minor, ushort build, UnityVersionType type)
|
||||
{
|
||||
ulong data = ((ulong)major << majorOffset) | ((ulong)minor << minorOffset) | ((ulong)build << buildOffset) |
|
||||
((ulong)(ushort)type << typeOffset) | subTypeMask & m_data;
|
||||
return new UnityVersion(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
|
||||
namespace AssetRipper.Primitives;
|
||||
partial struct UnityVersion :
|
||||
IComparisonOperators<UnityVersion, UnityVersion, bool>,
|
||||
IEqualityOperators<UnityVersion, UnityVersion, bool>,
|
||||
IMinMaxValue<UnityVersion>,
|
||||
IParsable<UnityVersion>
|
||||
{
|
||||
static UnityVersion IMinMaxValue<UnityVersion>.MaxValue => MaxVersion;
|
||||
|
||||
static UnityVersion IMinMaxValue<UnityVersion>.MinValue => MinVersion;
|
||||
|
||||
static UnityVersion IParsable<UnityVersion>.Parse(string s, IFormatProvider? provider)
|
||||
{
|
||||
return Parse(s);
|
||||
}
|
||||
|
||||
static bool IParsable<UnityVersion>.TryParse(string? s, IFormatProvider? provider, out UnityVersion result)
|
||||
{
|
||||
return TryParse(s, out result, out _);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+3
-8
@@ -1,4 +1,4 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
public readonly partial struct UnityVersion : IEquatable<UnityVersion>, IComparable, IComparable<UnityVersion>
|
||||
{
|
||||
@@ -27,12 +27,7 @@ public readonly partial struct UnityVersion : IEquatable<UnityVersion>, ICompara
|
||||
/// </returns>
|
||||
public int CompareTo(UnityVersion other)
|
||||
{
|
||||
if (this > other)
|
||||
return 1;
|
||||
else if (this < other)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
return m_data.CompareTo(other.m_data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -55,6 +50,6 @@ public readonly partial struct UnityVersion : IEquatable<UnityVersion>, ICompara
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return unchecked(827 + 911 * m_data.GetHashCode());
|
||||
return m_data.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#if NET5_0_OR_GREATER
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
[JsonConverter(typeof(UnityVersionJsonConverter))]
|
||||
partial struct UnityVersion
|
||||
{
|
||||
}
|
||||
#endif
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
public readonly partial struct UnityVersion
|
||||
{
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
namespace AssetRipper.Primitives;
|
||||
partial struct UnityVersion
|
||||
{
|
||||
/// <summary>
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
public readonly partial struct UnityVersion
|
||||
{
|
||||
@@ -0,0 +1,155 @@
|
||||
using AssetRipper.Primitives.Extensions;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AssetRipper.Primitives;
|
||||
public readonly partial struct UnityVersion
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialize the version as a string using <see cref="UnityVersionFormatFlags.Default"/>.
|
||||
/// </summary>
|
||||
/// <returns>A new string like 2019.4.3f1</returns>
|
||||
public override string ToString() => ToString(UnityVersionFormatFlags.Default);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the version as a string
|
||||
/// </summary>
|
||||
/// <param name="flags">The flags to control how the version is formatted</param>
|
||||
/// <returns>A new string containing the formatted version.</returns>
|
||||
public string ToString(UnityVersionFormatFlags flags)
|
||||
{
|
||||
return (flags & UnityVersionFormatFlags.ExcludeType) != 0
|
||||
? ToStringWithoutType()
|
||||
: Type is not UnityVersionType.China || (flags & UnityVersionFormatFlags.UseShortChineseFormat) != 0
|
||||
? $"{Major}.{Minor}.{Build}{Type.ToCharacter()}{TypeNumber}"
|
||||
: $"{Major}.{Minor}.{Build}f1c{TypeNumber}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the version as a string
|
||||
/// </summary>
|
||||
/// <param name="flags">The flags to control how the version is formatted</param>
|
||||
/// <param name="customEngineString">The custom engine string to be appended</param>
|
||||
/// <returns>A new string containing the formatted version.</returns>
|
||||
public string ToString(UnityVersionFormatFlags flags,
|
||||
#if !NET6_0_OR_GREATER
|
||||
string customEngineString = "")
|
||||
#else
|
||||
ReadOnlySpan<char> customEngineString = default)
|
||||
#endif
|
||||
{
|
||||
return customEngineString.Length == 0
|
||||
? ToString(flags)
|
||||
: (flags & UnityVersionFormatFlags.ExcludeType) != 0
|
||||
? ToStringWithoutType()
|
||||
: Type is not UnityVersionType.China || (flags & UnityVersionFormatFlags.UseShortChineseFormat) != 0
|
||||
? $"{Major}.{Minor}.{Build}{Type.ToCharacter()}{TypeNumber}{customEngineString}"
|
||||
: $"{Major}.{Minor}.{Build}f1c{TypeNumber}{customEngineString}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the version as a string using only <see cref="Major"/>, <see cref="Minor"/>, and <see cref="Build"/>.
|
||||
/// </summary>
|
||||
/// <returns>A new string like 2019.4.3</returns>
|
||||
public string ToStringWithoutType()
|
||||
{
|
||||
return $"{Major}.{Minor}.{Build}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a normal Unity version string
|
||||
/// </summary>
|
||||
/// <param name="s">A string to parse</param>
|
||||
/// <returns>The parsed Unity version</returns>
|
||||
/// <exception cref="ArgumentException">If the string is in an invalid format</exception>
|
||||
public static UnityVersion Parse(string s) => Parse(s, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Parse a normal Unity version string
|
||||
/// </summary>
|
||||
/// <param name="s">A string to parse</param>
|
||||
/// <param name="customEngine">Not null if this version was generated by a custom Unity Engine.</param>
|
||||
/// <returns>The parsed Unity version</returns>
|
||||
/// <exception cref="ArgumentException">If the string is in an invalid format</exception>
|
||||
public static UnityVersion Parse(string s, out string? customEngine)
|
||||
{
|
||||
return TryParse(s, out UnityVersion version, out customEngine)
|
||||
? version
|
||||
: throw new ArgumentException($"Invalid version format: {s}", nameof(s));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to parse a normal Unity version string
|
||||
/// </summary>
|
||||
/// <param name="s">A string to parse</param>
|
||||
/// <param name="version">The parsed Unity version</param>
|
||||
/// <param name="customEngine">Not null if this version was generated by a custom Unity Engine.</param>
|
||||
/// <returns>True if parsing was successful</returns>
|
||||
public static bool TryParse(string? s, out UnityVersion version, out string? customEngine)
|
||||
{
|
||||
if (s is null || s.Length is 0)
|
||||
{
|
||||
customEngine = null;
|
||||
version = default;
|
||||
return false;
|
||||
}
|
||||
else if (UnityVersionRegexes.GetChinaRegex().TryMatch(s, out Match? match))
|
||||
{
|
||||
int major = int.Parse(match.Groups[1].Value);
|
||||
int minor = int.Parse(match.Groups[2].Value);
|
||||
int build = int.Parse(match.Groups[3].Value);
|
||||
int typeNumber = int.Parse(match.Groups[4].Value);
|
||||
customEngine = GetNullableString(match.Groups[5]);
|
||||
version = new UnityVersion((ushort)major, (ushort)minor, (ushort)build, UnityVersionType.China, (byte)typeNumber);
|
||||
return true;
|
||||
}
|
||||
else if(UnityVersionRegexes.GetNormalRegex().TryMatch(s, out match))
|
||||
{
|
||||
int major = int.Parse(match.Groups[1].Value);
|
||||
int minor = int.Parse(match.Groups[2].Value);
|
||||
int build = int.Parse(match.Groups[3].Value);
|
||||
char type = match.Groups[4].Value[0];
|
||||
int typeNumber = int.Parse(match.Groups[5].Value);
|
||||
customEngine = GetNullableString(match.Groups[6]);
|
||||
version = new UnityVersion((ushort)major, (ushort)minor, (ushort)build, type.ToUnityVersionType(), (byte)typeNumber);
|
||||
return true;
|
||||
}
|
||||
else if (UnityVersionRegexes.GetMajorMinorBuildRegex().TryMatch(s, out match))
|
||||
{
|
||||
int major = int.Parse(match.Groups[1].Value);
|
||||
int minor = int.Parse(match.Groups[2].Value);
|
||||
int build = int.Parse(match.Groups[3].Value);
|
||||
customEngine = null;
|
||||
version = major is not 0 || minor is not 0 || build is not 0
|
||||
? new UnityVersion((ushort)major, (ushort)minor, (ushort)build, UnityVersionType.Final, 1)
|
||||
: default;
|
||||
return true;
|
||||
}
|
||||
else if (UnityVersionRegexes.GetMajorMinorRegex().TryMatch(s, out match))
|
||||
{
|
||||
int major = int.Parse(match.Groups[1].Value);
|
||||
int minor = int.Parse(match.Groups[2].Value);
|
||||
customEngine = null;
|
||||
version = major is not 0 || minor is not 0
|
||||
? new UnityVersion((ushort)major, (ushort)minor, 0, UnityVersionType.Final, 1)
|
||||
: default;
|
||||
return true;
|
||||
}
|
||||
else if (UnityVersionRegexes.GetMajorRegex().TryMatch(s, out match))
|
||||
{
|
||||
int major = int.Parse(match.Groups[1].Value);
|
||||
customEngine = null;
|
||||
version = major is not 0
|
||||
? new UnityVersion((ushort)major, 0, 0, UnityVersionType.Final, 1)
|
||||
: default;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
customEngine = null;
|
||||
version = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
static string? GetNullableString(Capture capture) => capture.Length == 0 ? null : capture.Value;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// A value type for holding Unity versions
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// Flags used when formatting a <see cref="UnityVersion"/>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum UnityVersionFormatFlags
|
||||
{
|
||||
/// <summary>
|
||||
/// The default flags when formatting.
|
||||
/// </summary>
|
||||
Default = 0,
|
||||
/// <summary>
|
||||
/// Exclude <see cref="UnityVersion.Type"/>, <see cref="UnityVersion.TypeNumber"/>, and the custom engine number when formatting.
|
||||
/// </summary>
|
||||
ExcludeType = 1 << 0,
|
||||
/// <summary>
|
||||
/// Format chinese versions as 2019.4.3c1 rather than 2019.4.3f1c1
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An example of the long version can be found in 'unity editor resources' and 'unity_builtin_extra' for the <see href="https://unity.cn/release-notes/full/2019/2019.4.40f1">2019.4.40f1c1</see> version.<br/>
|
||||
/// An example of the short version can be found in <see href="https://github.com/AssetRipper/AssetRipper/issues/841"/>.
|
||||
/// </remarks>
|
||||
UseShortChineseFormat = 1 << 1,
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#if NET5_0_OR_GREATER
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
public sealed class UnityVersionJsonConverter : JsonConverter<UnityVersion>
|
||||
{
|
||||
public override UnityVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return UnityVersion.Parse(reader.GetString() ?? throw new JsonException("String was read as null"));
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, UnityVersion value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
internal static partial class UnityVersionRegexes
|
||||
{
|
||||
#if NET35
|
||||
private const RegexOptions Options = RegexOptions.None;
|
||||
#else
|
||||
private const RegexOptions Options = RegexOptions.Compiled;
|
||||
#endif
|
||||
|
||||
[StringSyntax("regex")]
|
||||
private const string Major = @"^([0-9]+)$";
|
||||
|
||||
[StringSyntax("regex")]
|
||||
private const string MajorMinor = @"^([0-9]+)\.([0-9]+)$";
|
||||
|
||||
[StringSyntax("regex")]
|
||||
private const string MajorMinorBuild = @"^([0-9]+)\.([0-9]+)\.([0-9]+)$";
|
||||
|
||||
[StringSyntax("regex")]
|
||||
private const string Normal = @"^([0-9]+)\.([0-9]+)\.([0-9]+)\.?([abcfpx])([0-9]+)((?:.|[\r\n])+)?$";
|
||||
|
||||
[StringSyntax("regex")]
|
||||
private const string China = @"^([0-9]+)\.([0-9]+)\.([0-9]+)\.?f1c([0-9]+)((?:.|[\r\n])+)?$";
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[GeneratedRegex(Major, Options)]
|
||||
public static partial Regex GetMajorRegex();
|
||||
#else
|
||||
private static readonly Regex majorRegex = new Regex(Major, Options);
|
||||
public static Regex GetMajorRegex() => majorRegex;
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[GeneratedRegex(MajorMinor, Options)]
|
||||
public static partial Regex GetMajorMinorRegex();
|
||||
#else
|
||||
private static readonly Regex majorMinorRegex = new Regex(MajorMinor, Options);
|
||||
public static Regex GetMajorMinorRegex() => majorMinorRegex;
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[GeneratedRegex(MajorMinorBuild, Options)]
|
||||
public static partial Regex GetMajorMinorBuildRegex();
|
||||
#else
|
||||
private static readonly Regex majorMinorBuildRegex = new Regex(MajorMinorBuild, Options);
|
||||
public static Regex GetMajorMinorBuildRegex() => majorMinorBuildRegex;
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[GeneratedRegex(Normal, Options)]
|
||||
public static partial Regex GetNormalRegex();
|
||||
#else
|
||||
private static readonly Regex normalRegex = new Regex(Normal, Options);
|
||||
public static Regex GetNormalRegex() => normalRegex;
|
||||
#endif
|
||||
|
||||
#if NET7_0_OR_GREATER
|
||||
[GeneratedRegex(China, Options)]
|
||||
public static partial Regex GetChinaRegex();
|
||||
#else
|
||||
private static readonly Regex chinaRegex = new Regex(China, Options);
|
||||
public static Regex GetChinaRegex() => chinaRegex;
|
||||
#endif
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// An enumeration representing the letter in a Unity Version string
|
||||
@@ -1,2 +1 @@
|
||||
global using System;
|
||||
global using System.IO;
|
||||
@@ -0,0 +1,321 @@
|
||||
#if NET5_0_OR_GREATER || NETSTANDARD2_0_OR_GREATER || NET461_OR_GREATER
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
/// <summary>
|
||||
/// An immutable, thread-safe, representation of UTF8 text data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Conversions to and from <see cref="string"/> are handled by <see cref="Encoding.UTF8"/>.
|
||||
/// </remarks>
|
||||
#if NET5_0_OR_GREATER
|
||||
[System.Text.Json.Serialization.JsonConverter(typeof(Utf8StringJsonConverter))]
|
||||
#endif
|
||||
public sealed class Utf8String :
|
||||
#if NET7_0_OR_GREATER
|
||||
System.Numerics.IEqualityOperators<Utf8String?, Utf8String?, bool>,
|
||||
System.Numerics.IEqualityOperators<Utf8String?, string?, bool>,
|
||||
#endif
|
||||
IEquatable<Utf8String>
|
||||
{
|
||||
private readonly byte[] data;
|
||||
private string? cachedString;
|
||||
|
||||
public static Utf8String Empty { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The empty string. A shared instance is available in <see cref="Empty"/>.
|
||||
/// </summary>
|
||||
public Utf8String()
|
||||
{
|
||||
data = [];
|
||||
cachedString = "";
|
||||
}
|
||||
|
||||
public Utf8String(ReadOnlySpan<byte> data) : this(data.ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
private Utf8String(byte[] data)
|
||||
{
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Utf8String(string @string)
|
||||
{
|
||||
cachedString = @string;
|
||||
data = Encoding.UTF8.GetBytes(@string);
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> Data => data;
|
||||
|
||||
public string String
|
||||
{
|
||||
get
|
||||
{
|
||||
cachedString ??= Encoding.UTF8.GetString(data);
|
||||
return cachedString;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEmpty => data.Length == 0;
|
||||
|
||||
public override string ToString() => String;
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
HashCode hash = new();
|
||||
#if NET6_0_OR_GREATER
|
||||
hash.AddBytes(data);
|
||||
#else
|
||||
foreach (byte b in data)
|
||||
{
|
||||
hash.Add(b);
|
||||
}
|
||||
#endif
|
||||
return hash.ToHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj) => Equals(obj as Utf8String);
|
||||
|
||||
public bool Equals(Utf8String? other) => other is not null && Data.SequenceEqual(other.Data);
|
||||
|
||||
public static bool operator ==(Utf8String? string1, Utf8String? string2)
|
||||
{
|
||||
return EqualityComparer<Utf8String>.Default.Equals(string1, string2);
|
||||
}
|
||||
public static bool operator !=(Utf8String? string1, Utf8String? string2) => !(string1 == string2);
|
||||
|
||||
public static bool operator ==(Utf8String? utf8String, string? @string) => utf8String?.String == @string;
|
||||
public static bool operator !=(Utf8String? utf8String, string? @string) => utf8String?.String != @string;
|
||||
|
||||
public static bool operator ==(string? @string, Utf8String? utf8String) => utf8String?.String == @string;
|
||||
public static bool operator !=(string? @string, Utf8String? utf8String) => utf8String?.String != @string;
|
||||
|
||||
[return: NotNullIfNotNull(nameof(@string))]
|
||||
public static implicit operator Utf8String?(string? @string)
|
||||
{
|
||||
return @string is null
|
||||
? null
|
||||
: @string.Length == 0
|
||||
? Empty
|
||||
: new Utf8String(@string);
|
||||
}
|
||||
|
||||
[return: NotNullIfNotNull(nameof(utf8String))]
|
||||
public static implicit operator string?(Utf8String? utf8String)
|
||||
{
|
||||
return utf8String?.String;
|
||||
}
|
||||
|
||||
public static explicit operator Utf8String(ReadOnlySpan<byte> data)
|
||||
{
|
||||
// Explicit conversion because of first class spans
|
||||
// https://github.com/dotnet/roslyn/issues/78314
|
||||
return data.Length == 0 ? Empty : new Utf8String(data);
|
||||
}
|
||||
|
||||
public static implicit operator ReadOnlySpan<byte>(Utf8String? utf8String)
|
||||
{
|
||||
return utf8String is null ? default : utf8String.Data;
|
||||
}
|
||||
|
||||
public static Utf8String operator +(Utf8String? string1, Utf8String? string2)
|
||||
{
|
||||
return Concat(string1, string2);
|
||||
}
|
||||
|
||||
public static Utf8String operator +(Utf8String? string1, ReadOnlySpan<byte> string2)
|
||||
{
|
||||
return string1 is null
|
||||
? string2.Length == 0 ? Empty : new Utf8String(string2)
|
||||
: Concat(string1.Data, string2);
|
||||
}
|
||||
|
||||
public static Utf8String operator +(ReadOnlySpan<byte> string1, Utf8String? string2)
|
||||
{
|
||||
return string2 is null
|
||||
? string1.Length == 0 ? Empty : new Utf8String(string1)
|
||||
: Concat(string1, string2.Data);
|
||||
}
|
||||
|
||||
public static bool IsNullOrEmpty([NotNullWhen(false)] Utf8String? utf8String)
|
||||
{
|
||||
return utf8String is null || utf8String.IsEmpty;
|
||||
}
|
||||
|
||||
public static Utf8String Concat(Utf8String? string1, Utf8String? string2)
|
||||
{
|
||||
if (IsNullOrEmpty(string1))
|
||||
{
|
||||
return string2 ?? Empty;
|
||||
}
|
||||
if (IsNullOrEmpty(string2))
|
||||
{
|
||||
return string1;
|
||||
}
|
||||
|
||||
byte[] data = new byte[string1.data.Length + string2.data.Length];
|
||||
string1.data.CopyTo(data.AsSpan());
|
||||
string2.data.CopyTo(data.AsSpan(string1.data.Length));
|
||||
return new Utf8String(data);
|
||||
}
|
||||
|
||||
public static Utf8String Concat(Utf8String? string1, Utf8String? string2, Utf8String? string3)
|
||||
{
|
||||
if (IsNullOrEmpty(string1))
|
||||
{
|
||||
return Concat(string2, string3);
|
||||
}
|
||||
if (IsNullOrEmpty(string2))
|
||||
{
|
||||
return Concat(string1, string3);
|
||||
}
|
||||
if (IsNullOrEmpty(string3))
|
||||
{
|
||||
return Concat(string1, string2);
|
||||
}
|
||||
|
||||
byte[] data = new byte[string1.data.Length + string2.data.Length + string3.data.Length];
|
||||
string1.data.CopyTo(data.AsSpan());
|
||||
string2.data.CopyTo(data.AsSpan(string1.data.Length));
|
||||
string3.data.CopyTo(data.AsSpan(string1.data.Length + string2.data.Length));
|
||||
return new Utf8String(data);
|
||||
}
|
||||
|
||||
public static Utf8String Concat(Utf8String? string1, Utf8String? string2, Utf8String? string3, Utf8String? string4)
|
||||
{
|
||||
if (IsNullOrEmpty(string1))
|
||||
{
|
||||
return Concat(string2, string3, string4);
|
||||
}
|
||||
if (IsNullOrEmpty(string2))
|
||||
{
|
||||
return Concat(string1, string3, string4);
|
||||
}
|
||||
if (IsNullOrEmpty(string3))
|
||||
{
|
||||
return Concat(string1, string2, string4);
|
||||
}
|
||||
if (IsNullOrEmpty(string4))
|
||||
{
|
||||
return Concat(string1, string2, string3);
|
||||
}
|
||||
|
||||
byte[] data = new byte[string1.data.Length + string2.data.Length + string3.data.Length + string4.data.Length];
|
||||
string1.data.CopyTo(data.AsSpan());
|
||||
string2.data.CopyTo(data.AsSpan(string1.data.Length));
|
||||
string3.data.CopyTo(data.AsSpan(string1.data.Length + string2.data.Length));
|
||||
string4.data.CopyTo(data.AsSpan(string1.data.Length + string2.data.Length + string3.data.Length));
|
||||
return new Utf8String(data);
|
||||
}
|
||||
|
||||
public static Utf8String Concat(params Utf8String?[] values)
|
||||
{
|
||||
if (values is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
}
|
||||
|
||||
int totalLength = 0;
|
||||
foreach (Utf8String? value in values)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
totalLength += value.data.Length;
|
||||
}
|
||||
|
||||
if (totalLength == 0)
|
||||
{
|
||||
return Empty;
|
||||
}
|
||||
|
||||
byte[] data = new byte[totalLength];
|
||||
int offset = 0;
|
||||
foreach (Utf8String? value in values)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
value.data.CopyTo(data.AsSpan(offset));
|
||||
offset += value.data.Length;
|
||||
}
|
||||
|
||||
return new Utf8String(data);
|
||||
}
|
||||
|
||||
public static Utf8String Concat(ReadOnlySpan<byte> string1, ReadOnlySpan<byte> string2)
|
||||
{
|
||||
if (string1.Length == 0)
|
||||
{
|
||||
return string2.Length == 0 ? Empty : new Utf8String(string2);
|
||||
}
|
||||
if (string2.Length == 0)
|
||||
{
|
||||
return new Utf8String(string1);
|
||||
}
|
||||
|
||||
byte[] data = new byte[string1.Length + string2.Length];
|
||||
string1.CopyTo(data);
|
||||
string2.CopyTo(data.AsSpan(string1.Length));
|
||||
return new Utf8String(data);
|
||||
}
|
||||
|
||||
public static Utf8String Concat(ReadOnlySpan<byte> string1, ReadOnlySpan<byte> string2, ReadOnlySpan<byte> string3)
|
||||
{
|
||||
if (string1.Length == 0)
|
||||
{
|
||||
return Concat(string2, string3);
|
||||
}
|
||||
if (string2.Length == 0)
|
||||
{
|
||||
return Concat(string1, string3);
|
||||
}
|
||||
if (string3.Length == 0)
|
||||
{
|
||||
return Concat(string1, string2);
|
||||
}
|
||||
|
||||
byte[] data = new byte[string1.Length + string2.Length + string3.Length];
|
||||
string1.CopyTo(data);
|
||||
string2.CopyTo(data.AsSpan(string1.Length));
|
||||
string3.CopyTo(data.AsSpan(string1.Length + string2.Length));
|
||||
return new Utf8String(data);
|
||||
}
|
||||
|
||||
public static Utf8String Concat(ReadOnlySpan<byte> string1, ReadOnlySpan<byte> string2, ReadOnlySpan<byte> string3, ReadOnlySpan<byte> string4)
|
||||
{
|
||||
if (string1.Length == 0)
|
||||
{
|
||||
return Concat(string2, string3, string4);
|
||||
}
|
||||
if (string2.Length == 0)
|
||||
{
|
||||
return Concat(string1, string3, string4);
|
||||
}
|
||||
if (string3.Length == 0)
|
||||
{
|
||||
return Concat(string1, string2, string4);
|
||||
}
|
||||
if (string4.Length == 0)
|
||||
{
|
||||
return Concat(string1, string2, string3);
|
||||
}
|
||||
|
||||
byte[] data = new byte[string1.Length + string2.Length + string3.Length + string4.Length];
|
||||
string1.CopyTo(data);
|
||||
string2.CopyTo(data.AsSpan(string1.Length));
|
||||
string3.CopyTo(data.AsSpan(string1.Length + string2.Length));
|
||||
string4.CopyTo(data.AsSpan(string1.Length + string2.Length + string3.Length));
|
||||
return new Utf8String(data);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#if NET5_0_OR_GREATER
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AssetRipper.Primitives;
|
||||
|
||||
public sealed class Utf8StringJsonConverter : JsonConverter<Utf8String>
|
||||
{
|
||||
public override Utf8String Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return reader.GetString() ?? throw new JsonException("String was read as null");
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, Utf8String value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,8 +1,8 @@
|
||||
# AssetRipper.VersionUtilities
|
||||
# AssetRipper.Primitives
|
||||
|
||||
[](https://www.nuget.org/packages/AssetRipper.VersionUtilities)
|
||||
[](https://www.nuget.org/packages/AssetRipper.Primitives)
|
||||
|
||||
Managed library for handling Unity versions
|
||||
Managed library for primitive types used in AssetRipper projects.
|
||||
|
||||
# Legal Disclaimers
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
namespace AssetRipper.VersionUtilities.Tests;
|
||||
|
||||
public class ComparisonTests
|
||||
{
|
||||
private static readonly UnityVersion lowValue = new UnityVersion(2017, 3, 0, UnityVersionType.Patch, 1);
|
||||
private static readonly UnityVersion mediumValue = new UnityVersion(2019, 4, 3, UnityVersionType.Final, 1);
|
||||
private static readonly UnityVersion highValue = new UnityVersion(2020, 2, 0, UnityVersionType.Final, 1);
|
||||
|
||||
[Test]
|
||||
public void LessThanIsTransitive()
|
||||
{
|
||||
Assert.IsTrue(lowValue < mediumValue);
|
||||
Assert.IsTrue(mediumValue < highValue);
|
||||
Assert.IsTrue(lowValue < highValue);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GreaterThanIsTransitive()
|
||||
{
|
||||
Assert.IsTrue(mediumValue > lowValue);
|
||||
Assert.IsTrue(highValue > mediumValue);
|
||||
Assert.IsTrue(highValue > lowValue);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WeakInequalityHolds()
|
||||
{
|
||||
UnityVersion low = lowValue;
|
||||
Assert.IsTrue(lowValue <= low);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MinimumValueDefinition()
|
||||
{
|
||||
Assert.IsTrue(UnityVersion.MinVersion == new UnityVersion(ushort.MinValue, ushort.MinValue, ushort.MinValue, UnityVersionType.Alpha, byte.MinValue));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MaximumValueDefinition()
|
||||
{
|
||||
Assert.IsTrue(UnityVersion.MaxVersion == new UnityVersion(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, (UnityVersionType)byte.MaxValue, byte.MaxValue));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnIncreaseOfTypeNumberMakesInequal()
|
||||
{
|
||||
Assert.IsTrue(UnityVersion.MinVersion < new UnityVersion(ushort.MinValue, ushort.MinValue, ushort.MinValue, UnityVersionType.Alpha, 1));
|
||||
Assert.IsTrue(lowValue < new UnityVersion(lowValue.Major, lowValue.Minor, lowValue.Build, lowValue.Type, (byte)(lowValue.TypeNumber + 1)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AlphaZeroIsTheDefault()
|
||||
{
|
||||
Assert.IsTrue(new UnityVersion(2019, 1, 0) < new UnityVersion(2019, 1, 0, UnityVersionType.Final, 1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparisonMethodsIgnoreUnspecifiedValues()
|
||||
{
|
||||
Assert.IsTrue(new UnityVersion(2019, 1, 0, UnityVersionType.Final, 1).IsGreaterEqual(2019, 1, 0));
|
||||
Assert.IsTrue(new UnityVersion(2019, 1, 0, UnityVersionType.Final, 1).IsLessEqual(2019, 1, 0));
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
namespace AssetRipper.VersionUtilities.Tests;
|
||||
|
||||
public class ParsingTests
|
||||
{
|
||||
[Test]
|
||||
public void UnityVersionParsesCorrectly()
|
||||
{
|
||||
string version = "2343.4.5f7";
|
||||
UnityVersion expected = new UnityVersion(2343, 4, 5, UnityVersionType.Final, 7);
|
||||
Assert.AreEqual(expected, UnityVersion.Parse(version));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnityVersionParsesDllCorrectly()
|
||||
{
|
||||
string dllName = "_2343_4_5f7.dll";
|
||||
UnityVersion expected = new UnityVersion(2343, 4, 5, UnityVersionType.Final, 7);
|
||||
Assert.AreEqual(expected, UnityVersion.ParseFromDllName(dllName));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnityVersionParsesDllCorrectlyWithoutExtension()
|
||||
{
|
||||
string dllName = "_2343_4_5f7";
|
||||
UnityVersion expected = new UnityVersion(2343, 4, 5, UnityVersionType.Final, 7);
|
||||
Assert.AreEqual(expected, UnityVersion.ParseFromDllName(dllName));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnityVersionParsesDllCorrectlyWithoutLeadingUnderscore()
|
||||
{
|
||||
string dllName = "2343_4_5f7.dll";
|
||||
UnityVersion expected = new UnityVersion(2343, 4, 5, UnityVersionType.Final, 7);
|
||||
Assert.AreEqual(expected, UnityVersion.ParseFromDllName(dllName));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MajorMinorOnly()
|
||||
{
|
||||
string version = "2019.4";
|
||||
UnityVersion expected = new UnityVersion(2019, 4, 0, UnityVersionType.Final, 1);
|
||||
Assert.AreEqual(expected, UnityVersion.Parse(version));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MajorMinorBuildOnly()
|
||||
{
|
||||
string version = "2019.4.3";
|
||||
UnityVersion expected = new UnityVersion(2019, 4, 3, UnityVersionType.Final, 1);
|
||||
Assert.AreEqual(expected, UnityVersion.Parse(version));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChinaVersionString()
|
||||
{
|
||||
string version = "2021.3.11f1c2";//This is an actual version from a game.
|
||||
//Versions like 2019.3.0f2 and 2020.3.15f2 don't have corresponding Chinese versions.
|
||||
//Source: https://unity.cn/releases
|
||||
UnityVersion expected = new UnityVersion(2021, 3, 11, UnityVersionType.China, 2);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(expected, UnityVersion.Parse(version));
|
||||
Assert.AreEqual(version, expected.ToString());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
global using NUnit.Framework;
|
||||
@@ -1,23 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netframework3.5;netframework4.0;netframework4.8;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>AssetRipper.VersionUtilities.Tests</RootNamespace>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\VersionUtilities\VersionUtilities.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,60 +0,0 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
|
||||
public partial struct CompactUnityVersion24 : IEquatable<CompactUnityVersion24>, IComparable, IComparable<CompactUnityVersion24>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compare to another object
|
||||
/// </summary>
|
||||
/// <param name="obj">Another object</param>
|
||||
/// <returns>
|
||||
/// 1 if this is larger or the other is not a Unity version<br/>
|
||||
/// -1 if this is smaller<br/>
|
||||
/// 0 if equal
|
||||
/// </returns>
|
||||
public int CompareTo(object? obj)
|
||||
{
|
||||
return obj is CompactUnityVersion24 version ? CompareTo(version) : 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two Unity versions
|
||||
/// </summary>
|
||||
/// <param name="other">Another Unity version</param>
|
||||
/// <returns>
|
||||
/// 1 if this is larger<br/>
|
||||
/// -1 if this is smaller<br/>
|
||||
/// 0 if equal
|
||||
/// </returns>
|
||||
public int CompareTo(CompactUnityVersion24 other)
|
||||
{
|
||||
if (this > other)
|
||||
return 1;
|
||||
else if (this < other)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check equality with another object
|
||||
/// </summary>
|
||||
/// <param name="obj">Another object</param>
|
||||
/// <returns>True if they're equal; false otherwise</returns>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is CompactUnityVersion24 version && this == version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check equality with another Unity version
|
||||
/// </summary>
|
||||
/// <param name="other">Another Unity version</param>
|
||||
/// <returns>True if they're equal; false otherwise</returns>
|
||||
public bool Equals(CompactUnityVersion24 other) => this == other;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_MajorMinorByte << 16 | m_BuildTypeShort;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
|
||||
public partial struct CompactUnityVersion24
|
||||
{
|
||||
/// <summary>
|
||||
/// Expand to 64 bits
|
||||
/// </summary>
|
||||
/// <param name="version">The version to convert</param>
|
||||
public static implicit operator UnityVersion(CompactUnityVersion24 version)
|
||||
{
|
||||
return new UnityVersion(version.Major, version.Minor, version.Build, version.Type, version.TypeNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expand to 32 bits
|
||||
/// </summary>
|
||||
/// <param name="version">The version to convert</param>
|
||||
public static implicit operator CompactUnityVersion32(CompactUnityVersion24 version)
|
||||
{
|
||||
return new CompactUnityVersion32(version.Major, version.Minor, version.Build, version.Type, version.TypeNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compact to 24 bits
|
||||
/// </summary>
|
||||
/// <param name="version"></param>
|
||||
public static explicit operator CompactUnityVersion24(UnityVersion version)
|
||||
{
|
||||
return new CompactUnityVersion24(version.Major, (byte)version.Minor, (byte)version.Build, version.Type, version.TypeNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compact to 24 bits
|
||||
/// </summary>
|
||||
/// <param name="version"></param>
|
||||
public static explicit operator CompactUnityVersion24(CompactUnityVersion32 version)
|
||||
{
|
||||
return new CompactUnityVersion24(version.Major, version.Minor, version.Build, version.Type, version.TypeNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equality operator
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator ==(CompactUnityVersion24 left, CompactUnityVersion24 right)
|
||||
{
|
||||
return left.m_MajorMinorByte == right.m_MajorMinorByte && left.m_BuildTypeShort == right.m_BuildTypeShort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inequality operator
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator !=(CompactUnityVersion24 left, CompactUnityVersion24 right)
|
||||
{
|
||||
return left.m_MajorMinorByte != right.m_MajorMinorByte || left.m_BuildTypeShort != right.m_BuildTypeShort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Greater than
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator >(CompactUnityVersion24 left, CompactUnityVersion24 right)
|
||||
{
|
||||
return (left.m_MajorMinorByte > right.m_MajorMinorByte) ||
|
||||
(left.m_MajorMinorByte == right.m_MajorMinorByte && left.m_BuildTypeShort > right.m_BuildTypeShort);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Greater than or equal to
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator >=(CompactUnityVersion24 left, CompactUnityVersion24 right)
|
||||
{
|
||||
return (left.m_MajorMinorByte > right.m_MajorMinorByte) ||
|
||||
(left.m_MajorMinorByte == right.m_MajorMinorByte && left.m_BuildTypeShort >= right.m_BuildTypeShort);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Less than
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator <(CompactUnityVersion24 left, CompactUnityVersion24 right)
|
||||
{
|
||||
return (left.m_MajorMinorByte < right.m_MajorMinorByte) ||
|
||||
(left.m_MajorMinorByte == right.m_MajorMinorByte && left.m_BuildTypeShort < right.m_BuildTypeShort);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Less than or equal to
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator <=(CompactUnityVersion24 left, CompactUnityVersion24 right)
|
||||
{
|
||||
return (left.m_MajorMinorByte < right.m_MajorMinorByte) ||
|
||||
(left.m_MajorMinorByte == right.m_MajorMinorByte && left.m_BuildTypeShort <= right.m_BuildTypeShort);
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
|
||||
/// <summary>
|
||||
/// A compact value type for efficiently storing Unity versions inside 24 bits
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 5 bits for the major,
|
||||
/// 3 bits for the minor
|
||||
/// </summary>
|
||||
private readonly byte m_MajorMinorByte;
|
||||
|
||||
/// <summary>
|
||||
/// 7 bits for the build,
|
||||
/// 3 bits for the type,
|
||||
/// 6 bits for the type number
|
||||
/// </summary>
|
||||
private readonly ushort m_BuildTypeShort;
|
||||
|
||||
/// <summary>
|
||||
/// 2042
|
||||
/// </summary>
|
||||
public const ushort MajorMaxValue = (ushort)(31U + 2011U);
|
||||
|
||||
/// <summary>
|
||||
/// 5 bits
|
||||
/// </summary>
|
||||
private byte MajorRaw => unchecked((byte)((m_MajorMinorByte >> majorOffset) & bitMask5));
|
||||
|
||||
/// <summary>
|
||||
/// The first number in a Unity version string. 5 bits.
|
||||
/// Valid values inclusive: 0 - 5, 2017 - 2042
|
||||
/// </summary>
|
||||
public ushort Major => ConvertMajorRawToNormal(MajorRaw);
|
||||
|
||||
/// <summary>
|
||||
/// The second number in a Unity version string. 3 bits
|
||||
/// </summary>
|
||||
public byte Minor => unchecked((byte)(m_MajorMinorByte & bitMask3));
|
||||
|
||||
/// <summary>
|
||||
/// The third number in a Unity version string. 7 bits
|
||||
/// </summary>
|
||||
public byte Build => unchecked((byte)((m_BuildTypeShort >> buildOffset) & bitMask7));
|
||||
|
||||
/// <summary>
|
||||
/// The letter in a Unity version string. 3 bits
|
||||
/// </summary>
|
||||
public UnityVersionType Type => (UnityVersionType)unchecked((byte)((m_BuildTypeShort >> typeOffset) & bitMask3));
|
||||
|
||||
/// <summary>
|
||||
/// The last number in a Unity version string. 6 bits
|
||||
/// </summary>
|
||||
public byte TypeNumber => unchecked((byte)(m_BuildTypeShort & bitMask6));
|
||||
|
||||
/// <summary>
|
||||
/// The minimum value this type can have
|
||||
/// </summary>
|
||||
public static CompactUnityVersion24 MinVersion { get; } = new CompactUnityVersion24((byte)0, (ushort)0);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum value this type can have
|
||||
/// </summary>
|
||||
public static CompactUnityVersion24 MaxVersion { get; } = new CompactUnityVersion24(byte.MaxValue, ushort.MaxValue);
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Unity version
|
||||
/// </summary>
|
||||
public CompactUnityVersion24(ushort major)
|
||||
{
|
||||
m_MajorMinorByte = unchecked((byte)(ConvertMajorRawToNormal(major) << majorOffset));
|
||||
m_BuildTypeShort = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Unity version
|
||||
/// </summary>
|
||||
public CompactUnityVersion24(ushort major, byte minor)
|
||||
{
|
||||
m_MajorMinorByte = unchecked((byte)((ConvertMajorRawToNormal(major) << majorOffset) | CastToThreeBits(minor)));
|
||||
m_BuildTypeShort = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Unity version
|
||||
/// </summary>
|
||||
public CompactUnityVersion24(ushort major, byte minor, byte build)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
m_MajorMinorByte = (byte)((ConvertMajorRawToNormal(major) << majorOffset) | CastToThreeBits(minor));
|
||||
m_BuildTypeShort = (ushort)(CastToSevenBits(build) << buildOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Unity version
|
||||
/// </summary>
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Unity version
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this to its binary representation
|
||||
/// </summary>
|
||||
public void GetBits(out byte majorMinorByte, out ushort buildTypeShort)
|
||||
{
|
||||
majorMinorByte = m_MajorMinorByte;
|
||||
buildTypeShort = m_BuildTypeShort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a binary representation into its respective version
|
||||
/// </summary>
|
||||
/// <param name="majorMinorByte"></param>
|
||||
/// <param name="buildTypeShort"></param>
|
||||
/// <returns>A new Unity version with the cooresponding bits</returns>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the version as a string
|
||||
/// </summary>
|
||||
/// <returns>A new string like 2019.4.3f1</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return ((UnityVersion)this).ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
|
||||
public partial struct CompactUnityVersion32 : IEquatable<CompactUnityVersion32>, IComparable, IComparable<CompactUnityVersion32>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compare to another object
|
||||
/// </summary>
|
||||
/// <param name="obj">Another object</param>
|
||||
/// <returns>
|
||||
/// 1 if this is larger or the other is not a Unity version<br/>
|
||||
/// -1 if this is smaller<br/>
|
||||
/// 0 if equal
|
||||
/// </returns>
|
||||
public int CompareTo(object? obj)
|
||||
{
|
||||
return obj is CompactUnityVersion32 version ? CompareTo(version) : 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two Unity versions
|
||||
/// </summary>
|
||||
/// <param name="other">Another Unity version</param>
|
||||
/// <returns>
|
||||
/// 1 if this is larger<br/>
|
||||
/// -1 if this is smaller<br/>
|
||||
/// 0 if equal
|
||||
/// </returns>
|
||||
public int CompareTo(CompactUnityVersion32 other)
|
||||
{
|
||||
if (this > other)
|
||||
return 1;
|
||||
else if (this < other)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check equality with another object
|
||||
/// </summary>
|
||||
/// <param name="obj">Another object</param>
|
||||
/// <returns>True if they're equal; false otherwise</returns>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is CompactUnityVersion32 version && this == version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check equality with another Unity version
|
||||
/// </summary>
|
||||
/// <param name="other">Another Unity version</param>
|
||||
/// <returns>True if they're equal; false otherwise</returns>
|
||||
public bool Equals(CompactUnityVersion32 other) => this == other;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_data.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
|
||||
public partial struct CompactUnityVersion32
|
||||
{
|
||||
/// <summary>
|
||||
/// Expand to 64 bits
|
||||
/// </summary>
|
||||
/// <param name="version">The version to convert</param>
|
||||
public static implicit operator UnityVersion(CompactUnityVersion32 version)
|
||||
{
|
||||
return new UnityVersion(version.Major, version.Minor, version.Build, version.Type, version.TypeNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compact to 32 bits
|
||||
/// </summary>
|
||||
/// <param name="version"></param>
|
||||
public static explicit operator CompactUnityVersion32(UnityVersion version)
|
||||
{
|
||||
return new CompactUnityVersion32(version.Major, (byte)version.Minor, (byte)version.Build, version.Type, version.TypeNumber);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equality operator
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator ==(CompactUnityVersion32 left, CompactUnityVersion32 right)
|
||||
{
|
||||
return left.m_data == right.m_data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inequality operator
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator !=(CompactUnityVersion32 left, CompactUnityVersion32 right)
|
||||
{
|
||||
return left.m_data != right.m_data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Greater than
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator >(CompactUnityVersion32 left, CompactUnityVersion32 right)
|
||||
{
|
||||
return left.m_data > right.m_data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Greater than or equal to
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator >=(CompactUnityVersion32 left, CompactUnityVersion32 right)
|
||||
{
|
||||
return left.m_data >= right.m_data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Less than
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator <(CompactUnityVersion32 left, CompactUnityVersion32 right)
|
||||
{
|
||||
return left.m_data < right.m_data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Less than or equal to
|
||||
/// </summary>
|
||||
/// <param name="left">The left Unity version</param>
|
||||
/// <param name="right">The right Unity version</param>
|
||||
/// <returns></returns>
|
||||
public static bool operator <=(CompactUnityVersion32 left, CompactUnityVersion32 right)
|
||||
{
|
||||
return left.m_data <= right.m_data;
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
|
||||
/// <summary>
|
||||
/// A compact value type for efficiently storing Unity versions inside 32 bits
|
||||
/// </summary>
|
||||
public partial struct CompactUnityVersion32
|
||||
{
|
||||
private const int majorOffset = 24;
|
||||
private const int minorOffset = 20;
|
||||
private const int buildOffset = 12;
|
||||
private const int typeOffset = 8;
|
||||
|
||||
private const uint byteMask = 0x00FFU;
|
||||
private const uint bitMask4 = 0x000FU;
|
||||
|
||||
private readonly uint m_data;
|
||||
|
||||
/// <summary>
|
||||
/// 2266
|
||||
/// </summary>
|
||||
public const ushort MajorMaxValue = (ushort)(byte.MaxValue + 2011U);
|
||||
|
||||
/// <summary>
|
||||
/// 8 bits
|
||||
/// </summary>
|
||||
private byte MajorRaw => unchecked((byte)((m_data >> majorOffset) & byteMask));
|
||||
|
||||
/// <summary>
|
||||
/// The first number in a Unity version string. 8 bits.
|
||||
/// Valid values inclusive: 0 - 5, 2017 - 2266
|
||||
/// </summary>
|
||||
public ushort Major => ConvertMajorRawToNormal(MajorRaw);
|
||||
|
||||
/// <summary>
|
||||
/// The second number in a Unity version string. 4 bits
|
||||
/// </summary>
|
||||
public byte Minor => unchecked((byte)((m_data >> minorOffset) & bitMask4));
|
||||
|
||||
/// <summary>
|
||||
/// The third number in a Unity version string. 8 bits
|
||||
/// </summary>
|
||||
public byte Build => unchecked((byte)((m_data >> buildOffset) & byteMask));
|
||||
|
||||
/// <summary>
|
||||
/// The letter in a Unity version string. 4 bits
|
||||
/// </summary>
|
||||
public UnityVersionType Type => (UnityVersionType)unchecked((byte)((m_data >> typeOffset) & bitMask4));
|
||||
|
||||
/// <summary>
|
||||
/// The last number in a Unity version string. 8 bits
|
||||
/// </summary>
|
||||
public byte TypeNumber => unchecked((byte)(m_data & byteMask));
|
||||
|
||||
/// <summary>
|
||||
/// The minimum value this type can have
|
||||
/// </summary>
|
||||
public static CompactUnityVersion32 MinVersion { get; } = new CompactUnityVersion32(0U);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum value this type can have
|
||||
/// </summary>
|
||||
public static CompactUnityVersion32 MaxVersion { get; } = new CompactUnityVersion32(uint.MaxValue);
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Unity version
|
||||
/// </summary>
|
||||
public CompactUnityVersion32(ushort major)
|
||||
{
|
||||
m_data = (uint)ConvertMajorRawToNormal(major) << majorOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Unity version
|
||||
/// </summary>
|
||||
public CompactUnityVersion32(ushort major, byte minor)
|
||||
{
|
||||
m_data = ((uint)ConvertMajorRawToNormal(major) << majorOffset) | ((uint)CastToFourBits(minor) << minorOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Unity version
|
||||
/// </summary>
|
||||
public CompactUnityVersion32(ushort major, byte minor, byte build)
|
||||
{
|
||||
m_data = ((uint)ConvertMajorRawToNormal(major) << majorOffset) | ((uint)CastToFourBits(minor) << minorOffset) | ((uint)build << buildOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Unity version
|
||||
/// </summary>
|
||||
public CompactUnityVersion32(ushort major, byte minor, byte build, UnityVersionType type)
|
||||
{
|
||||
m_data = ((uint)ConvertMajorRawToNormal(major) << majorOffset) | ((uint)CastToFourBits(minor) << minorOffset) | ((uint)build << buildOffset)
|
||||
| ((uint)CastToFourBits(type) << typeOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Unity version
|
||||
/// </summary>
|
||||
public CompactUnityVersion32(ushort major, byte minor, byte build, UnityVersionType type, byte typeNumber)
|
||||
{
|
||||
m_data = ((uint)ConvertMajorRawToNormal(major) << majorOffset) | ((uint)CastToFourBits(minor) << minorOffset) | ((uint)build << buildOffset)
|
||||
| ((uint)CastToFourBits(type) << typeOffset) | typeNumber;
|
||||
}
|
||||
|
||||
private CompactUnityVersion32(uint data)
|
||||
{
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this to its binary representation
|
||||
/// </summary>
|
||||
/// <returns>An unsigned integer having the same bits as this</returns>
|
||||
public uint GetBits() => m_data;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a binary representation into its respective version
|
||||
/// </summary>
|
||||
/// <param name="bits">An unsigned integer having the relevant bits</param>
|
||||
/// <returns>A new Unity version with the cooresponding bits</returns>
|
||||
public static CompactUnityVersion32 FromBits(uint bits) => new CompactUnityVersion32(bits);
|
||||
|
||||
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 CastToFourBits(byte b)
|
||||
{
|
||||
return b <= bitMask4 ? b : throw new ArgumentOutOfRangeException(nameof(b));
|
||||
}
|
||||
|
||||
private static UnityVersionType CastToFourBits(UnityVersionType type)
|
||||
{
|
||||
return (byte)type <= bitMask4 ? type : throw new ArgumentOutOfRangeException(nameof(type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the version as a string
|
||||
/// </summary>
|
||||
/// <returns>A new string like 2019.4.3f1</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return ((UnityVersion)this).ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
namespace AssetRipper.VersionUtilities.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Unity version extension methods for <see cref="BinaryReader"/>
|
||||
/// </summary>
|
||||
public static class BinaryReaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads a Unity version from the stream
|
||||
/// </summary>
|
||||
/// <param name="reader">A binary reader</param>
|
||||
/// <returns>The read Unity version</returns>
|
||||
public static UnityVersion ReadUnityVersion(this BinaryReader reader)
|
||||
{
|
||||
return UnityVersion.FromBits(reader.ReadUInt64());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Unity version from the stream
|
||||
/// </summary>
|
||||
/// <param name="reader">A binary reader</param>
|
||||
/// <returns>The read Unity version</returns>
|
||||
public static CompactUnityVersion32 ReadCompactUnityVersion32(this BinaryReader reader)
|
||||
{
|
||||
return CompactUnityVersion32.FromBits(reader.ReadUInt32());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Unity version from the stream
|
||||
/// </summary>
|
||||
/// <param name="reader">A binary reader</param>
|
||||
/// <returns>The read Unity version</returns>
|
||||
public static CompactUnityVersion24 ReadCompactUnityVersion24(this BinaryReader reader)
|
||||
{
|
||||
byte b = reader.ReadByte();
|
||||
ushort s = reader.ReadUInt16();
|
||||
return CompactUnityVersion24.FromBits(b, s);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
namespace AssetRipper.VersionUtilities.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Unity version extension methods for <see cref="BinaryWriter"/>
|
||||
/// </summary>
|
||||
public static class BinaryWriterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a Unity version to the stream
|
||||
/// </summary>
|
||||
/// <param name="writer">A binary writer</param>
|
||||
/// <param name="version">A Unity version</param>
|
||||
public static void Write(this BinaryWriter writer, UnityVersion version)
|
||||
{
|
||||
writer.Write(version.GetBits());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Unity version to the stream
|
||||
/// </summary>
|
||||
/// <param name="writer">A binary writer</param>
|
||||
/// <param name="version">A Unity version</param>
|
||||
public static void Write(this BinaryWriter writer, CompactUnityVersion32 version)
|
||||
{
|
||||
writer.Write(version.GetBits());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a Unity version to the stream
|
||||
/// </summary>
|
||||
/// <param name="writer">A binary writer</param>
|
||||
/// <param name="version">A Unity version</param>
|
||||
public static void Write(this BinaryWriter writer, CompactUnityVersion24 version)
|
||||
{
|
||||
version.GetBits(out byte b, out ushort s);
|
||||
writer.Write(b);
|
||||
writer.Write(s);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
|
||||
public readonly partial struct UnityVersion
|
||||
{
|
||||
private const ulong subMajorMask = 0x0000FFFFFFFFFFFFUL;
|
||||
private const ulong subMinorMask = 0x00000000FFFFFFFFUL;
|
||||
private const ulong subBuildMask = 0x000000000000FFFFUL;
|
||||
private const ulong subTypeMask = 0x00000000000000FFUL;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
public bool IsEqual(ushort major) => this == From(major);
|
||||
public bool IsEqual(ushort major, ushort minor) => this == From(major, minor);
|
||||
public bool IsEqual(ushort major, ushort minor, ushort build) => this == From(major, minor, build);
|
||||
public bool IsEqual(ushort major, ushort minor, ushort build, UnityVersionType type) => this == From(major, minor, build, type);
|
||||
public bool IsEqual(ushort major, ushort minor, ushort build, UnityVersionType type, byte typeNumber) => this == new UnityVersion(major, minor, build, type, typeNumber);
|
||||
public bool IsEqual(string version) => this == Parse(version);
|
||||
|
||||
public bool IsLess(ushort major) => this < From(major);
|
||||
public bool IsLess(ushort major, ushort minor) => this < From(major, minor);
|
||||
public bool IsLess(ushort major, ushort minor, ushort build) => this < From(major, minor, build);
|
||||
public bool IsLess(ushort major, ushort minor, ushort build, UnityVersionType type) => this < From(major, minor, build, type);
|
||||
public bool IsLess(ushort major, ushort minor, ushort build, UnityVersionType type, byte typeNumber) => this < new UnityVersion(major, minor, build, type, typeNumber);
|
||||
public bool IsLess(string version) => this < Parse(version);
|
||||
|
||||
public bool IsLessEqual(ushort major) => this <= From(major);
|
||||
public bool IsLessEqual(ushort major, ushort minor) => this <= From(major, minor);
|
||||
public bool IsLessEqual(ushort major, ushort minor, ushort build) => this <= From(major, minor, build);
|
||||
public bool IsLessEqual(ushort major, ushort minor, ushort build, UnityVersionType type) => this <= From(major, minor, build, type);
|
||||
public bool IsLessEqual(ushort major, ushort minor, ushort build, UnityVersionType type, byte typeNumber) => this <= new UnityVersion(major, minor, build, type, typeNumber);
|
||||
public bool IsLessEqual(string version) => this <= Parse(version);
|
||||
|
||||
public bool IsGreater(ushort major) => this > From(major);
|
||||
public bool IsGreater(ushort major, ushort minor) => this > From(major, minor);
|
||||
public bool IsGreater(ushort major, ushort minor, ushort build) => this > From(major, minor, build);
|
||||
public bool IsGreater(ushort major, ushort minor, ushort build, UnityVersionType type) => this > From(major, minor, build, type);
|
||||
public bool IsGreater(ushort major, ushort minor, ushort build, UnityVersionType type, byte typeNumber) => this > new UnityVersion(major, minor, build, type, typeNumber);
|
||||
public bool IsGreater(string version) => this > Parse(version);
|
||||
|
||||
public bool IsGreaterEqual(ushort major) => this >= From(major);
|
||||
public bool IsGreaterEqual(ushort major, ushort minor) => this >= From(major, minor);
|
||||
public bool IsGreaterEqual(ushort major, ushort minor, ushort build) => this >= From(major, minor, build);
|
||||
public bool IsGreaterEqual(ushort major, ushort minor, ushort build, UnityVersionType type) => this >= From(major, minor, build, type);
|
||||
public bool IsGreaterEqual(ushort major, ushort minor, ushort build, UnityVersionType type, byte typeNumber) => this >= new UnityVersion(major, minor, build, type, typeNumber);
|
||||
public bool IsGreaterEqual(string version) => this >= Parse(version);
|
||||
|
||||
private UnityVersion From(ushort major)
|
||||
{
|
||||
ulong data = ((ulong)major << majorOffset) | subMajorMask & m_data;
|
||||
return new UnityVersion(data);
|
||||
}
|
||||
private UnityVersion From(ushort major, ushort minor)
|
||||
{
|
||||
ulong data = ((ulong)major << majorOffset) | ((ulong)minor << minorOffset) | subMinorMask & m_data;
|
||||
return new UnityVersion(data);
|
||||
}
|
||||
private UnityVersion From(ushort major, ushort minor, ushort build)
|
||||
{
|
||||
ulong data = ((ulong)major << majorOffset) | ((ulong)minor << minorOffset) | ((ulong)build << buildOffset) |
|
||||
subBuildMask & m_data;
|
||||
return new UnityVersion(data);
|
||||
}
|
||||
private UnityVersion From(ushort major, ushort minor, ushort build, UnityVersionType type)
|
||||
{
|
||||
ulong data = ((ulong)major << majorOffset) | ((ulong)minor << minorOffset) | ((ulong)build << buildOffset) |
|
||||
((ulong)(ushort)type << typeOffset) | subTypeMask & m_data;
|
||||
return new UnityVersion(data);
|
||||
}
|
||||
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#if NET7_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
partial struct UnityVersion : IComparisonOperators<UnityVersion, UnityVersion, bool>, IEqualityOperators<UnityVersion, UnityVersion, bool>, IMinMaxValue<UnityVersion>
|
||||
{
|
||||
static UnityVersion IMinMaxValue<UnityVersion>.MaxValue => MaxVersion;
|
||||
|
||||
static UnityVersion IMinMaxValue<UnityVersion>.MinValue => MinVersion;
|
||||
}
|
||||
#endif
|
||||
@@ -1,145 +0,0 @@
|
||||
using AssetRipper.VersionUtilities.Extensions;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AssetRipper.VersionUtilities;
|
||||
|
||||
public readonly partial struct UnityVersion
|
||||
{
|
||||
private static readonly Regex majorMinorRegex = new Regex(@"^([0-9]+)\.([0-9]+)$", RegexOptions.Compiled);
|
||||
private static readonly Regex majorMinorBuildRegex = new Regex(@"^([0-9]+)\.([0-9]+)\.([0-9]+)$", RegexOptions.Compiled);
|
||||
private static readonly Regex normalRegex = new Regex(@"^([0-9]+)\.([0-9]+)\.([0-9]+)\.?([abfpx])([0-9]+)$", RegexOptions.Compiled);
|
||||
private static readonly Regex chinaRegex = new Regex(@"^([0-9]+)\.([0-9]+)\.([0-9]+)\.?f1c([0-9]+)$", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the version as a string
|
||||
/// </summary>
|
||||
/// <returns>A new string like 2019.4.3f1</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return ToString(false, false, false, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the version as a string
|
||||
/// </summary>
|
||||
/// <param name="hasUnderscorePrefix">Include the _ prefix</param>
|
||||
/// <param name="useUnderscoresAsSeparators">Use underscores as separators instead of periods</param>
|
||||
/// <param name="hasDllExtension">Include the .dll extension</param>
|
||||
/// <param name="hasSeparatorAfterBuild">Include a separator between <see cref="Build"/> and <see cref="Type"/></param>
|
||||
/// <returns>A new string generated with those parameters</returns>
|
||||
public string ToString(bool hasUnderscorePrefix, bool useUnderscoresAsSeparators, bool hasDllExtension, bool hasSeparatorAfterBuild)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
|
||||
char separator = useUnderscoresAsSeparators ? '_' : '.';
|
||||
|
||||
if(hasUnderscorePrefix)
|
||||
sb.Append('_');
|
||||
|
||||
sb.Append(Major);
|
||||
sb.Append(separator);
|
||||
sb.Append(Minor);
|
||||
sb.Append(separator);
|
||||
sb.Append(Build);
|
||||
if (hasSeparatorAfterBuild)
|
||||
{
|
||||
sb.Append(separator);
|
||||
}
|
||||
if (Type == UnityVersionType.China)
|
||||
{
|
||||
sb.Append("f1");
|
||||
sb.Append('c');
|
||||
sb.Append(TypeNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(Type.ToCharacter());
|
||||
sb.Append(TypeNumber);
|
||||
}
|
||||
|
||||
if(hasDllExtension)
|
||||
sb.Append(".dll");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the version as a string
|
||||
/// </summary>
|
||||
/// <returns>A new string like 2019.4.3</returns>
|
||||
public string ToStringWithoutType()
|
||||
{
|
||||
return $"{Major}.{Minor}.{Build}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a dll name like _2019_4_3f1.dll
|
||||
/// </summary>
|
||||
/// <param name="dllName">The name of a dll file</param>
|
||||
/// <returns>The parsed Unity version</returns>
|
||||
/// <exception cref="ArgumentNullException">If the string is null or empty</exception>
|
||||
public static UnityVersion ParseFromDllName(string dllName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(dllName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(dllName));
|
||||
}
|
||||
if (dllName[0] == '_')
|
||||
{
|
||||
dllName = dllName.Substring(1);
|
||||
}
|
||||
|
||||
return Parse(dllName.Replace('_', '.').Replace(".dll", ""));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a normal Unity version string
|
||||
/// </summary>
|
||||
/// <param name="version">A string to parse</param>
|
||||
/// <returns>The parsed Unity version</returns>
|
||||
/// <exception cref="ArgumentNullException">If the string is null or empty</exception>
|
||||
/// <exception cref="ArgumentException">If the string is in an invalid format</exception>
|
||||
public static UnityVersion Parse(string version)
|
||||
{
|
||||
if (string.IsNullOrEmpty(version))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(version));
|
||||
}
|
||||
|
||||
if (normalRegex.TryMatch(version, out Match? match))
|
||||
{
|
||||
int major = int.Parse(match.Groups[1].Value);
|
||||
int minor = int.Parse(match.Groups[2].Value);
|
||||
int build = int.Parse(match.Groups[3].Value);
|
||||
char type = match.Groups[4].Value[0];
|
||||
int typeNumber = int.Parse(match.Groups[5].Value);
|
||||
return new UnityVersion((ushort)major, (ushort)minor, (ushort)build, type.ToUnityVersionType(), (byte)typeNumber);
|
||||
}
|
||||
else if (majorMinorBuildRegex.TryMatch(version, out match))
|
||||
{
|
||||
int major = int.Parse(match.Groups[1].Value);
|
||||
int minor = int.Parse(match.Groups[2].Value);
|
||||
int build = int.Parse(match.Groups[3].Value);
|
||||
return new UnityVersion((ushort)major, (ushort)minor, (ushort)build, UnityVersionType.Final, 1);
|
||||
}
|
||||
else if (majorMinorRegex.TryMatch(version, out match))
|
||||
{
|
||||
int major = int.Parse(match.Groups[1].Value);
|
||||
int minor = int.Parse(match.Groups[2].Value);
|
||||
return new UnityVersion((ushort)major, (ushort)minor, 0, UnityVersionType.Final, 1);
|
||||
}
|
||||
else if (chinaRegex.TryMatch(version, out match))
|
||||
{
|
||||
int major = int.Parse(match.Groups[1].Value);
|
||||
int minor = int.Parse(match.Groups[2].Value);
|
||||
int build = int.Parse(match.Groups[3].Value);
|
||||
int typeNumber = int.Parse(match.Groups[4].Value);
|
||||
return new UnityVersion((ushort)major, (ushort)minor, (ushort)build, UnityVersionType.China, (byte)typeNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Invalid version format: {version}", nameof(version));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netframework3.5;netframework4.0;netstandard2.0;net5.0;net6.0;net7.0</TargetFrameworks>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>AssetRipper.VersionUtilities</RootNamespace>
|
||||
<AssemblyName>AssetRipper.VersionUtilities</AssemblyName>
|
||||
<Authors>ds5678</Authors>
|
||||
<Company>AssetRipper</Company>
|
||||
<Version>1.3.1.0</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<PackageId>AssetRipper.VersionUtilities</PackageId>
|
||||
<PackageTags>C# version unity unity3d</PackageTags>
|
||||
<RepositoryUrl>https://github.com/AssetRipper/VersionUtilities</RepositoryUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageProjectUrl>https://github.com/AssetRipper/VersionUtilities</PackageProjectUrl>
|
||||
<Copyright>Copyright (c) 2022-$([System.DateTime]::Now.Year) ds5678</Copyright>
|
||||
<Description>Managed library for handling Unity versions</Description>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<DocumentationFile>bin\VersionUtilities.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' != 'netframework3.5' and '$(TargetFramework)' != 'netframework4.0' ">
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="PolySharp" Version="1.10.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in new issue
Block a user