/// <summary> /// Cleans the specified input items. /// </summary> /// <param name="inputItems">The input items.</param> /// <param name="outputItems">The output items.</param> /// <param name="assetResolver">The asset resolver.</param> /// <param name="cloneInput">if set to <c>true</c> [clone input].</param> /// <exception cref="System.ArgumentNullException"> /// inputItems /// or /// outputItems /// or /// assetResolver /// </exception> /// <exception cref="System.ArgumentException">List cannot contain null items;inputItems</exception> public static void Clean(ICollection <AssetItem> inputItems, ICollection <AssetItem> outputItems, AssetResolver assetResolver, bool cloneInput) { if (inputItems == null) { throw new ArgumentNullException("inputItems"); } if (outputItems == null) { throw new ArgumentNullException("outputItems"); } if (assetResolver == null) { throw new ArgumentNullException("assetResolver"); } // Check that all items are non-null if (inputItems.Any(item => item == null)) { throw new ArgumentException("List cannot contain null items", "inputItems"); } var items = inputItems; if (cloneInput) { items = inputItems.Select(item => item.Clone()).ToList(); } // Check if locations are conflicting var locationConflicts = new Dictionary <AssetItem, UFile>(); foreach (var item in items) { UFile newLocation; if (assetResolver.RegisterLocation(item.Location, out newLocation)) { locationConflicts[item] = newLocation; } } // Check if ids are conflicting var idConflicts = new Dictionary <AssetItem, Guid>(); foreach (var item in items) { Guid newGuid; if (assetResolver.RegisterId(item.Id, out newGuid)) { idConflicts[item] = newGuid; } } // Calculate final guid => guid remapping // Because several asset items can have the same id, we are only using the first one for remapping var idRemap = new Dictionary <Guid, Tuple <Guid, UFile> >(); var locationRemap = new Dictionary <UFile, UFile>(); foreach (var item in items) { if (outputItems.Contains(item)) { continue; } outputItems.Add(item); Guid newGuid; if (!idConflicts.TryGetValue(item, out newGuid)) { newGuid = item.Id; } UFile newLocation; if (locationConflicts.TryGetValue(item, out newLocation) && !locationRemap.ContainsKey(item.Location)) { locationRemap.Add(item.Location, newLocation); } if (!idRemap.ContainsKey(item.Id)) { idRemap.Add(item.Id, new Tuple <Guid, UFile>(newGuid, newLocation ?? item.Location)); } } // Process assets foreach (var item in outputItems) { // Replace Id Guid newGuid; if (idConflicts.TryGetValue(item, out newGuid)) { item.Asset.Id = newGuid; item.IsDirty = true; } // Replace location if (locationConflicts.ContainsKey(item)) { item.Location = locationConflicts[item]; item.IsDirty = true; } // The loop is a one or two-step. // - If there is no link to update, and the asset has not been cloned, we can exist immediately // - If there is links to update, and the asset has not been cloned, we need to clone it and re-enter the loop // to perform the update of the clone asset var links = AssetReferenceAnalysis.Visit(item.Asset).Where(link => link.Reference is IContentReference).ToList(); foreach (var assetLink in links) { var assetReference = (IContentReference)assetLink.Reference; var newId = assetReference.Id; var newLocation = assetReference.Location; bool requireUpdate = false; Tuple <Guid, UFile> newRemap; if (idRemap.TryGetValue(newId, out newRemap) && (newId != newRemap.Item1 || newLocation != newRemap.Item2)) { newId = newRemap.Item1; newLocation = newRemap.Item2; requireUpdate = true; } UFile remapLocation; if (!requireUpdate && locationRemap.TryGetValue(newLocation, out remapLocation)) { newLocation = remapLocation; requireUpdate = true; } if (requireUpdate) { assetLink.UpdateReference(newId, newLocation); item.IsDirty = true; } } } }
/// <summary> /// Cleans the specified input items. /// </summary> /// <param name="package">The package to process (optional).</param> /// <param name="inputItems">The input items.</param> /// <param name="outputItems">The output items.</param> /// <param name="assetResolver">The asset resolver.</param> /// <param name="cloneInput">if set to <c>true</c> [clone input].</param> /// <exception cref="System.ArgumentNullException"> /// inputItems /// or /// outputItems /// or /// assetResolver /// </exception> /// <exception cref="System.ArgumentException">List cannot contain null items;inputItems</exception> public static void Clean(Package package, ICollection <AssetItem> inputItems, ICollection <AssetItem> outputItems, AssetResolver assetResolver, bool cloneInput) { if (inputItems == null) { throw new ArgumentNullException(nameof(inputItems)); } if (outputItems == null) { throw new ArgumentNullException(nameof(outputItems)); } if (assetResolver == null) { throw new ArgumentNullException(nameof(assetResolver)); } // Check that all items are non-null if (inputItems.Any(item => item == null)) { throw new ArgumentException("List cannot contain null items", nameof(inputItems)); } var items = inputItems; if (cloneInput) { items = inputItems.Select(item => item.Clone()).ToList(); } // idRemap should contain only assets that have either 1) their id remapped or 2) their location remapped var idRemap = new Dictionary <Guid, Tuple <Guid, UFile> >(); var itemRemap = new Dictionary <AssetItem, Tuple <Guid, UFile> >(); foreach (var item in items) { if (outputItems.Contains(item)) { continue; } outputItems.Add(item); bool changed = false; Guid newGuid; if (assetResolver.RegisterId(item.Id, out newGuid)) { changed = true; } UFile newLocation; if (assetResolver.RegisterLocation(item.Location, out newLocation)) { changed = true; } var tuple = new Tuple <Guid, UFile>(newGuid != Guid.Empty ? newGuid : item.Id, newLocation ?? item.Location); if (changed) { if (!itemRemap.ContainsKey(item)) { itemRemap.Add(item, tuple); } } if (!idRemap.ContainsKey(item.Id)) { idRemap.Add(item.Id, tuple); } } // Process assets foreach (var item in outputItems) { Tuple <Guid, UFile> remap; if (itemRemap.TryGetValue(item, out remap) && (remap.Item1 != item.Asset.Id || remap.Item2 != item.Location)) { item.Asset.Id = remap.Item1; item.Location = remap.Item2; item.IsDirty = true; } // The loop is a one or two-step. // - If there is no link to update, and the asset has not been cloned, we can exist immediately // - If there is links to update, and the asset has not been cloned, we need to clone it and re-enter the loop // to perform the update of the clone asset var links = AssetReferenceAnalysis.Visit(item.Asset).Where(link => link.Reference is IReference).ToList(); foreach (var assetLink in links) { var assetReference = (IReference)assetLink.Reference; var newId = assetReference.Id; if (idRemap.TryGetValue(newId, out remap) && IsNewReference(remap, assetReference)) { assetLink.UpdateReference(remap.Item1, remap.Item2); item.IsDirty = true; } } // Fix base if there are any if (item.Asset.Base != null && idRemap.TryGetValue(item.Asset.Base.Id, out remap) && IsNewReference(remap, item.Asset.Base)) { item.Asset.Base.Asset.Id = remap.Item1; item.Asset.Base = new AssetBase(remap.Item2, item.Asset.Base.Asset); item.IsDirty = true; } // Fix base parts if there are any remap for them as well if (item.Asset.BaseParts != null) { for (int i = 0; i < item.Asset.BaseParts.Count; i++) { var basePart = item.Asset.BaseParts[i]; if (idRemap.TryGetValue(basePart.Id, out remap) && IsNewReference(remap, basePart)) { basePart.Asset.Id = remap.Item1; item.Asset.BaseParts[i] = new AssetBase(remap.Item2, basePart.Asset); item.IsDirty = true; } } } } // Process roots (until references in package are handled in general) if (package != null) { UpdateRootAssets(package.RootAssets, idRemap); // We check dependencies to be consistent with other places, but nothing should be changed in there // (except if we were to instantiate multiple packages referencing each other at once?) foreach (var dependency in package.LocalDependencies) { if (dependency.RootAssets != null) { UpdateRootAssets(dependency.RootAssets, idRemap); } } foreach (var dependency in package.Meta.Dependencies) { if (dependency.RootAssets != null) { UpdateRootAssets(dependency.RootAssets, idRemap); } } } }
/// <summary> /// Cleans the specified input items. /// </summary> /// <param name="package">The package to process (optional).</param> /// <param name="inputItems">The input items.</param> /// <param name="outputItems">The output items.</param> /// <param name="assetResolver">The asset resolver.</param> /// <param name="cloneInput">if set to <c>true</c> [clone input].</param> /// <param name="removeUnloadableObjects">If set to <c>true</c>, assets will be cloned with <see cref="AssetClonerFlags.RemoveUnloadableObjects"/>.</param> /// <exception cref="System.ArgumentNullException"> /// inputItems /// or /// outputItems /// or /// assetResolver /// </exception> /// <exception cref="System.ArgumentException">List cannot contain null items;inputItems</exception> public static void Clean(Package package, ICollection<AssetItem> inputItems, ICollection<AssetItem> outputItems, AssetResolver assetResolver, bool cloneInput, bool removeUnloadableObjects) { if (inputItems == null) throw new ArgumentNullException(nameof(inputItems)); if (outputItems == null) throw new ArgumentNullException(nameof(outputItems)); if (assetResolver == null) throw new ArgumentNullException(nameof(assetResolver)); // Check that all items are non-null if (inputItems.Any(item => item == null)) { throw new ArgumentException("List cannot contain null items", nameof(inputItems)); } var items = inputItems; if (cloneInput) { items = inputItems.Select(item => item.Clone(flags: removeUnloadableObjects ? AssetClonerFlags.RemoveUnloadableObjects : AssetClonerFlags.None)).ToList(); } // idRemap should contain only assets that have either 1) their id remapped or 2) their location remapped var idRemap = new Dictionary<AssetId, Tuple<AssetId, UFile>>(); var itemRemap = new Dictionary<AssetItem, Tuple<AssetId, UFile>>(); foreach (var item in items) { if (outputItems.Contains(item)) { continue; } outputItems.Add(item); bool changed = false; AssetId newId; if (assetResolver.RegisterId(item.Id, out newId)) { changed = true; } UFile newLocation; if (assetResolver.RegisterLocation(item.Location, out newLocation)) { changed = true; } var tuple = new Tuple<AssetId, UFile>(newId != AssetId.Empty ? newId : item.Id, newLocation ?? item.Location); if (changed) { if (!itemRemap.ContainsKey(item)) { itemRemap.Add(item, tuple); } } if (!idRemap.ContainsKey(item.Id)) { idRemap.Add(item.Id, tuple); } } // Process assets foreach (var item in outputItems) { Tuple<AssetId, UFile> remap; if (itemRemap.TryGetValue(item, out remap) && (remap.Item1 != item.Asset.Id || remap.Item2 != item.Location)) { item.Asset.Id = remap.Item1; item.Location = remap.Item2; item.IsDirty = true; } // The loop is a one or two-step. // - If there is no link to update, and the asset has not been cloned, we can exist immediately // - If there is links to update, and the asset has not been cloned, we need to clone it and re-enter the loop // to perform the update of the clone asset var links = AssetReferenceAnalysis.Visit(item.Asset).Where(link => link.Reference is IReference).ToList(); foreach (var assetLink in links) { var assetReference = (IReference)assetLink.Reference; var newId = assetReference.Id; if (idRemap.TryGetValue(newId, out remap) && IsNewReference(remap, assetReference)) { assetLink.UpdateReference(remap.Item1, remap.Item2); item.IsDirty = true; } } // Fix base parts if there are any remap for them as well var assetComposite = item.Asset as IAssetComposite; if (assetComposite != null) { foreach (var basePart in assetComposite.CollectParts()) { if (basePart.Base != null && idRemap.TryGetValue(basePart.Base.BasePartAsset.Id, out remap) && IsNewReference(remap, basePart.Base.BasePartAsset)) { var newAssetReference = new AssetReference(remap.Item1, remap.Item2); basePart.UpdateBase(new BasePart(newAssetReference, basePart.Base.BasePartId, basePart.Base.InstanceId)); item.IsDirty = true; } } } } // Process roots (until references in package are handled in general) if (package != null) { UpdateRootAssets(package.RootAssets, idRemap); // We check dependencies to be consistent with other places, but nothing should be changed in there // (except if we were to instantiate multiple packages referencing each other at once?) foreach (var dependency in package.LocalDependencies) { if (dependency.RootAssets != null) UpdateRootAssets(dependency.RootAssets, idRemap); } foreach (var dependency in package.Meta.Dependencies) { if (dependency.RootAssets != null) UpdateRootAssets(dependency.RootAssets, idRemap); } } }
/// <summary> /// Cleans the specified input items. /// </summary> /// <param name="inputItems">The input items.</param> /// <param name="outputItems">The output items.</param> /// <param name="assetResolver">The asset resolver.</param> /// <param name="cloneInput">if set to <c>true</c> [clone input].</param> /// <exception cref="System.ArgumentNullException"> /// inputItems /// or /// outputItems /// or /// assetResolver /// </exception> /// <exception cref="System.ArgumentException">List cannot contain null items;inputItems</exception> public static void Clean(ICollection<AssetItem> inputItems, ICollection<AssetItem> outputItems, AssetResolver assetResolver, bool cloneInput) { if (inputItems == null) throw new ArgumentNullException("inputItems"); if (outputItems == null) throw new ArgumentNullException("outputItems"); if (assetResolver == null) throw new ArgumentNullException("assetResolver"); // Check that all items are non-null if (inputItems.Any(item => item == null)) { throw new ArgumentException("List cannot contain null items", "inputItems"); } var items = inputItems; if (cloneInput) { items = inputItems.Select(item => item.Clone()).ToList(); } // Check if locations are conflicting var locationConflicts = new Dictionary<AssetItem, UFile>(); foreach (var item in items) { UFile newLocation; if (assetResolver.RegisterLocation(item.Location, out newLocation)) { locationConflicts[item] = newLocation; } } // Check if ids are conflicting var idConflicts = new Dictionary<AssetItem, Guid>(); foreach (var item in items) { Guid newGuid; if (assetResolver.RegisterId(item.Id, out newGuid)) { idConflicts[item] = newGuid; } } // Calculate final guid => guid remapping // Because several asset items can have the same id, we are only using the first one for remapping var idRemap = new Dictionary<Guid, Tuple<Guid, UFile>>(); var locationRemap = new Dictionary<UFile, UFile>(); foreach (var item in items) { if (outputItems.Contains(item)) { continue; } outputItems.Add(item); Guid newGuid; if (!idConflicts.TryGetValue(item, out newGuid)) { newGuid = item.Id; } UFile newLocation; if (locationConflicts.TryGetValue(item, out newLocation) && !locationRemap.ContainsKey(item.Location)) { locationRemap.Add(item.Location, newLocation); } if (!idRemap.ContainsKey(item.Id)) { idRemap.Add(item.Id, new Tuple<Guid, UFile>(newGuid, newLocation ?? item.Location)); } } // Process assets foreach (var item in outputItems) { // Replace Id Guid newGuid; if (idConflicts.TryGetValue(item, out newGuid)) { item.Asset.Id = newGuid; item.IsDirty = true; } // Replace location if (locationConflicts.ContainsKey(item)) { item.Location = locationConflicts[item]; item.IsDirty = true; } // The loop is a one or two-step. // - If there is no link to update, and the asset has not been cloned, we can exist immediately // - If there is links to update, and the asset has not been cloned, we need to clone it and re-enter the loop // to perform the update of the clone asset var links = AssetReferenceAnalysis.Visit(item.Asset).Where(link => link.Reference is IContentReference).ToList(); foreach (var assetLink in links) { var assetReference = (IContentReference)assetLink.Reference; var newId = assetReference.Id; var newLocation = assetReference.Location; bool requireUpdate = false; Tuple<Guid, UFile> newRemap; if (idRemap.TryGetValue(newId, out newRemap) && (newId != newRemap.Item1 || newLocation != newRemap.Item2)) { newId = newRemap.Item1; newLocation = newRemap.Item2; requireUpdate = true; } UFile remapLocation; if (!requireUpdate && locationRemap.TryGetValue(newLocation, out remapLocation)) { newLocation = remapLocation; requireUpdate = true; } if (requireUpdate) { assetLink.UpdateReference(newId, newLocation); item.IsDirty = true; } } } }