21 Commits
Author SHA1 Message Date
ds5678 ffb82e8164 Assembly improvements 2025-06-19 22:55:05 -07:00
ds5678 c39c2e5d34 More accurate assembly name acquisition 2025-06-18 18:45:10 -07:00
ds5678 3366a0125a Assembly data mining 2025-04-21 15:05:23 -07:00
ds5678 fcd32bbdba Bump version to 1.5.0 2025-04-20 15:17:57 -07:00
ds5678 98cf7b1eed Use string instead of UnityGuid on NET Standard
* Port of https://github.com/AssetRipper/AssetRipper.Mining/commit/a43a560f9ab164e155509a6580229263292f8a6d
2025-04-20 15:11:45 -07:00
ds5678 58d5eaf0aa EngineResourceData deserialization improvements
* Port of https://github.com/AssetRipper/AssetRipper.Mining/commit/54eb41b856546423f2b7a269bf08d9bec55afc61
2025-04-20 15:07:43 -07:00
ds5678 888b673dce Only mark package as AOT compatible on .NET 7 and above 2025-04-20 14:48:18 -07:00
ds5678 9627dc2d8d Add package readme 2025-04-20 14:46:50 -07:00
ds5678 54f41eda93 Fix null warning in MonoScript 2025-04-20 14:46:02 -07:00
ds5678 8df013ebed Update projects to .NET 9 and bump NuGet dependencies 2025-04-20 14:45:31 -07:00
ds5678 9047c383bb Update for parity with main 2024-06-09 20:16:28 -07:00
ds5678 bd12d9eae3 Resolve #1 2024-05-27 11:50:52 -07:00
ds5678 87328aaac2 Bump references 2024-05-27 11:12:44 -07:00
ds5678 9ca9112942 AssetDictionary.TryAdd and bump version 2024-01-18 10:36:26 -05:00
ds5678 8e0f803787 Bump version 2024-01-18 09:07:06 -05:00
ds5678 f01470f867 Use a dictionary for package assets, including scripts 2024-01-18 09:05:08 -05:00
ds5678 ab8b136b19 MonoScript.FromType 2024-01-18 08:57:28 -05:00
ds5678 e9b0003601 Fix UInt128 bugs 2024-01-18 08:24:58 -05:00
ds5678 9e2097dd81 API for adding assets to package data 2024-01-18 08:23:19 -05:00
ds5678 63316d5977 Rename Meta to Type 2024-01-16 03:25:48 -05:00
ds5678 0bfce878cb bump version 2024-01-16 02:43:25 -05:00
29 changed files with 669 additions and 99 deletions

No files matched your search

@@ -0,0 +1,270 @@
using AngleSharp;
using AngleSharp.Dom;
using AssetRipper.Primitives;
using System.Diagnostics;
using System.Formats.Tar;
using System.IO.Compression;
namespace AssetRipper.Mining.AssemblyNameCollector;
public readonly record struct AssemblyData(IReadOnlyList<string> Mono2, IReadOnlyList<string> Mono4, IReadOnlyList<string> Unity, IReadOnlyList<KeyValuePair<string, UnityGuid>> UnityExtensions)
{
public static AssemblyData FromFolder(string root)
{
IReadOnlyList<string> mono2 = GetAssemblies_Mono2(root);
IReadOnlyList<string> mono4 = GetAssemblies_Mono4(root);
IReadOnlyList<string> unity = GetAssemblies_Unity(root);
IReadOnlyList<KeyValuePair<string, UnityGuid>> unityExtensions = GetUnityExtensions(root).Concat(GetUnityPackages(root)).OrderBy(pair => pair.Key).ToList();
return new AssemblyData(mono2, mono4, unity, unityExtensions);
}
public bool Equals(AssemblyData other)
{
return Mono2.SequenceEqual(other.Mono2) && Mono4.SequenceEqual(other.Mono4) && Unity.SequenceEqual(other.Unity) && UnityExtensions.SequenceEqual(other.UnityExtensions);
}
public override int GetHashCode()
{
HashCode hash = new();
foreach (string assembly in Mono2)
{
hash.Add(assembly);
}
foreach (string assembly in Mono4)
{
hash.Add(assembly);
}
foreach (string assembly in Unity)
{
hash.Add(assembly);
}
foreach (KeyValuePair<string, UnityGuid> pair in UnityExtensions)
{
hash.Add(pair);
}
return hash.ToHashCode();
}
static IReadOnlyList<string> GetAssemblies_Mono2(string root)
{
return GetAssemblies(root,
"Editor/Data/Mono/lib/mono/unity",
"Editor/Data/Mono/lib/mono/2.0",
"Editor/Data/Frameworks/Mono.framework");
}
static IReadOnlyList<string> GetAssemblies_Mono4(string root)
{
return GetAssemblies(root,
"Editor/Data/MonoBleedingEdge/lib/mono/unityjit-win32",
"Editor/Data/MonoBleedingEdge/lib/mono/unityjit",
"Editor/Data/MonoBleedingEdge/lib/mono/unity",
"Editor/Data/MonoBleedingEdge/lib/mono/4.5",
"Editor/Data/MonoBleedingEdge/lib/mono/4.0");
}
static IReadOnlyList<string> GetAssemblies_Unity(string root)
{
if (Directory.Exists(Path.Combine(root, "Editor/Data/Frameworks")))
{
// Unity 2
return ["UnityEngine"];
}
return GetAssemblies(root,
"Editor/Data/PlaybackEngines/windowsstandalonesupport/Variations/win64_nondevelopment/Data/Managed",
"Editor/Data/PlaybackEngines/windowsstandalonesupport/Variations/win64_nondevelopment_mono/Data/Managed",
"Editor/Data/PlaybackEngines/windowsstandalonesupport/Variations/win64_player_nondevelopment_mono/Data/Managed",
"Editor/Data/PlaybackEngines/windows64standaloneplayer/Managed",
"Editor/Data/PlaybackEngines/windowsstandaloneplayer/Managed");
}
static IReadOnlyList<string> GetAssemblies(string root, params ReadOnlySpan<string> relativePaths)
{
foreach (string relativePath in relativePaths)
{
string path = Path.Combine(root, relativePath);
if (Directory.Exists(path))
{
List<string> result = GetManagedAssemblies(path).ToList();
string facadeDirectory = Path.Combine(path, "Facades");
if (Directory.Exists(facadeDirectory))
{
result.AddRange(GetManagedAssemblies(facadeDirectory).Where(a => !result.Contains(a)));
}
return result;
}
}
return [];
}
static IEnumerable<string> GetManagedAssemblies(string directory)
{
return Directory.EnumerateFiles(directory, "*.dll", SearchOption.AllDirectories)
.Select(Path.GetFileNameWithoutExtension)
.Where(s => !string.IsNullOrEmpty(s))
.Distinct()
.Order()!;
}
static IReadOnlyList<KeyValuePair<string, UnityGuid>> GetUnityExtensions(string root)
{
string directory = Path.Combine(root, "Editor/Data/UnityExtensions");
if (Directory.Exists(directory))
{
List<(string, UnityGuid)> extractedArtifacts = new();
foreach (string file in Directory.EnumerateFiles(directory, "ivy.xml", SearchOption.AllDirectories))
{
string xml = File.ReadAllText(file);
IDocument document = BrowsingContext.New(Configuration.Default.WithXml()).OpenAsync(req => req.Content(xml)).GetAwaiter().GetResult();
foreach (IElement artifact in document.QuerySelectorAll("artifact"))
{
string? name = artifact.GetAttribute("name");
string? type = artifact.GetAttribute("type");
string? ext = artifact.GetAttribute("ext");
string? guidString = artifact.GetAttribute("e:guid"); // namespaced attribute
if (string.IsNullOrEmpty(name) || type is not "dll" || ext is not "dll" || string.IsNullOrEmpty(guidString) || !TryParseGuid(guidString, out UnityGuid guid))
{
continue;
}
extractedArtifacts.Add((name, guid));
}
}
Dictionary<string, List<(string, UnityGuid)>> dictionary = new();
foreach ((string path, UnityGuid guid) in extractedArtifacts)
{
string name = Path.GetFileName(path);
if (!dictionary.TryGetValue(name, out List<(string, UnityGuid)>? newList))
{
newList = new();
dictionary.Add(name, newList);
}
newList.Add((path, guid));
}
List<KeyValuePair<string, UnityGuid>> finalList = new();
foreach ((string name, List<(string, UnityGuid)> subList) in dictionary.OrderBy(p => p.Key))
{
Debug.Assert(subList.Count > 0);
UnityGuid guid;
if (subList.Count == 1)
{
guid = subList[0].Item2;
}
else
{
guid = subList.FirstOrDefault(pair => pair.Item1 == name).Item2;
if (guid != default)
{
goto Found;
}
ReadOnlySpan<string> starts =
[
"Standalone/", // Preferred over Editor/
"Runtime/", // Preferred over RuntimeEditor/
// NUnit
"portable/",
"nunit-standalone/",
];
foreach (string start in starts)
{
foreach ((string path, UnityGuid subGuid) in subList)
{
if (path.StartsWith(start, StringComparison.Ordinal))
{
guid = subGuid;
goto Found;
}
}
}
throw new InvalidOperationException($"Failed to find a guid for {name}");
}
Found:
finalList.Add(new(name, guid));
}
return finalList;
}
return [];
}
static IReadOnlyList<KeyValuePair<string, UnityGuid>> GetUnityPackages(string root)
{
// Unity 2017.2 introduced packages, which are stored in a compressed format.
if (root.EndsWith("2017.4.34f1", StringComparison.Ordinal))
{
// Unity 2017.4.34f1 uses a weird compression format for packages.
// It's the only version that does this, so we redirect to the subsequent version.
return GetUnityPackages(Path.Combine(Path.GetDirectoryName(root) ?? string.Empty, "2017.4.35f1"));
}
else if (!Path.GetFileName(root).StartsWith("2017.", StringComparison.Ordinal))
{
// Unity 2018.1 introduced the Package Manager UI, which makes package references no longer automatic.
return [];
}
string directory = Path.Combine(root, "Editor/Data/Resources/PackageManager/Editor");
if (!Directory.Exists(directory))
{
return [];
}
List<KeyValuePair<string, UnityGuid>> assemblyGuids = [];
foreach (string file in Directory.EnumerateFiles(directory, "*.tgz"))
{
using FileStream fileStream = File.OpenRead(file);
using GZipStream gzipStream = new(fileStream, CompressionMode.Decompress);
MemoryStream tarStream = new();
gzipStream.CopyTo(tarStream);
tarStream.Position = 0;
using TarReader reader = new(tarStream);
TarEntry? entry;
while ((entry = reader.GetNextEntry()) is not null)
{
string? entryPath = entry.Name;
if (string.IsNullOrEmpty(entryPath) || !entryPath.EndsWith(".dll.meta", StringComparison.Ordinal))
{
continue;
}
string assemblyName = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(entryPath)); // Remove .meta and .dll
string text = new StreamReader(entry.DataStream!).ReadToEnd();
string? guidString = text.Split('\n')
.FirstOrDefault(line => line.StartsWith("guid: ", StringComparison.Ordinal))?
.Substring("guid: ".Length)
.Trim();
if (!string.IsNullOrEmpty(guidString) && TryParseGuid(guidString, out UnityGuid guid))
{
assemblyGuids.Add(new KeyValuePair<string, UnityGuid>(assemblyName, guid));
}
}
}
return assemblyGuids;
}
private static bool TryParseGuid(string guidString, out UnityGuid guid)
{
// Todo: implement a proper solution upstream
try
{
guid = UnityGuid.Parse(guidString);
return true;
}
catch
{
guid = default;
return false;
}
}
}
@@ -0,0 +1,13 @@
using AssetRipper.Primitives;
using System.Text.Json;
namespace AssetRipper.Mining.AssemblyNameCollector;
public readonly record struct AssemblyDataFile(IReadOnlyList<UnityVersion> Versions, IReadOnlyList<KeyValuePair<UnityVersion, AssemblyData>> Assemblies)
{
public void Write(string path)
{
using FileStream stream = File.Create(path);
JsonSerializer.Serialize(stream, this, AssemblyDataSerializerContext.Default.AssemblyDataFile);
}
}
@@ -0,0 +1,10 @@
using AssetRipper.Primitives;
using System.Text.Json.Serialization;
namespace AssetRipper.Mining.AssemblyNameCollector;
[JsonSourceGenerationOptions(WriteIndented = true, IndentCharacter = '\t', IndentSize = 1, NewLine = "\n")]
[JsonSerializable(typeof(AssemblyDataFile))]
internal sealed partial class AssemblyDataSerializerContext : JsonSerializerContext
{
}
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AngleSharp" Version="1.3.0" />
<PackageReference Include="AngleSharp.Xml" Version="1.0.0" />
<PackageReference Include="AssetRipper.Primitives" Version="3.1.6" />
</ItemGroup>
</Project>
@@ -0,0 +1,35 @@
using AssetRipper.Primitives;
namespace AssetRipper.Mining.AssemblyNameCollector;
internal static class Program
{
static void Main(string[] args)
{
// Extract data
List<UnityVersion> versions = new();
List<KeyValuePair<UnityVersion, AssemblyData>> list = new();
foreach ((string directory, UnityVersion version) in GetRootDirectoriesInOrder(args[0]))
{
Console.WriteLine(directory);
versions.Add(version);
AssemblyData data = AssemblyData.FromFolder(directory);
if (list.Count is 0 || !list[^1].Value.Equals(data))
{
list.Add(new(version, data));
}
}
// Write data
new AssemblyDataFile(versions, list).Write(args[1]);
Console.WriteLine("Done!");
}
static IEnumerable<(string, UnityVersion)> GetRootDirectoriesInOrder(string parentDirectory)
{
return Directory.EnumerateDirectories(parentDirectory)
.Select(d => (d, UnityVersion.Parse(Path.GetFileName(d))))
.OrderBy(p => p.Item2);
}
}
@@ -1,18 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AssetsTools.NET" Version="3.0.0-preview3" />
<PackageReference Include="AssetsTools.NET" Version="3.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetRipper.Mining.PredefinedAssets\AssetRipper.Mining.PredefinedAssets.csproj" />
<ItemGroup>
<ProjectReference Include="..\AssetRipper.Mining.PredefinedAssets\AssetRipper.Mining.PredefinedAssets.csproj" />
</ItemGroup>
</Project>
@@ -1,4 +1,6 @@
using AssetsTools.NET;
using AssetRipper.Mining.PredefinedAssets;
using AssetsTools.NET;
using System.Numerics;
namespace AssetRipper.Mining.EngineFileExtractor;
@@ -9,4 +11,14 @@ internal static class AssetTypeValueFieldExtensions
AssetTypeValueField result = @this.Get(name);
return result.IsDummy ? null : result;
}
}
public static Vector3 AsVector3(this AssetTypeValueField @this)
{
return new Vector3(@this["x"].AsFloat, @this["y"].AsFloat, @this["z"].AsFloat);
}
public static AxisAlignedBoundingBox AsAxisAlignedBoundingBox(this AssetTypeValueField @this)
{
return new AxisAlignedBoundingBox(@this["m_Center"].AsVector3(), @this["m_Extent"].AsVector3());
}
}
@@ -85,7 +85,8 @@ internal static class Program
{
Name = asset.Name,
VertexCount = asset.BaseField.Get("m_VertexData").Get("m_VertexCount").AsInt,
SubMeshCount = asset.BaseField.Get("m_SubMeshes").Get("Array").Children.Count
SubMeshCount = asset.BaseField.Get("m_SubMeshes").Get("Array").Children.Count,
LocalAABB = asset.BaseField.Get("m_LocalAABB").AsAxisAlignedBoundingBox(),
};
}
break;
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5.0;net7.0;net8.0</TargetFrameworks>
<TargetFrameworks>net4.8;net9.0</TargetFrameworks>
<LangVersion>Latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
@@ -11,14 +11,17 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.7.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
@@ -1,3 +1,6 @@
using System.Numerics;
using System.Text.Json;
namespace AssetRipper.Mining.PredefinedAssets.Tests;
public class Tests
@@ -17,4 +20,66 @@ public class Tests
}
""".Replace(Environment.NewLine, "\n")));
}
}
[Test]
public void UnityPackageDataCanBeSerialized()
{
UnityPackageData package = new UnityPackageData("Example", "1.0.0", true);
package.Assets.Add(new TextAsset("", ""u8), default);
string json = package.ToJson();
Assert.That(json, Is.Not.Empty);
}
[Test]
public void UnityPackageDataCanBeDeserialized()
{
UnityPackageData package = UnityPackageData.FromJson(MakeJson());
Assert.That(package.Assets, Is.Not.Empty);
static string MakeJson()
{
UnityPackageData package = new UnityPackageData("Example", "1.0.0", true);
package.Assets.Add(new TextAsset("", ""u8), default);
return package.ToJson();
}
}
[Test]
public void Vector3Serialization()
{
string result = """
{
"X": 1,
"Y": 2,
"Z": 3
}
""".Replace("\r", null);
Vector3 vector = new Vector3(1.0f, 2.0f, 3.0f);
Assert.That(JsonSerializer.Serialize(vector, MiningSerializerContext.Default.Vector3).Replace("\r", null), Is.EqualTo(result));
}
[Test]
public void Vector3Deserialization()
{
string json = """{ "X": 1.0, "Y": 2.0, "Z": 3.0 }""";
Vector3 result = new Vector3(1.0f, 2.0f, 3.0f);
Assert.That(JsonSerializer.Deserialize(json, MiningSerializerContext.Default.Vector3), Is.EqualTo(result));
}
[TestCase("")]
[TestCase("{}")]
public void EmptyEngineResourceDataDeserialization(string json)
{
EngineResourceData result = EngineResourceData.FromJson(json);
Assert.Multiple(() =>
{
Assert.That(result.DefaultResources, Is.Not.Null);
Assert.That(result.ExtraResources, Is.Not.Null);
});
Assert.Multiple(() =>
{
Assert.That(result.DefaultResources, Is.Empty);
Assert.That(result.ExtraResources, Is.Empty);
});
}
}
@@ -0,0 +1,81 @@
using System.Collections;
namespace AssetRipper.Mining.PredefinedAssets;
public readonly struct AssetDictionary : IReadOnlyCollection<KeyValuePair<Object, PPtr>>, ICollection<KeyValuePair<Object, PPtr>>
{
//This class implements all the IReadOnlyDictionary`2 members, but does not implement the interface because that changes how System.Text.Json serializes it.
private readonly Dictionary<Object, PPtr> _dictionary = new();
public int Count => _dictionary.Count;
public IEnumerable<Object> Keys => _dictionary.Keys;
public IEnumerable<PPtr> Values => _dictionary.Values;
public PPtr this[Object key] => _dictionary[key];
public AssetDictionary()
{
}
public void Add(Object key, PPtr value) => _dictionary.Add(key, value);
public void Add(MonoScript key, UnityGuid guid) => Add(key, new PPtr(11500000, guid, AssetType.Meta));
public void Clear() => _dictionary.Clear();
public bool ContainsKey(Object key)
{
return _dictionary.ContainsKey(key);
}
public bool TryAdd(Object key, PPtr value)
{
#if NETSTANDARD
if (_dictionary.ContainsKey(key))
{
return false;
}
else
{
_dictionary.Add(key, value);
return true;
}
#else
return _dictionary.TryAdd(key, value);
#endif
}
public bool TryGetValue(Object key, out PPtr value)
{
return _dictionary.TryGetValue(key, out value);
}
public IEnumerator<KeyValuePair<Object, PPtr>> GetEnumerator() => _dictionary.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
bool ICollection<KeyValuePair<Object, PPtr>>.IsReadOnly => ((ICollection<KeyValuePair<Object, PPtr>>)_dictionary).IsReadOnly;
void ICollection<KeyValuePair<Object, PPtr>>.Add(KeyValuePair<Object, PPtr> item)
{
((ICollection<KeyValuePair<Object, PPtr>>)_dictionary).Add(item);
}
bool ICollection<KeyValuePair<Object, PPtr>>.Contains(KeyValuePair<Object, PPtr> item)
{
return ((ICollection<KeyValuePair<Object, PPtr>>)_dictionary).Contains(item);
}
void ICollection<KeyValuePair<Object, PPtr>>.CopyTo(KeyValuePair<Object, PPtr>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<Object, PPtr>>)_dictionary).CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<Object, PPtr>>.Remove(KeyValuePair<Object, PPtr> item)
{
return ((ICollection<KeyValuePair<Object, PPtr>>)_dictionary).Remove(item);
}
}
@@ -1,41 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>Latest</LangVersion>
<Nullable>enable</Nullable>
<IsTrimmable>true</IsTrimmable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<Authors>ds5678</Authors>
<Company>AssetRipper</Company>
<Version>1.0.3</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<PackageId>AssetRipper.Mining.PredefinedAssets</PackageId>
<PackageTags>C# Unity</PackageTags>
<RepositoryUrl>https://github.com/AssetRipper/AssetRipper.Mining</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryType>git</RepositoryType>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
<Copyright>Copyright (c) 2023 - 2024 ds5678</Copyright>
<Description>Library for storing datamined asset information.</Description>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AssetRipper.Primitives" Version="2.1.1" />
<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>
<PackageReference Include="System.Text.Json" Version="8.0.1" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>Latest</LangVersion>
<Nullable>enable</Nullable>
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<Authors>ds5678</Authors>
<Company>AssetRipper</Company>
<Version>1.5.0</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<PackageId>AssetRipper.Mining.PredefinedAssets</PackageId>
<PackageTags>C# Unity</PackageTags>
<RepositoryUrl>https://github.com/AssetRipper/AssetRipper.Mining</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryType>git</RepositoryType>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
<Copyright>Copyright (c) 2023 - 2025 ds5678</Copyright>
<Description>Library for storing datamined asset information.</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AssetRipper.Primitives" Version="3.1.6" />
<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.15.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="9.0.4" />
</ItemGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>
</Project>
@@ -0,0 +1,5 @@
using System.Numerics;
namespace AssetRipper.Mining.PredefinedAssets;
public record struct AxisAlignedBoundingBox(Vector3 Center, Vector3 Extent);
@@ -1,20 +1,35 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetRipper.Mining.PredefinedAssets;
public readonly record struct EngineResourceData(Dictionary<long, Object> DefaultResources, Dictionary<long, Object> ExtraResources)
public readonly record struct EngineResourceData
{
public EngineResourceData() : this(new(), new())
{
public Dictionary<long, Object> DefaultResources { get; }
public Dictionary<long, Object> ExtraResources { get; }
public EngineResourceData()
{
DefaultResources = new();
ExtraResources = new();
}
[JsonConstructor]
public EngineResourceData(Dictionary<long, Object>? defaultResources, Dictionary<long, Object>? extraResources)
{
DefaultResources = defaultResources ?? new();
ExtraResources = extraResources ?? new();
}
public readonly string ToJson()
{
return JsonSerializer.Serialize(this, InternalSerializerContext.Default.EngineResourceData);
return JsonSerializer.Serialize(this, MiningSerializerContext.Default.EngineResourceData);
}
public static EngineResourceData FromJson(string text)
{
return JsonSerializer.Deserialize(text, InternalSerializerContext.Default.EngineResourceData);
return string.IsNullOrEmpty(text)
? new()
: JsonSerializer.Deserialize(text, MiningSerializerContext.Default.EngineResourceData);
}
}
@@ -1,6 +1,4 @@
using System.Diagnostics.CodeAnalysis;
namespace AssetRipper.Mining.PredefinedAssets;
namespace AssetRipper.Mining.PredefinedAssets;
public sealed record class GameObject : Object
{
@@ -1,10 +0,0 @@
using System.Text.Json.Serialization;
namespace AssetRipper.Mining.PredefinedAssets;
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(EngineResourceData))]
[JsonSerializable(typeof(UnityPackageData))]
internal sealed partial class InternalSerializerContext : JsonSerializerContext
{
}
+3 -1
View File
@@ -4,15 +4,17 @@ public sealed record class Mesh : NamedObject
{
public required int VertexCount { get; init; }
public required int SubMeshCount { get; init; }
public required AxisAlignedBoundingBox LocalAABB { get; init; }
public Mesh()
{
}
[SetsRequiredMembers]
public Mesh(string name, int vertexCount, int subMeshCount) : base(name)
public Mesh(string name, int vertexCount, int subMeshCount, AxisAlignedBoundingBox localAabb) : base(name)
{
VertexCount = vertexCount;
SubMeshCount = subMeshCount;
LocalAABB = localAabb;
}
}
@@ -0,0 +1,11 @@
using System.Text.Json.Serialization;
namespace AssetRipper.Mining.PredefinedAssets;
[JsonSourceGenerationOptions(WriteIndented = true, IncludeFields = true)]
[JsonSerializable(typeof(EngineResourceData))]
[JsonSerializable(typeof(UnityPackageData))]
[JsonSerializable(typeof(ReferenceAssemblyData))]
public sealed partial class MiningSerializerContext : JsonSerializerContext
{
}
@@ -17,4 +17,9 @@ public sealed record class MonoScript : Object
Namespace = @namespace;
ClassName = className;
}
public static MonoScript FromType(Type type)
{
return new MonoScript(type.Assembly.GetName().Name ?? "", type.Namespace ?? "", type.Name);
}
}
@@ -59,11 +59,11 @@ public abstract record class Object
public string ToJson()
{
return JsonSerializer.Serialize(this, InternalSerializerContext.Default.Object);
return JsonSerializer.Serialize(this, MiningSerializerContext.Default.Object);
}
public static Object? FromJson(string text)
{
return JsonSerializer.Deserialize(text, InternalSerializerContext.Default.Object);
return JsonSerializer.Deserialize(text, MiningSerializerContext.Default.Object);
}
}
+3 -5
View File
@@ -1,13 +1,11 @@
using AssetRipper.Primitives;
namespace AssetRipper.Mining.PredefinedAssets;
namespace AssetRipper.Mining.PredefinedAssets;
/// <summary>
/// A data structure representing a reference to an asset.
/// </summary>
/// <param name="FileID">The local identifier within the file.</param>
/// <param name="Guid">The global identifier for the file.</param>
/// <param name="Meta">The type of file. </param>
public readonly record struct PPtr(long FileID, UnityGuid Guid, AssetType Meta)
/// <param name="Type">The type of file. </param>
public readonly record struct PPtr(long FileID, UnityGuid Guid, AssetType Type)
{
}
@@ -0,0 +1 @@
# AssetRipper.Mining.PredefinedAssets
@@ -0,0 +1,16 @@
using System.Text.Json;
namespace AssetRipper.Mining.PredefinedAssets;
public readonly record struct ReferenceAssemblyData(List<string> Unity, List<string>? Mono2, List<string>? Mono4, List<string>? CoreCLR)
{
public string ToJson()
{
return JsonSerializer.Serialize(this, MiningSerializerContext.Default.ReferenceAssemblyData);
}
public static ReferenceAssemblyData FromJson(string json)
{
return JsonSerializer.Deserialize<ReferenceAssemblyData>(json, MiningSerializerContext.Default.ReferenceAssemblyData);
}
}
@@ -1,6 +1,4 @@
using System.Diagnostics.CodeAnalysis;
namespace AssetRipper.Mining.PredefinedAssets;
namespace AssetRipper.Mining.PredefinedAssets;
public sealed record class Shader : NamedObject
{
+15 -2
View File
@@ -2,7 +2,9 @@
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(UInt128))]
#else
using AssetRipper.Mining.PredefinedAssets;
using System.Buffers;
using System.Numerics;
using System.Text;
using System.Text.Json.Serialization;
namespace System;
@@ -35,6 +37,15 @@ public readonly record struct UInt128
return TryParse(value, out UInt128 result) ? result : throw new FormatException("Input string was not in a correct format.");
}
internal static UInt128 Parse(ReadOnlySpan<byte> value)
{
byte[] rented = ArrayPool<byte>.Shared.Rent(value.Length);
value.CopyTo(rented);
UInt128 result = Parse(Encoding.ASCII.GetString(rented, 0, value.Length));
ArrayPool<byte>.Shared.Return(rented);
return result;
}
public static bool TryParse(string value, out UInt128 result)
{
if (BigInteger.TryParse(value, out BigInteger bigInteger))
@@ -57,8 +68,10 @@ public readonly record struct UInt128
private static UInt128 FromBigInteger(BigInteger value)
{
ulong upper = (ulong)(value >> 64);
ulong lower = (ulong)value;
ulong lower = (ulong)(value & LowerBitMask);
return new UInt128(upper, lower);
}
private static readonly BigInteger LowerBitMask = new BigInteger(ulong.MaxValue);
}
#endif
#endif
@@ -8,7 +8,7 @@ public sealed class UInt128JsonConverter : JsonConverter<UInt128>
{
public override UInt128 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return UInt128.Parse(reader.GetString() ?? throw new JsonException("String was read as null"));
return UInt128.Parse(reader.ValueSpan);
}
public override void Write(Utf8JsonWriter writer, UInt128 value, JsonSerializerOptions options)
@@ -16,4 +16,4 @@ public sealed class UInt128JsonConverter : JsonConverter<UInt128>
writer.WriteRawValue(value.ToString());
}
}
#endif
#endif
@@ -1,5 +1,4 @@
using AssetRipper.Primitives;
using System.Text.Json;
using System.Text.Json;
namespace AssetRipper.Mining.PredefinedAssets;
@@ -13,19 +12,15 @@ namespace AssetRipper.Mining.PredefinedAssets;
/// <param name="Version">The version or git url of this Unity package. Only applicable if <paramref name="UsedInPackageJson"/> is true.</param>
/// <param name="UsedInPackageJson">If true, this Unity package should be referenced in the project's package json file.</param>
/// <param name="Assemblies">A dictionary of compiled dll assemblies in the package.</param>
/// <param name="Scripts">A dictionary of loose cs scripts in the package. Assembly name : { Full name : Guid }</param>
/// <param name="Assets">A dictionary of assets in the package.</param>
public readonly record struct UnityPackageData(
string Name,
string Version,
bool UsedInPackageJson,
Dictionary<string, UnityGuid> Assemblies,
Dictionary<string, Dictionary<string, UnityGuid>> Scripts,
List<KeyValuePair<Object, PPtr>> Assets)
AssetDictionary Assets)
{
//Assets is a list because dictionaries require simple types as keys for serialization.
public UnityPackageData(string name, string version, bool usedInPackageJson) : this(name, version, usedInPackageJson, new(), new(), new())
public UnityPackageData(string name, string version, bool usedInPackageJson) : this(name, version, usedInPackageJson, new(), new())
{
}
@@ -35,11 +30,11 @@ public readonly record struct UnityPackageData(
public readonly string ToJson()
{
return JsonSerializer.Serialize(this, InternalSerializerContext.Default.UnityPackageData);
return JsonSerializer.Serialize(this, MiningSerializerContext.Default.UnityPackageData);
}
public static UnityPackageData FromJson(string text)
{
return JsonSerializer.Deserialize(text, InternalSerializerContext.Default.UnityPackageData);
return JsonSerializer.Deserialize(text, MiningSerializerContext.Default.UnityPackageData);
}
}
@@ -1 +1,6 @@
global using System.Diagnostics.CodeAnalysis;
#if NET5_0_OR_GREATER
global using UnityGuid = AssetRipper.Primitives.UnityGuid;
#else
global using UnityGuid = string;
#endif
+6
View File
@@ -16,6 +16,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetRipper.Mining.EngineFi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetRipper.Mining.PredefinedAssets.Tests", "AssetRipper.Mining.PredefinedAssets.Tests\AssetRipper.Mining.PredefinedAssets.Tests.csproj", "{C3E8E9EA-1710-4346-B96A-EA757FB13301}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetRipper.Mining.AssemblyNameCollector", "AssetRipper.Mining.AssemblyNameCollector\AssetRipper.Mining.AssemblyNameCollector.csproj", "{90716FE5-A36D-4F2F-AC14-877BF63053E2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -34,6 +36,10 @@ Global
{C3E8E9EA-1710-4346-B96A-EA757FB13301}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3E8E9EA-1710-4346-B96A-EA757FB13301}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3E8E9EA-1710-4346-B96A-EA757FB13301}.Release|Any CPU.Build.0 = Release|Any CPU
{90716FE5-A36D-4F2F-AC14-877BF63053E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{90716FE5-A36D-4F2F-AC14-877BF63053E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90716FE5-A36D-4F2F-AC14-877BF63053E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90716FE5-A36D-4F2F-AC14-877BF63053E2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE