diff --git a/Source/AssetRipper.Processing/AssetGroup.cs b/Source/AssetRipper.Processing/AssetGroup.cs index e959c369a..73035578a 100644 --- a/Source/AssetRipper.Processing/AssetGroup.cs +++ b/Source/AssetRipper.Processing/AssetGroup.cs @@ -1,5 +1,6 @@ using AssetRipper.Assets; using AssetRipper.Assets.Metadata; +using System.Diagnostics; namespace AssetRipper.Processing; @@ -16,6 +17,7 @@ public abstract class AssetGroup : UnityObjectBase MainAsset = this; foreach (IUnityObjectBase asset in Assets) { + Debug.Assert(asset.MainAsset is null || asset.MainAsset == this, "Asset already has a main asset assigned."); asset.MainAsset = this; } } diff --git a/Source/AssetRipper.Processing/ScriptableObject/ScriptableObjectGroup.cs b/Source/AssetRipper.Processing/ScriptableObject/ScriptableObjectGroup.cs index 436278a31..ce21155c8 100644 --- a/Source/AssetRipper.Processing/ScriptableObject/ScriptableObjectGroup.cs +++ b/Source/AssetRipper.Processing/ScriptableObject/ScriptableObjectGroup.cs @@ -12,7 +12,7 @@ public sealed class ScriptableObjectGroup : AssetGroup, INamed Root = root; } - public IMonoBehaviour Root { get; set; } + public IMonoBehaviour Root { get; } public List Children { get; } = []; public override IEnumerable Assets => Children.Prepend(Root); @@ -36,7 +36,7 @@ public sealed class ScriptableObjectGroup : AssetGroup, INamed { this.WalkPrimitiveField(walker, Name); walker.DivideAsset(this); - this.WalkPrimitiveField(walker, FileExtension ?? ""); + this.WalkPrimitiveField(walker, FileExtension ?? "", nameof(FileExtension)); walker.DivideAsset(this); this.WalkPPtrField(walker, Root); walker.DivideAsset(this); diff --git a/Source/AssetRipper.Processing/ScriptableObject/ScriptableObjectProcessor.cs b/Source/AssetRipper.Processing/ScriptableObject/ScriptableObjectProcessor.cs index 0d3907afc..d9d956da6 100644 --- a/Source/AssetRipper.Processing/ScriptableObject/ScriptableObjectProcessor.cs +++ b/Source/AssetRipper.Processing/ScriptableObject/ScriptableObjectProcessor.cs @@ -55,14 +55,24 @@ public class ScriptableObjectProcessor : IAssetProcessor continue; } - children.Add(child); - SerializableStructure? childStructure = child.LoadStructure(); if (childStructure is null) { continue; } + if (!childStructure.TryGetField("m_Parent", out SerializableValue parent)) + { + continue; + } + + if (root.Collection.TryGetAsset(parent.AsPPtr.FileID, parent.AsPPtr.PathID) != root) + { + continue; + } + + children.Add(child); + if (childStructure.TryGetField("m_Clips", out SerializableValue clips)) { foreach (SerializableStructure clip in clips.AsAssetArray.Cast()) diff --git a/Source/AssetRipper.Tests/ExportTests.cs b/Source/AssetRipper.Tests/ExportTests.cs index 135501969..bab55ca86 100644 --- a/Source/AssetRipper.Tests/ExportTests.cs +++ b/Source/AssetRipper.Tests/ExportTests.cs @@ -6,6 +6,7 @@ using AssetRipper.Import.Structure.Assembly.Managers; using AssetRipper.IO.Files; using AssetRipper.Primitives; using AssetRipper.Processing; +using AssetRipper.Processing.ScriptableObject; using AssetRipper.SourceGenerated.Classes.ClassID_114; using AssetRipper.SourceGenerated.Classes.ClassID_115; using AssetRipper.SourceGenerated.Classes.ClassID_43; @@ -41,6 +42,48 @@ internal class ExportTests Assert.That(fileSystem.File.Exists("/output/ExportedProject/Assets/MonoBehaviour/MonoBehaviour.asset")); } + [Test] + public void ScriptableObjectGroupIsExported_1() + { + ProcessedAssetCollection collection = AssetCreator.CreateCollection(UnityVersion.V_2022); + + IMonoBehaviour behaviour1 = collection.CreateMonoBehaviour(); + IMonoBehaviour behaviour2 = collection.CreateMonoBehaviour(); + ScriptableObjectGroup group = collection.CreateAsset(default, (assetInfo) => new ScriptableObjectGroup(assetInfo, behaviour1)); + group.Children.Add(behaviour2); + group.SetMainAsset(); + + Assert.DoesNotThrow(() => Export(collection)); + } + + [Test] + public void ScriptableObjectGroupIsExported_2() + { + ProcessedAssetCollection collection = AssetCreator.CreateCollection(UnityVersion.V_2022); + + IMonoBehaviour behaviour1 = collection.CreateMonoBehaviour(); + IMonoBehaviour behaviour2 = collection.CreateMonoBehaviour(); + ScriptableObjectGroup group = collection.CreateAsset(default, (assetInfo) => new ScriptableObjectGroup(assetInfo, behaviour2)); + group.Children.Add(behaviour1); + group.SetMainAsset(); + + Assert.DoesNotThrow(() => Export(collection)); + } + + [Test] + public void ScriptableObjectGroupIsExported_3() + { + ProcessedAssetCollection collection = AssetCreator.CreateCollection(UnityVersion.V_2022); + + IMonoBehaviour behaviour1 = collection.CreateMonoBehaviour(); + ScriptableObjectGroup group = collection.CreateAsset(default, (assetInfo) => new ScriptableObjectGroup(assetInfo, behaviour1)); + IMonoBehaviour behaviour2 = collection.CreateMonoBehaviour(); + group.Children.Add(behaviour2); + group.SetMainAsset(); + + Assert.DoesNotThrow(() => Export(collection)); + } + [Test] public void EmptyMeshIsExported() {