diff --git a/Editor/Assets/AssetTree.cs b/Editor/Assets/AssetTree.cs index 2c0c791..6d3575e 100644 --- a/Editor/Assets/AssetTree.cs +++ b/Editor/Assets/AssetTree.cs @@ -7,13 +7,18 @@ namespace BundleKit.Assets { - [DebuggerDisplay("{assetsFileInstance.name}/{name} fid:{FileId} pid:{PathId} children: {Children.Count}")] + [DebuggerDisplay("{assetExternal.file.name}/{name} fid:{FileId} pid:{PathId} children: {Children.Count}")] public struct AssetTree : IEquatable { public string name; - public AssetExternal assetExternal; + public string resourceManagerName; + public AssetExternal sourceData; public int FileId; public long PathId; + /// + /// List of dependencies in the dependency graph for the object has been resolved. + /// Null if dependencies are unknown. + /// public List Children; public IEnumerable<(int fileId, long pathId)> FlattenIds(bool enterDependencies) @@ -24,13 +29,17 @@ public struct AssetTree : IEquatable foreach (var result in child.FlattenIds(enterDependencies)) yield return result; } - public IEnumerable Flatten(bool enterDependencies) + public IEnumerable WithDeps(bool enterDependencies) { yield return this; - foreach (var child in Children) - if (enterDependencies || child.FileId == 0) - foreach (var result in child.Flatten(enterDependencies)) - yield return result; + if (enterDependencies) + foreach (var child in Children) + yield return child; + } + + public string GetBkCatalogName() + { + return string.IsNullOrEmpty(resourceManagerName) ? name.ToLower() : resourceManagerName; } public override bool Equals(object obj) @@ -40,14 +49,14 @@ public override bool Equals(object obj) public bool Equals(AssetTree other) { - return EqualityComparer.Default.Equals(assetExternal.file, other.assetExternal.file) && + return EqualityComparer.Default.Equals(sourceData.file, other.sourceData.file) && PathId == other.PathId; } public override int GetHashCode() { int hashCode = -1120199924; - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(assetExternal.file); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(sourceData.file); hashCode = hashCode * -1521134295 + PathId.GetHashCode(); return hashCode; } diff --git a/Editor/Assets/Filter.cs b/Editor/Assets/Filter.cs index 2464be8..81660f8 100644 --- a/Editor/Assets/Filter.cs +++ b/Editor/Assets/Filter.cs @@ -1,12 +1,46 @@ -using AssetsTools.NET.Extra; +using AssetsTools.NET; +using AssetsTools.NET.Extra; using System; +using System.Linq; +using System.Text.RegularExpressions; +using UnityEngine; namespace BundleKit.Assets { [Serializable] public struct Filter { + [Tooltip("An object must have a name matching one of these expressions. Leave this list empty to match any name.")] public string[] nameRegex; + [Tooltip("Unity built-in object class required for this filter to match. Use 'Object' to match any kind of object.")] public AssetClassID assetClass; + + private Regex[] regexCache; + + public bool Match(AssetFileInfo assetFileInfo, string name) + { + if (!((AssetClassID)assetFileInfo.TypeId == assetClass)) + { + return false; + } + + // match all objects with the given class if not filtering by name. + if (nameRegex.Length == 0) + { + return true; + } + + regexCache ??= nameRegex.Select(p => new Regex(p, RegexOptions.IgnoreCase)).ToArray(); + + foreach (var regex in regexCache) + { + if (regex.IsMatch(name)) + { + return true; + } + } + + return false; + } } } \ No newline at end of file diff --git a/Templates.meta b/Editor/Assets/Replacers.meta similarity index 77% rename from Templates.meta rename to Editor/Assets/Replacers.meta index 3c084d0..0ab340e 100644 --- a/Templates.meta +++ b/Editor/Assets/Replacers.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f599a8660ef90f3428030ddc35e25373 +guid: 19ae86bcef2e4bb4482719e69c45cb01 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Editor/Assets/Replacers/DeferredBaseFieldSerializer.cs b/Editor/Assets/Replacers/DeferredBaseFieldSerializer.cs new file mode 100644 index 0000000..9cbfb6c --- /dev/null +++ b/Editor/Assets/Replacers/DeferredBaseFieldSerializer.cs @@ -0,0 +1,46 @@ +using AssetsTools.NET; +using System.IO; + +namespace BundleKit.Assets.Replacers +{ + /// + /// Serialize the provided AssetBaseValueField reference at write time. (Closure) + /// + /// Use this to set up serialization on an aset that is still actively being manipulated, + /// or to defer generating the serialized byte[] until it's actually needed (and then can be GC'd) + /// + public class DeferredBaseFieldSerializer : IContentReplacer + { + private AssetTypeValueField baseField; + readonly private bool discardAfterWrite; + + /// + /// Serialize baseField at write time, discarding the reference if discardAfterWrite is true. + /// + /// Note that discarding the reference means the writer will only work once. + /// + /// Reference to the asset's base field + /// If true, the reference will be discarded after write so that it can be garbage collected. + public DeferredBaseFieldSerializer(AssetTypeValueField baseField, bool discardAfterWrite = true) + { + this.baseField = baseField; + this.discardAfterWrite = discardAfterWrite; + } + + public Stream GetPreviewStream() => + throw new System.NotImplementedException(); + + public ContentReplacerType GetReplacerType() => ContentReplacerType.AddOrModify; + + public bool HasPreview() => false; + + public void Write(AssetsFileWriter writer) + { + writer.BaseStream.Write(baseField.WriteToByteArray(writer.BigEndian)); + if (discardAfterWrite) + { + baseField = null; + } + } + } +} \ No newline at end of file diff --git a/Editor/PipelineJobs/DestroyBundlePreloadTableJob.cs.meta b/Editor/Assets/Replacers/DeferredBaseFieldSerializer.cs.meta similarity index 83% rename from Editor/PipelineJobs/DestroyBundlePreloadTableJob.cs.meta rename to Editor/Assets/Replacers/DeferredBaseFieldSerializer.cs.meta index 958841f..042f8d9 100644 --- a/Editor/PipelineJobs/DestroyBundlePreloadTableJob.cs.meta +++ b/Editor/Assets/Replacers/DeferredBaseFieldSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: cb030344aa0a35946b2bc3f40dae2571 +guid: ebbfc9bc35a672a4a88a66acba7e4d11 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Editor/Assets/ResourceManagerDb.cs b/Editor/Assets/ResourceManagerDb.cs new file mode 100644 index 0000000..259e2ff --- /dev/null +++ b/Editor/Assets/ResourceManagerDb.cs @@ -0,0 +1,75 @@ +using AssetsTools.NET; +using AssetsTools.NET.Extra; +using System.Collections.Generic; + +namespace BundleKit.Assets +{ + /// + /// Provides data queries into ResourceManager. + /// + /// ResourceManager stores data for APIs such as Resources.Load() to function. + /// It stores the file paths, preload tables, and dependency information. + /// In a build, this data lives in globalgamemanagers. + /// + public class ResourceManagerDb + { + private readonly Dictionary assetToName = new(); + + private const string ggmFileName = "globalgamemanagers"; + + /// + /// Create ResourceManagerDb from the provied AssetsManager. + /// am must have already loaded globalgamemanagers. + /// + /// AssetsManager representing a game build, with globalgamemanagers file loaded. + public ResourceManagerDb(AssetsManager am) + { + var ggmFileInstance = am.FileLookup[ggmFileName]; + var rmInfo = ggmFileInstance.file.GetAssetsOfType(AssetClassID.ResourceManager)[0]; + var rmBase = am.GetBaseField(ggmFileInstance, rmInfo); + var ggmExternals = ggmFileInstance.file.Metadata.Externals; + + ParseMContainer(rmBase, ggmExternals); + + // Skipping implementation for m_DependentAssets until it's needed + } + + public string this[AssetId id] => assetToName[id]; + + public bool TryGetName(AssetId id, out string resourceManagerName) + => assetToName.TryGetValue(id, out resourceManagerName); + + public bool TryGetName(string assetsFileName, long pathIdInFile, out string resourceManagerName) + => assetToName.TryGetValue(new AssetId(assetsFileName, pathIdInFile), out resourceManagerName); + + private void ParseMContainer(AssetTypeValueField rmBase, List ggmExternals) + { + var mContainer = rmBase["m_Container"]; + foreach (var entry in mContainer.Children[0].Children) + { + string name = entry.Children[0].AsString; + int pathId = entry.Children[1].Children[0].AsInt; + long fileId = entry.Children[1].Children[1].AsLong; + + if (pathId == 0) + { + // Some entries have m_FileID: 0, m_PathID: 0 + // not sure what causes this, but may be a path for an asset that didn't go into the build. + continue; + } + + string containerFileName; + if (pathId > 0) + { + containerFileName = ggmExternals[pathId - 1].OriginalPathName; + } + else + { + containerFileName = ggmFileName; + } + + assetToName.Add(new AssetId(containerFileName, fileId), name); + } + } + } +} diff --git a/Editor/PipelineJobs/RemoveAllBundleAssets.cs.meta b/Editor/Assets/ResourceManagerDb.cs.meta similarity index 83% rename from Editor/PipelineJobs/RemoveAllBundleAssets.cs.meta rename to Editor/Assets/ResourceManagerDb.cs.meta index 180a903..76b7036 100644 --- a/Editor/PipelineJobs/RemoveAllBundleAssets.cs.meta +++ b/Editor/Assets/ResourceManagerDb.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7058a557b32f159459b6fc0c37f94a22 +guid: 9d3a4ad78eb7fd34c89bf0fc7e59c290 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Editor/BundleKit.Editor.asmdef b/Editor/BundleKit.Editor.asmdef index 5ab2e17..8d17010 100644 --- a/Editor/BundleKit.Editor.asmdef +++ b/Editor/BundleKit.Editor.asmdef @@ -1,12 +1,12 @@ { "name": "BundleKit.Editor", + "rootNamespace": "", "references": [ "ThunderKit.Common", "ThunderKit.Core", "Unity.ScriptableBuildPipeline", "Unity.ScriptableBuildPipeline.Editor" ], - "optionalUnityReferences": [], "includePlatforms": [ "Editor" ], @@ -14,8 +14,11 @@ "allowUnsafeCode": false, "overrideReferences": true, "precompiledReferences": [ - "AssetsTools.NET.dll" + "AssetsTools.NET.dll", + "AssetsTools.NET.Texture.dll" ], "autoReferenced": false, - "defineConstraints": [] + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/Editor/Bundles/Catalog.cs b/Editor/Bundles/Catalog.cs index c3f2fe2..d8446e2 100644 --- a/Editor/Bundles/Catalog.cs +++ b/Editor/Bundles/Catalog.cs @@ -29,8 +29,8 @@ public void Initialize() var (bun, bundleAssetsFile, assetBundleExtAsset) = am.LoadBundle(path); - var bundleBaseField = assetBundleExtAsset.instance.GetBaseField(); - var bundleName = bundleBaseField.GetValue("m_AssetBundleName").AsString(); + var bundleBaseField = assetBundleExtAsset.baseField; + var bundleName = bundleBaseField["m_AssetBundleName"].AsString; am.UnloadAll(); @@ -53,7 +53,7 @@ public void Initialize() break; } } - var fileMapJson = bundle.LoadAsset("FileMap"); + var fileMapJson = bundle.LoadAsset("BundleKitFileMap"); var fileMap = JsonUtility.FromJson(fileMapJson.text); var lookup = fileMap.Maps.ToDictionary(element => element.LocalId, element => element.OriginId); diff --git a/Editor/Editors/SerializableMaterialDataImporterEditor.cs b/Editor/Editors/SerializableMaterialDataImporterEditor.cs index 95188cf..4e18004 100644 --- a/Editor/Editors/SerializableMaterialDataImporterEditor.cs +++ b/Editor/Editors/SerializableMaterialDataImporterEditor.cs @@ -1,7 +1,12 @@ using BundleKit.Assets; using BundleKit.Bundles; using UnityEditor; +#if UNITY_2020_2_OR_NEWER +using UnityEditor.AssetImporters; +#else using UnityEditor.Experimental.AssetImporters; +#endif + using UnityEditorInternal; using UnityEngine; diff --git a/Editor/Importers/CatalogImporter.cs b/Editor/Importers/CatalogImporter.cs index fe7a524..f4c56e3 100644 --- a/Editor/Importers/CatalogImporter.cs +++ b/Editor/Importers/CatalogImporter.cs @@ -8,7 +8,12 @@ using System.Linq; using UnityEditor; using UnityEditor.Build.Pipeline.Utilities; +#if UNITY_2020_2_OR_NEWER +using UnityEditor.AssetImporters; +#else using UnityEditor.Experimental.AssetImporters; +#endif + using UnityEngine; using Object = UnityEngine.Object; @@ -27,11 +32,10 @@ public override void OnImportAsset(AssetImportContext ctx) var (bun, bundleAssetsFile, assetBundleExtAsset) = am.LoadBundle(ctx.assetPath); - var bundleBaseField = assetBundleExtAsset.instance.GetBaseField(); - var dependencyArray = bundleBaseField.GetField("m_Dependencies/Array"); - var dependencies = dependencyArray.GetChildrenList().Select(dep => dep.GetValue().AsString()).ToArray(); - var container = bundleBaseField.GetField("m_Container/Array"); - var bundleName = bundleBaseField.GetValue("m_AssetBundleName").AsString(); + var bundleBaseField = assetBundleExtAsset.baseField; + var dependencyArray = bundleBaseField["m_Dependencies.Array"]; + var container = bundleBaseField["m_Container.Array"]; + var bundleName = bundleBaseField["m_AssetBundleName"].AsString; am.UnloadAll(); @@ -60,7 +64,7 @@ public override void OnImportAsset(AssetImportContext ctx) for (int i = 0; i < allAssets.Length; i++) { var asset = allAssets[i]; - if (asset.name == "FileMap") continue; + if (asset.name == "BundleKitFileMap") continue; if (asset is Shader shader) { ShaderUtil.RegisterShader(shader); diff --git a/Editor/Importers/SerializableMaterialDataImporter.cs b/Editor/Importers/SerializableMaterialDataImporter.cs index 88895de..d0daca3 100644 --- a/Editor/Importers/SerializableMaterialDataImporter.cs +++ b/Editor/Importers/SerializableMaterialDataImporter.cs @@ -2,7 +2,12 @@ using System.IO; using System.Linq; using UnityEditor; +#if UNITY_2020_2_OR_NEWER +using UnityEditor.AssetImporters; +#else using UnityEditor.Experimental.AssetImporters; +#endif + using UnityEngine; namespace BundleKit.Bundles diff --git a/Editor/PipelineJobs/CreateCatalogBundle.cs b/Editor/PipelineJobs/CreateCatalogBundle.cs index 6f319ce..36126d2 100644 --- a/Editor/PipelineJobs/CreateCatalogBundle.cs +++ b/Editor/PipelineJobs/CreateCatalogBundle.cs @@ -1,12 +1,11 @@ using AssetsTools.NET; using AssetsTools.NET.Extra; using BundleKit.Assets; +using BundleKit.Assets.Replacers; using BundleKit.Utility; -using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text.RegularExpressions; using System.Threading.Tasks; using ThunderKit.Common.Logging; using ThunderKit.Core.Data; @@ -17,17 +16,23 @@ namespace BundleKit.PipelineJobs { + /// + /// Creates an AssetBundle from assets in an existing Unity build. + /// Set the build to read from in the ThunderKit Game Project settings. + /// [PipelineSupport(typeof(Pipeline))] public class CreateCatalogBundle : PipelineJob { - public DefaultAsset templateBundle; + [Tooltip("File to write to. Path is relative to project directory.")] public string outputAssetBundlePath; + [Tooltip("Object classes to include in bundle")] public Filter[] filters; + private delegate void Log(string title = null, string message = null, float progress = -1, bool log = true, params string[] context); + public override Task Execute(Pipeline pipeline) { var am = new AssetsManager(); - var assetsReplacers = new List(); using (var progressBar = new ProgressBar("Constructing AssetBundle")) try @@ -40,182 +45,294 @@ void Log(string title = null, string message = null, float progress = -1, bool l progressBar.Update(message, title, progress); } - var templateBundlePath = AssetDatabase.GetAssetPath(templateBundle); - var settings = ThunderKitSetting.GetOrCreateSettings(); var gameName = Path.GetFileNameWithoutExtension(settings.GameExecutable); var dataDirectoryPath = Path.Combine(settings.GamePath, $"{gameName}_Data"); var classDataPath = Path.Combine("Packages", "com.passivepicasso.bundlekit", "Library", "classdata.tpk"); - var sharedAssetsFiles = Directory.EnumerateFiles(dataDirectoryPath, "sharedassets*.assets").ToArray(); - var levelFiles = Directory.EnumerateFiles(dataDirectoryPath, "level*").Where(file => Path.GetExtension(file) == string.Empty).ToArray(); - var resourcesFilePath = Path.Combine(dataDirectoryPath, "resources.assets"); - var ggmAssetsPath = Path.Combine(dataDirectoryPath, "globalgamemanagers.assets"); - var ggmPath = Path.Combine(dataDirectoryPath, "globalgamemanagers"); - - var targetFiles = Enumerable.Empty().Concat(sharedAssetsFiles).Prepend(resourcesFilePath).Prepend(ggmAssetsPath).ToArray(); + var sourceFiles = new string[] + { + Path.Combine(dataDirectoryPath, "globalgamemanagers"), + Path.Combine(dataDirectoryPath, "globalgamemanagers.assets"), + Path.Combine(dataDirectoryPath, "resources.assets"), + }.Concat(Directory.EnumerateFiles(dataDirectoryPath, "sharedassets*.assets")); + //var levelFiles = Directory.EnumerateFiles(dataDirectoryPath, "level*").Where(file => Path.GetExtension(file) == string.Empty).ToArray(); am.LoadClassPackage(classDataPath); am.LoadClassDatabaseFromPackage(Application.unityVersion); - - var (bun, bundleAssetsFile, assetBundleExtAsset) = am.LoadBundle(templateBundlePath); - - var bundleBaseField = assetBundleExtAsset.instance.GetBaseField(); - - var containerArray = bundleBaseField.GetField("m_Container/Array"); - var dependencyArray = bundleBaseField.GetField("m_Dependencies/Array"); - var preloadTableArray = bundleBaseField.GetField("m_PreloadTable/Array"); + foreach (var sourceAssetsFile in sourceFiles) + { + am.LoadAssetsFile(sourceAssetsFile, true); + } + var resourceManagerDb = new ResourceManagerDb(am); var bundleName = Path.GetFileNameWithoutExtension(outputAssetBundlePath); - bundleBaseField.Get("m_Name").GetValue().Set(bundleName); - bundleBaseField.Get("m_AssetBundleName").GetValue().Set(bundleName); - var preloadChildren = new List(); - var mContainerChildren = new List(); - var streamReaders = new Dictionary(); + AssetsToolsExtensions.CreateBundleAssetsFile(bundleName, am.ClassDatabase, out var bundleAssetsFile, out var bundleBaseField); + AssetFileInfo cabData = bundleAssetsFile.AssetInfos[0]; - bundleAssetsFile.file.dependencies.dependencies.Clear(); - bundleAssetsFile.file.dependencies.dependencyCount = 0; - bundleAssetsFile.dependencies.Clear(); + var containerArray = bundleBaseField["m_Container.Array"]; + var preloadTableArray = bundleBaseField["m_PreloadTable.Array"]; - var compiledFilters = filters.Select(f => (assetClass: f.assetClass, nameRegex: f.nameRegex.Select(reg => new Regex(reg)).ToArray())).ToArray(); - var treeEnumeration = targetFiles - .Select(p => am.LoadAssetsFile(p, false)) - .SelectMany(af => compiledFilters.SelectMany(filter => af.CollectAssetTrees(am, filter.nameRegex, filter.assetClass, Log))); + // Find objects selected by user filters + // These are our main objects to be show in the catalog browser. + Log($"Collecting objects matching filters"); + foreach (var sourceFile in sourceFiles) + { + am.LoadAssetsFile(sourceFile, true); + } + List localFiles = CollectRootAssets(am, filters, Log, resourceManagerDb).ToList(); - var felledTree = treeEnumeration.SelectMany(tree => tree.Flatten(true)); - var localGroups = felledTree.GroupBy(tree => tree).ToArray(); + // Resolve full dependency graphs for root objects and allocate fileId slots. var localIdMap = new Dictionary(); var fileMaps = new HashSet(); - var preloadIndex = 0; - Log($"Generating Tree Map"); - for (long i = 0; i < localGroups.Length; i++) + int rootObjCount = localFiles.Count; + for (int i = 0; i < rootObjCount; i++) { - var assetTree = localGroups[i].First(); - localIdMap[assetTree] = i + 2; - Log(message: $"{assetTree.name} = {i + 2}"); + var root = localFiles[i]; + int localId = i + 2; + localIdMap[root] = localId; + Log("Collecting dependencies", $"Root object: {root.GetBkCatalogName()} ({localId})", log: true, progress: i / (float)localFiles.Count); + // resolve dependency graph + if (root.Children is null) + { + localFiles[i] = root.sourceData.file.GetDependencies( + am, resourceManagerDb, 0, + root.sourceData.info.PathId, true); + } + fileMaps.Add(new MapRecord(localId, (root.sourceData.file.name, root.PathId))); } - Log($"Writing Assets"); - foreach (var group in localGroups) - { - var assetTree = group.First(); - var localId = localIdMap[group.Key]; - var asset = assetTree.assetExternal; - var baseField = assetTree.assetExternal.instance.GetBaseField(); - - Log(message: $"Remapping ({baseField.GetFieldType()}) {assetTree.name} PPts"); - var distinctChildren = assetTree.Children.Distinct().ToArray(); - - var fileMapElements = distinctChildren - .Select(child => new MapRecord(localIdMap[child], (child.assetExternal.file.name, child.PathId))) - .Prepend(new MapRecord(localIdMap[assetTree], (assetTree.assetExternal.file.name, assetTree.PathId))); - foreach (var map in fileMapElements) - fileMaps.Add(map); - - var remap = distinctChildren.ToDictionary(child => (child.FileId, child.PathId), child => (0, localIdMap[child])); - baseField.RemapPPtrs(remap); + // Allocate fileId slots for objects only included as required dependencies. + // These don't need names, and their dep graphs are a subset of root objects' dep graphs. + var depObjects = localFiles.SelectMany(a => a.Children) + .Where(c => !localFiles.Contains(c)) // a root object can depend on another root object. + .Distinct().ToList(); + for (int i = 0; i < depObjects.Count; i++) + { + var depObj = depObjects[i]; + int localId = rootObjCount + i + 2; + localIdMap[depObj] = localId; + Log("Collecting dependencies", $"dep object: {depObj.GetBkCatalogName()} ({localId})", log: true, progress: i / (float)depObjects.Count); + if (depObj.Children is null) + { + // The full dep graph will already be available, + // but we do need to know the immediate deps for remapping the PPtrs. + depObj = depObj.sourceData.file.GetDependencies( + am, resourceManagerDb, 0, + depObj.sourceData.info.PathId, false); + } + localFiles.Add(depObj); + } - var tableData = assetTree.Flatten(true).Distinct().ToArray(); - preloadIndex = preloadChildren.Count; + // create CAB entry for root objects only now that we know where deps are going to go. + for (int i = 0; i < rootObjCount; i++) + { + var root = localFiles[i]; + int localId = i + 2; + int preloadStart = preloadTableArray.Children.Count; + int preloadSize = 0; + var tableData = root.Children.Distinct().ToArray(); foreach (var data in tableData) { var entry = ValueBuilder.DefaultValueFieldFromArrayTemplate(preloadTableArray); - entry.SetValue("m_FileID", 0); - entry.SetValue("m_PathID", localIdMap[data]); - preloadChildren.Add(entry); + entry["m_FileID"].AsInt = 0; + entry["m_PathID"].AsLong = localIdMap[data]; + preloadTableArray.Children.Add(entry); + preloadSize++; } - switch (baseField.GetFieldType()) + + if (preloadSize == 0) { - case "Texture2D": - case "Cubemap": - TextureFile texFile = TextureFile.ReadTextureFile(baseField); - texFile.ImportTextureData(streamReaders, dataDirectoryPath); - texFile.WriteTextureFile(baseField); - break; + preloadStart = 0; } - - var assetBytes = asset.instance.WriteToByteArray(); - var currentAssetReplacer = new AssetsReplacerFromMemory(0, localId, (int)asset.info.curFileType, - AssetHelper.GetScriptIndex(asset.file.file, asset.info), - assetBytes); - assetsReplacers.Add(currentAssetReplacer); - mContainerChildren.Add(containerArray.CreateEntry(assetTree.name, 0, localId, preloadIndex, preloadChildren.Count - preloadIndex)); + containerArray.CreateEntry(root.GetBkCatalogName(), 0, localId, preloadStart, preloadSize); } - AddFileMap(am, assetsReplacers, containerArray, preloadTableArray, mContainerChildren, preloadChildren, preloadIndex, fileMaps); - - preloadTableArray.SetChildrenList(preloadChildren.ToArray()); - containerArray.SetChildrenList(mContainerChildren.ToArray()); - dependencyArray.SetChildrenList(Array.Empty()); + Log($"Rewriting Assets"); + int remapProgressBar = 0; + foreach (var localFile in localFiles) + { + var localFileId = localIdMap[localFile]; + IContentReplacer replacer; - var newAssetBundleBytes = bundleBaseField.WriteToByteArray(); - assetsReplacers.Insert(0, new AssetsReplacerFromMemory(0, assetBundleExtAsset.info.index, (int)assetBundleExtAsset.info.curFileType, 0xFFFF, newAssetBundleBytes)); + if (localFile.Children.Count > 0) + { + Log(message: $"Remapping {localFile.GetBkCatalogName()} PPts", progress: remapProgressBar / (float)localFiles.Count); + var remapedBaseField = CreateRemapedContent(localIdMap, localFile); + replacer = new DeferredBaseFieldSerializer(remapedBaseField); + } + else + { + Log(message: $"Direct copying {localFile.GetBkCatalogName()}", progress: remapProgressBar / (float)localFiles.Count); + // If an asset has no outgoing PPtrs and doesn't need to be modified, + // lift-and-shift from the source file. + var srcFile = localFile.sourceData.file; + var srcInfo = localFile.sourceData.info; + var dataOffset = srcFile.file.Header.DataOffset; + replacer = new ContentReplacerFromStream( + srcFile.AssetsStream, + dataOffset + srcInfo.ByteOffset, + (int)srcInfo.ByteSize); + } - foreach (var stream in streamReaders) - stream.Value.Dispose(); - streamReaders.Clear(); + // Append this to the intermediate assets file that will be inserted into the bundle + // Eventually we might want to use the source AssetTreeData here, + // especially for scripts and such that don't exist in the ClassDatabase. + var newAssetInfo = AssetFileInfo.Create( + bundleAssetsFile, + localFileId, + localFile.sourceData.info.TypeId, + am.ClassDatabase); + newAssetInfo.Replacer = replacer; + bundleAssetsFile.AssetInfos.Add(newAssetInfo); + + remapProgressBar++; + } - byte[] newAssetData; - using (var bundleStream = new MemoryStream()) - using (var writer = new AssetsFileWriter(bundleStream)) + var filemapInfo = AddFileMap(am, + bundleAssetsFile, + containerArray, + fileMaps); + bundleAssetsFile.AssetInfos.Add(filemapInfo); + + // The first DirectoryInfo in the bundle is actually an entire assets archive. + // Normally this is called something like CAB-XXXXXXX. + // So we build an entire assets file with the desired content, and then add it to the actual bundle file. + Log("Writing temporary assets file", progress: 0); + using (var tempAssetsFile = new FileStream(Path.GetTempFileName(), + FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, + 4096, FileOptions.RandomAccess | FileOptions.DeleteOnClose)) + using (var tempWriter = new AssetsFileWriter(tempAssetsFile)) { - bundleAssetsFile.file.Write(writer, 0, assetsReplacers, 0, am.classFile); - newAssetData = bundleStream.ToArray(); + bundleAssetsFile.Write(tempWriter); + // can't release tempWriter here, because the library will close the stream, which deletes the file. + + tempAssetsFile.Position = 0; + + // build the actual asset bundle file + Log($"Writing {outputAssetBundlePath}"); + using var fileStream = File.Open(outputAssetBundlePath, FileMode.Create); + using var writer = new AssetsFileWriter(fileStream); + var targetBundleFile = AssetsToolsExtensions.CreateEmptyAssetBundle(); + var dirInfo = AssetBundleDirectoryInfo.Create(bundleName, true); + var dirInfoContent = new ContentReplacerFromStream(tempAssetsFile); + dirInfo.Replacer = dirInfoContent; + targetBundleFile.BlockAndDirInfo.DirectoryInfos.Add(dirInfo); + + targetBundleFile.Write(writer); } - var bundles = new List - { - new BundleReplacerFromMemory(bundleAssetsFile.name, bundleName, true, newAssetData, -1) - }; - using (var file = File.OpenWrite(outputAssetBundlePath)) - using (var writer = new AssetsFileWriter(file)) - bun.file.Write(writer, bundles); - - preloadChildren.Clear(); - mContainerChildren.Clear(); - bundles.Clear(); localIdMap.Clear(); fileMaps.Clear(); - bundles = null; - } finally { - assetsReplacers.Clear(); am.UnloadAll(true); } return Task.CompletedTask; } - private static void AddFileMap(AssetsManager am, List assetsReplacers, AssetTypeValueField containerArray, AssetTypeValueField preloadTableArray, List mContainerChildren, List preloadChildren, int preloadIndex, HashSet fileMaps) + AssetTypeValueField CreateRemapedContent(Dictionary localIdMap, AssetTree assetTree) + { + var baseField = assetTree.sourceData.baseField; + + var distinctChildren = assetTree.Children.Distinct().ToArray(); + + var fileMapElements = distinctChildren + .Select(child => new MapRecord(localIdMap[child], (child.sourceData.file.name, child.PathId))) + .Prepend(new MapRecord(localIdMap[assetTree], (assetTree.sourceData.file.name, assetTree.PathId))); + + var remap = distinctChildren.ToDictionary(child => (child.FileId, child.PathId), child => (0, localIdMap[child])); + // Some assets can point to themselves. + remap.Add((0, assetTree.PathId), (0, localIdMap[assetTree])); + baseField.RemapPPtrs(remap); + + return baseField; + } + + private static AssetFileInfo AddFileMap( + AssetsManager am, + AssetsFile bundleAssetsFile, + AssetTypeValueField containerArray, + HashSet fileMaps) { - const string assetName = "FileMap"; + const string assetName = "BundleKitFileMap"; var templateField = new AssetTypeTemplateField(); - var cldbType = AssetHelper.FindAssetClassByID(am.classFile, (int)AssetClassID.TextAsset); - templateField.FromClassDatabase(am.classFile, cldbType, 0); + var cldbType = am.ClassDatabase.FindAssetClassByID((int)AssetClassID.TextAsset); + templateField.FromClassDatabase(am.ClassDatabase, cldbType); var textAssetBaseField = ValueBuilder.DefaultValueFieldFromTemplate(templateField); var fileMap = new FileMap { Maps = fileMaps.ToArray() }; - var mapJson = EditorJsonUtility.ToJson(fileMap, false); + var mapJson = EditorJsonUtility.ToJson(fileMap, true); - textAssetBaseField.SetValue("m_Name", assetName); - textAssetBaseField.SetValue("m_Script", mapJson); + textAssetBaseField["m_Name"].AsString = assetName; + textAssetBaseField["m_Script"].AsString = mapJson; - int pathId = assetsReplacers.Count + 2; - assetsReplacers.Add(new AssetsReplacerFromMemory(0, pathId, cldbType.classId, 0xffff, textAssetBaseField.WriteToByteArray())); - - var entry = ValueBuilder.DefaultValueFieldFromArrayTemplate(preloadTableArray); - entry.SetValue("m_FileID", 0); - entry.SetValue("m_PathID", pathId); - preloadChildren.Add(entry); + var pathId = bundleAssetsFile.AssetInfos.Count + 2; + AssetFileInfo assetFileInfo = AssetFileInfo.Create(bundleAssetsFile, pathId, (int)AssetClassID.TextAsset, am.ClassDatabase); // Use m_Container to construct an blank element for it - var pair = containerArray.CreateEntry($"assets/{assetName}.json".ToLowerInvariant(), 0, pathId, preloadIndex, preloadChildren.Count - preloadIndex); - mContainerChildren.Add(pair); + containerArray.CreateEntry(assetName.ToLowerInvariant(), 0, pathId); + + assetFileInfo.Replacer = new DeferredBaseFieldSerializer(textAssetBaseField); + + return assetFileInfo; + } + + private static IEnumerable CollectRootAssets( + AssetsManager am, + Filter[] filters, + UpdateLog Update, + ResourceManagerDb resourceManagerDb) + { + foreach (var assetsFileInst in am.Files) + { + Update(message: assetsFileInst.name, log: false); + + foreach (AssetFileInfo assetFileInfo in assetsFileInst.file.AssetInfos) + { + if (!filters.MatchesAnyClass(assetFileInfo)) + { + continue; + } + + var external = am.GetExtAsset(assetsFileInst, 0, assetFileInfo.PathId); + resourceManagerDb.TryGetName(assetsFileInst.name, assetFileInfo.PathId, out var rmName); + string name = external.GetName(am); + + + if (!(filters.AnyMatch(assetFileInfo, rmName) || filters.AnyMatch(assetFileInfo, name))) + { + continue; + } + + // we know we want the asset as a root asset at this point + + bool canHaveDeps = ((AssetClassID)assetFileInfo.TypeId).CanHaveDependencies(); + + // dispose the baseAsset if we're not going to need it for anything other than the name. + if (!canHaveDeps) + { + external.baseField = null; + } + + yield return new AssetTree() + { + name = name, + resourceManagerName = rmName, + sourceData = external, + FileId = 0, + PathId = assetFileInfo.PathId, + Children = canHaveDeps + ? null // deps are unknown at this point + : new(), // we know there are no deps + }; + } + } } } } diff --git a/Editor/PipelineJobs/DestroyBundlePreloadTableJob.cs b/Editor/PipelineJobs/DestroyBundlePreloadTableJob.cs deleted file mode 100644 index 4a9df41..0000000 --- a/Editor/PipelineJobs/DestroyBundlePreloadTableJob.cs +++ /dev/null @@ -1,68 +0,0 @@ -using AssetsTools.NET; -using AssetsTools.NET.Extra; -using BundleKit.Bundles; -using BundleKit.Utility; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using ThunderKit.Core.Pipelines; -using UnityEditor; - -namespace BundleKit.PipelineJobs -{ - [PipelineSupport(typeof(Pipeline))] - public class DestroyBundlePreloadTableJob : PipelineJob - { - public DefaultAsset bundle; - public string outputAssetBundlePath; - - public override Task Execute(Pipeline pipeline) - { - var am = new AssetsManager(); - var path = AssetDatabase.GetAssetPath(bundle); - - var (bun, bundleAssetsFile, assetBundleExtAsset) = am.LoadBundle(path); - - var classDataPath = Path.Combine("Packages", "com.passivepicasso.bundlekit", "Library", "classdata.tpk"); - am.LoadClassPackage(classDataPath); - - var assetsReplacers = new List(); - var bundleReplacers = new List(); - var referenceContext = "Removed Assets\r\n"; - var bundleBaseField = assetBundleExtAsset.instance.GetBaseField(); - var preloadTableArray = bundleBaseField.GetField("m_PreloadTable/Array"); - preloadTableArray.SetChildrenList(Array.Empty()); - - var containerChildren = bundleBaseField.GetField("m_Container/Array").GetChildrenList(); - foreach (var child in containerChildren) - { - child.SetValue("second/preloadIndex", 0); - child.SetValue("second/preloadSize", 0); - } - - var newAssetBundleBytes = bundleBaseField.WriteToByteArray(); - assetsReplacers.Add(new AssetsReplacerFromMemory(0, assetBundleExtAsset.info.index, (int)assetBundleExtAsset.info.curFileType, 0xFFFF, newAssetBundleBytes)); - - pipeline.Log(LogLevel.Information, "Removing bundle assets", referenceContext); - - byte[] newAssetData; - using (var bundleStream = new MemoryStream()) - using (var writer = new AssetsFileWriter(bundleStream)) - { - bundleAssetsFile.file.Write(writer, 0, assetsReplacers, 0); - newAssetData = bundleStream.ToArray(); - } - var bundleReplacer = new BundleReplacerFromMemory(bundleAssetsFile.name, bundleAssetsFile.name, true, newAssetData, -1); - bundleReplacers.Add(bundleReplacer); - - using (var file = File.OpenWrite(outputAssetBundlePath)) - using (var writer = new AssetsFileWriter(file)) - bun.file.Write(writer, bundleReplacers); - - pipeline.Log(LogLevel.Information, "Removed bundle assets", referenceContext); - - return Task.CompletedTask; - } - } -} diff --git a/Editor/PipelineJobs/RemoveAllBundleAssets.cs b/Editor/PipelineJobs/RemoveAllBundleAssets.cs deleted file mode 100644 index 0d34213..0000000 --- a/Editor/PipelineJobs/RemoveAllBundleAssets.cs +++ /dev/null @@ -1,70 +0,0 @@ -using AssetsTools.NET; -using AssetsTools.NET.Extra; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using ThunderKit.Core.Pipelines; -using UnityEditor; - -namespace BundleKit.PipelineJobs -{ - [PipelineSupport(typeof(Pipeline))] - public class RemoveAllBundleAssets : PipelineJob - { - public DefaultAsset bundle; - public string outputAssetBundlePath; - - public override Task Execute(Pipeline pipeline) - { - var am = new AssetsManager(); - var path = AssetDatabase.GetAssetPath(bundle); - - var fileStream = File.OpenRead(path); - var bun = am.LoadBundleFile(fileStream, true); - var bundleAssetsFile = am.LoadAssetsFileFromBundle(bun, 0); - var resSFile = bun.file.bundleInf6.dirInf.FirstOrDefault(dir => dir.name.Contains("resS")); - - am.LoadClassPackage("classdata.tpk"); - var assetsReplacers = new List(); - var bundleReplacers = new List(); - var referenceContext = "Removed Assets\r\n"; - - foreach (var assetFileInfo in bundleAssetsFile.table.assetFileInfo)// m_Container.children - { - if (!assetFileInfo.ReadName(bundleAssetsFile.file, out var name)) continue; - if ((AssetClassID)assetFileInfo.curFileType == AssetClassID.AssetBundle) continue; - - long pathId = assetFileInfo.index; - - var type = (AssetClassID)assetFileInfo.curFileType; - referenceContext += $"1. ({type}) \"{name}\" {{FileID: 0, PathID: {pathId} }}\r\n"; - - var remover = new AssetsRemover(0, pathId, (int)type); - assetsReplacers.Add(remover); - } - - pipeline.Log(LogLevel.Information, "Removing bundle assets", referenceContext); - - byte[] newAssetData; - using (var bundleStream = new MemoryStream()) - using (var writer = new AssetsFileWriter(bundleStream)) - { - bundleAssetsFile.file.Write(writer, 0, assetsReplacers, 0); - newAssetData = bundleStream.ToArray(); - } - var resSRemover = new BundleRemover(resSFile.name, true); - bundleReplacers.Add(resSRemover); - var bundleReplacer = new BundleReplacerFromMemory(bundleAssetsFile.name, bundleAssetsFile.name, true, newAssetData, -1); - bundleReplacers.Add(bundleReplacer); - - using (var file = File.OpenWrite(outputAssetBundlePath)) - using (var writer = new AssetsFileWriter(file)) - bun.file.Write(writer, bundleReplacers); - - pipeline.Log(LogLevel.Information, "Removed bundle assets", referenceContext); - - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/Editor/Utility/AssetsToolsExtensions.cs b/Editor/Utility/AssetsToolsExtensions.cs index 3ef0b51..f86a0fb 100644 --- a/Editor/Utility/AssetsToolsExtensions.cs +++ b/Editor/Utility/AssetsToolsExtensions.cs @@ -1,10 +1,10 @@ using AssetsTools.NET; using AssetsTools.NET.Extra; -using System; +using BundleKit.Assets; +using BundleKit.Assets.Replacers; using System.Collections.Generic; -using System.IO; using System.Linq; -using UnityEditor.Build.Pipeline.Utilities; +using UnityEngine; namespace BundleKit.Utility { @@ -17,12 +17,16 @@ public static (BundleFileInstance bun, AssetsFileInstance bundleAssetsFile, Asse var bundleAssetsFile = am.LoadAssetsFileFromBundle(bun, 0); //Load AssetBundle asset from Bundle AssetsFile so that we can update its data later - var assetBundleAsset = bundleAssetsFile.table.GetAssetsOfType((int)AssetClassID.AssetBundle)[0]; - var assetBundleExtAsset = am.GetExtAsset(bundleAssetsFile, 0, assetBundleAsset.index); + var assetBundleAsset = bundleAssetsFile.file.GetAssetsOfType((int)AssetClassID.AssetBundle)[0]; + var assetBundleExtAsset = am.GetExtAsset(bundleAssetsFile, 0, assetBundleAsset.PathId); return (bun, bundleAssetsFile, assetBundleExtAsset); } + // Obsoleted by changes in copy process? + // the source files may have dependencies, but we only need to + // ensure the destination doesn't depend on the source. AssetsReplacer + /* public static void AddDependency(this AssetsFileInstance assetsFileInst, AssetsFileDependency assetsFileDependency) { var dependencies = assetsFileInst.file.dependencies.dependencies; @@ -76,51 +80,60 @@ public static void AddDependency(this AssetsFileInstance assetsFileInst, string dependencies.dependencyCount = dependencies.dependencies.Count; } + */ + + // Oboleted by changes in bundle copy process? + // texFile.WriteTo() + /* public static void WriteTextureFile(this TextureFile textureFile, AssetTypeValueField baseField) { - if (!baseField.GetField("m_Name").IsDummy()) baseField.SetValue("m_Name", textureFile.m_Name); - if (!baseField.GetField("m_ForcedFallbackFormat").IsDummy()) baseField.SetValue("m_ForcedFallbackFormat", textureFile.m_ForcedFallbackFormat); - if (!baseField.GetField("m_DownscaleFallback").IsDummy()) baseField.SetValue("m_DownscaleFallback", textureFile.m_DownscaleFallback); - if (!baseField.GetField("m_DownscaleFallback").IsDummy()) baseField.SetValue("m_DownscaleFallback", textureFile.m_DownscaleFallback); - if (!baseField.GetField("m_Width").IsDummy()) baseField.SetValue("m_Width", textureFile.m_Width); - if (!baseField.GetField("m_Height").IsDummy()) baseField.SetValue("m_Height", textureFile.m_Height); - if (!baseField.GetField("m_TextureFormat").IsDummy()) baseField.SetValue("m_TextureFormat", textureFile.m_TextureFormat); - if (!baseField.GetField("m_MipCount").IsDummy()) baseField.SetValue("m_MipCount", textureFile.m_MipCount); - if (!baseField.GetField("m_MipMap").IsDummy()) if (!baseField.Get("m_MipMap").IsDummy()) baseField.SetValue("m_MipMap", textureFile.m_MipMap); - if (!baseField.GetField("m_IsReadable").IsDummy()) baseField.SetValue("m_IsReadable", textureFile.m_IsReadable); - if (!baseField.GetField("m_ReadAllowed").IsDummy()) baseField.SetValue("m_ReadAllowed", textureFile.m_ReadAllowed); - if (!baseField.GetField("m_StreamingMipmaps").IsDummy()) baseField.SetValue("m_StreamingMipmaps", textureFile.m_StreamingMipmaps); - if (!baseField.GetField("m_StreamingMipmapsPriority").IsDummy()) baseField.SetValue("m_StreamingMipmapsPriority", textureFile.m_StreamingMipmapsPriority); - if (!baseField.GetField("m_ImageCount").IsDummy()) baseField.SetValue("m_ImageCount", textureFile.m_ImageCount); - if (!baseField.GetField("m_TextureDimension").IsDummy()) baseField.SetValue("m_TextureDimension", textureFile.m_TextureDimension); - if (!baseField.GetField("m_TextureSettings/m_FilterMode").IsDummy()) baseField.SetValue("m_TextureSettings/m_FilterMode", textureFile.m_TextureSettings.m_FilterMode); - if (!baseField.GetField("m_TextureSettings/m_Aniso").IsDummy()) baseField.SetValue("m_TextureSettings/m_Aniso", textureFile.m_TextureSettings.m_Aniso); - if (!baseField.GetField("m_TextureSettings/m_MipBias").IsDummy()) baseField.SetValue("m_TextureSettings/m_MipBias", textureFile.m_TextureSettings.m_MipBias); - if (!baseField.GetField("m_TextureSettings/m_WrapMode").IsDummy()) baseField.SetValue("m_TextureSettings/m_WrapMode", textureFile.m_TextureSettings.m_WrapMode); - if (!baseField.GetField("m_TextureSettings/m_WrapU").IsDummy()) baseField.SetValue("m_TextureSettings/m_WrapU", textureFile.m_TextureSettings.m_WrapU); - if (!baseField.GetField("m_TextureSettings/m_WrapV").IsDummy()) baseField.SetValue("m_TextureSettings/m_WrapV", textureFile.m_TextureSettings.m_WrapV); - if (!baseField.GetField("m_TextureSettings/m_WrapW").IsDummy()) baseField.SetValue("m_TextureSettings/m_WrapW", textureFile.m_TextureSettings.m_WrapW); - - if (!baseField.GetField("m_LightmapFormat").IsDummy()) baseField.SetValue("m_LightmapFormat", textureFile.m_LightmapFormat); - if (!baseField.GetField("m_ColorSpace").IsDummy()) baseField.SetValue("m_ColorSpace", textureFile.m_ColorSpace); - - var image_data = baseField.GetField("image data"); - image_data.GetValue().type = EnumValueTypes.ByteArray; - image_data.templateField.valueType = EnumValueTypes.ByteArray; - var byteArray = new AssetTypeByteArray() - { - size = (uint)textureFile.pictureData.Length, - data = textureFile.pictureData - }; - image_data.GetValue().Set(byteArray); - if (!baseField.GetField("m_CompleteImageSize").IsDummy()) baseField.SetValue("m_CompleteImageSize", textureFile.pictureData.Length); + if (!baseField["m_Name"].IsDummy) baseField["m_Name"].AsString = textureFile.m_Name; + if (!baseField["m_ForcedFallbackFormat"].IsDummy) baseField["m_ForcedFallbackFormat"].AsInt = textureFile.m_ForcedFallbackFormat; + if (!baseField["m_DownscaleFallback"].IsDummy) baseField["m_DownscaleFallback"].AsBool = textureFile.m_DownscaleFallback; + if (!baseField["m_DownscaleFallback"].IsDummy) baseField["m_DownscaleFallback"].AsBool = textureFile.m_DownscaleFallback; + if (!baseField["m_Width"].IsDummy) baseField["m_Width"].AsInt = textureFile.m_Width; + if (!baseField["m_Height"].IsDummy) baseField["m_Height"].AsInt = textureFile.m_Height; + if (!baseField["m_TextureFormat"].IsDummy) baseField["m_TextureFormat"].AsInt = textureFile.m_TextureFormat; + if (!baseField["m_MipCount"].IsDummy) baseField["m_MipCount"].AsInt = textureFile.m_MipCount; + if (!baseField["m_MipMap"].IsDummy) if (!baseField["m_MipMap"].IsDummy) baseField["m_MipMap"].AsBool = textureFile.m_MipMap; + if (!baseField["m_IsReadable"].IsDummy) baseField["m_IsReadable"].AsBool = textureFile.m_IsReadable; + if (!baseField["m_ReadAllowed"].IsDummy) baseField["m_ReadAllowed"].AsBool = textureFile.m_ReadAllowed; + if (!baseField["m_StreamingMipmaps"].IsDummy) baseField["m_StreamingMipmaps"].AsBool = textureFile.m_StreamingMipmaps; + if (!baseField["m_StreamingMipmapsPriority"].IsDummy) baseField["m_StreamingMipmapsPriority"].AsInt = textureFile.m_StreamingMipmapsPriority; + if (!baseField["m_ImageCount"].IsDummy) baseField["m_ImageCount"].AsInt = textureFile.m_ImageCount; + if (!baseField["m_TextureDimension"].IsDummy) baseField["m_TextureDimension"].AsInt = textureFile.m_TextureDimension; + if (!baseField["m_TextureSettings/m_FilterMode"].IsDummy) baseField["m_TextureSettings/m_FilterMode"].AsInt = textureFile.m_TextureSettings.m_FilterMode; + if (!baseField["m_TextureSettings/m_Aniso"].IsDummy) baseField["m_TextureSettings/m_Aniso"].AsInt = textureFile.m_TextureSettings.m_Aniso; + if (!baseField["m_TextureSettings/m_MipBias"].IsDummy) baseField["m_TextureSettings/m_MipBias"].AsFloat = textureFile.m_TextureSettings.m_MipBias; + if (!baseField["m_TextureSettings/m_WrapMode"].IsDummy) baseField["m_TextureSettings/m_WrapMode"].AsInt = textureFile.m_TextureSettings.m_WrapMode; + if (!baseField["m_TextureSettings/m_WrapU"].IsDummy) baseField["m_TextureSettings/m_WrapU"].AsInt = textureFile.m_TextureSettings.m_WrapU; + if (!baseField["m_TextureSettings/m_WrapV"].IsDummy) baseField["m_TextureSettings/m_WrapV"].AsInt = textureFile.m_TextureSettings.m_WrapV; + if (!baseField["m_TextureSettings/m_WrapW"].IsDummy) baseField["m_TextureSettings/m_WrapW"].AsInt = textureFile.m_TextureSettings.m_WrapW; + if (!baseField["m_LightmapFormat"].IsDummy) baseField["m_LightmapFormat"].AsInt = textureFile.m_LightmapFormat; + if (!baseField["m_ColorSpace"].IsDummy) baseField["m_ColorSpace"].AsInt = textureFile.m_ColorSpace; - if (!baseField.GetField("m_StreamData/offset").IsDummy()) baseField.SetValue("m_StreamData/offset", textureFile.m_StreamData.offset); - if (!baseField.GetField("m_StreamData/size").IsDummy()) baseField.SetValue("m_StreamData/size", textureFile.m_StreamData.size); - if (!baseField.GetField("m_StreamData/path").IsDummy()) baseField.SetValue("m_StreamData/path", textureFile.m_StreamData.path); + var image_data = baseField["image data"]; + //image_data.GetValue().type = AssetValueType.ByteArray; + //image_data.TemplateField.valueType = AssetValueType.ByteArray; + //var byteArray = new AssetTypeByteArray() + //{ + // size = (uint)textureFile.pictureData.Length, + // data = textureFile.pictureData + //}; + //image_data.Value = byteArray; + image_data.Value.ValueType = AssetValueType.ByteArray; + image_data.AsByteArray = textureFile.pictureData; + + if (!baseField["m_CompleteImageSize"].IsDummy) baseField["m_CompleteImageSize"].AsInt = textureFile.pictureData.Length; + if (!baseField["m_StreamData/offset"].IsDummy) baseField["m_StreamData/offset"].AsULong = textureFile.m_StreamData.offset; + if (!baseField["m_StreamData/size"].IsDummy) baseField["m_StreamData/size"].AsUInt = textureFile.m_StreamData.size; + if (!baseField["m_StreamData/path"].IsDummy) baseField["m_StreamData/path"].AsString = textureFile.m_StreamData.path; } + */ + // No references. Still needed? + /* public static IEnumerable FindFieldType(this AssetTypeValueField valueField, Predicate typeMatch) { var fieldStack = new Stack(); @@ -129,14 +142,14 @@ public static IEnumerable FindFieldType(this AssetTypeValue while (fieldStack.Any()) { field = fieldStack.Pop(); - if (field.childrenCount > 0) + if (field.ChildrenCount > 0) { - string typeName = field.templateField.type; + string typeName = field.TemplateField.type; if (typeMatch(typeName)) { yield return field; } - foreach (var child in field.children) + foreach (var child in field.Children) fieldStack.Push(child); } } @@ -149,32 +162,19 @@ public static IEnumerable FindField(this AssetTypeValueFiel while (fieldStack.Any()) { field = fieldStack.Pop(); - if (field.childrenCount > 0) + if (field.ChildrenCount > 0) { var targetField = field.Get(fieldPath); - if (targetField.childrenCount > -1) + if (targetField.ChildrenCount > -1) { yield return targetField; } - foreach (var child in field.children) + foreach (var child in field.Children) fieldStack.Push(child); } } } - - public static AssetTypeValueField Get(this AssetTypeValueField valueField, params string[] fieldPath) - { - var field = valueField; - foreach (var pathField in fieldPath) - field = field.Get(pathField); - return field; - } - - public static AssetTypeValueField GetField(this AssetTypeValueField valueField, string fieldPath) => valueField.Get(fieldPath.Split('/')); - - public static AssetTypeValue GetValue(this AssetTypeValueField valueField, string fieldName) => valueField.Get(fieldName.Split('/')).GetValue(); - - public static void SetValue(this AssetTypeValueField valueField, string fieldName, object value) => valueField.GetField(fieldName).GetValue().Set(value); + */ public static void RemapPPtrs(this AssetTypeValueField field, IDictionary<(int fileId, long pathId), (int fileId, long pathId)> map) { @@ -183,47 +183,199 @@ public static void RemapPPtrs(this AssetTypeValueField field, IDictionary<(int f while (fieldStack.Any()) { var current = fieldStack.Pop(); - foreach (AssetTypeValueField child in current.children) + foreach (AssetTypeValueField child in current.Children) { - //not a value (ie not an int) - if (!child.templateField.hasValue) + if (!child.TryRemapPPtr(map)) { - //not array of values either - if (child.templateField.isArray && child.templateField.children[1].valueType != EnumValueTypes.ValueType_None) - continue; - - string typeName = child.templateField.type; - //is a pptr - if (typeName.StartsWith("PPtr<") && typeName.EndsWith(">")) + //recurse through dependencies + fieldStack.Push(child); + } + // is PPtr array, eg m_Dependencies + else if (child.TemplateField.IsArray) + { + if (child.TemplateField.Children[1].Type.StartsWith("PPtr<")) { - var fileIdField = child.Get("m_FileID").GetValue(); - var pathIdField = child.Get("m_PathID").GetValue(); - var pathId = pathIdField.AsInt64(); - var fileId = fileIdField.AsInt(); - if (!map.ContainsKey((fileId, pathId))) continue; - - var newPPtr = map[(fileId, pathId)]; - fileIdField.Set(newPPtr.fileId); - pathIdField.Set(newPPtr.pathId); + foreach (var pPtr in child.Children) + { + pPtr.TryRemapPPtr(map); + } } - //recurse through dependencies + else + { + // may be struct array that contains a PPtr + fieldStack.Push(child); + } + } + else + { fieldStack.Push(child); } } } - } - public static AssetTypeValueField CreateEntry(this AssetTypeValueField containerArray, string name, int fileId, long pathId, int preloadIndex = 0, int preloadSize = 0) + public static void CreateEntry(this AssetTypeValueField containerArray, string name, int fileId, long pathId, int preloadIndex = 0, int preloadSize = 0) { var pair = ValueBuilder.DefaultValueFieldFromArrayTemplate(containerArray); - pair.SetValue("first", name); - pair.SetValue("second/preloadIndex", preloadIndex); - pair.SetValue("second/preloadSize", preloadSize); - pair.SetValue("second/asset/m_FileID", fileId); - pair.SetValue("second/asset/m_PathID", pathId); - return pair; + pair["first"].AsString = name; + pair["second"]["preloadIndex"].AsInt = preloadIndex; + pair["second"]["preloadSize"].AsInt = preloadSize; + pair["second"]["asset"]["m_FileID"].AsInt = fileId; + pair["second"]["asset"]["m_PathID"].AsLong = pathId; + containerArray.Children.Add(pair); } - } + /// + /// Create an AssetBundleFile from scratch and initialize for the current unity version. + /// + /// Any assets the bundle shoud contain would be in an AssetsFile structure + /// wrapped in a AssetBundleDirectoryInfo (with a ContentReplacer) and added to + /// BlockAndDirInfo.DirectoryInfos. + /// + /// A minimally initialized AssetBundleFile + public static AssetBundleFile CreateEmptyAssetBundle() + { + // Most of this isn't used outside of bundle reading/writing a bundle + // So we need to set our write intent here. + AssetBundleFile file = new() + { + Header = new AssetBundleHeader() + { + EngineVersion = Application.unityVersion, + Signature = "UnityFS", + GenerationVersion = "5.x.x", + Version = 8, // 6, 7, or 8 + FileStreamHeader = new() + { + // Sizes are calculated at write time. + Flags = AssetBundleFSHeaderFlags.HasDirectoryInfo + }, + }, + BlockAndDirInfo = new AssetBundleBlockAndDirInfo() + { + // needed to put assets into a directory + DirectoryInfos = new(), + BlockInfos = new AssetBundleBlockInfo[] + { + new() + { + Flags = 0x40, // don't stream + }, + }, + }, + }; + + return file; + } + + /// + /// Create from scratch an AssetsFile suitable for use in an AssetBundle. + /// + /// Name of the CAB object. (Type AssetClassID.AssetBundle = 142) + /// Type database to initialize cabBaseField + /// A (mostly empty) AssetsFile. + /// + public static void CreateBundleAssetsFile(string cabName, ClassDatabaseFile cldb, out AssetsFile assetsFile, out AssetTypeValueField cabBaseField) + { + assetsFile = new AssetsFile() + { + Header = new() + { + Version = 22, // 2020.x and up + }, + Metadata = new() + { + TypeTreeEnabled = true, + UnityVersion = Application.unityVersion, + RefTypes = new(), + TypeTreeTypes = new(), + ScriptTypes = new(), + AssetInfos = new List(), + Externals = new(), + UserInformation = "BundleKit generated bundle", + }, + }; + + // setup empty CAB as first asset. (pathId 1) + AssetTypeTemplateField templateField = new() + { + Children = new(), + }; + + var cldbType = cldb.FindAssetClassByID((int)AssetClassID.AssetBundle); + templateField.FromClassDatabase(cldb, cldbType); + cabBaseField = ValueBuilder.DefaultValueFieldFromTemplate(templateField); + + // The Unity editor will cowardly refuse to load the bundle if this is not set. + cabBaseField["m_RuntimeCompatibility"].AsUInt = 1; + cabBaseField["m_Name"].AsString = cabName; + cabBaseField["m_AssetBundleName"].AsString = cabName; + + var cabDataInfo = AssetFileInfo.Create(assetsFile, 1, (int)AssetClassID.AssetBundle, cldb); + cabDataInfo.Replacer = new DeferredBaseFieldSerializer(cabBaseField); + assetsFile.AssetInfos.Add(cabDataInfo); + } + + /// + /// Try to parse the provided AssetTypeValueField as a PPtr. + /// + /// Record that might be a PPtr + /// The AssetsManager. + /// The file containing this record. + /// The file pointed to if this is a valid and reachable PPtr. + /// True if the value field is a valid PPtr record that points to a file accessible by am. + public static bool TryParsePPtr(this AssetTypeValueField pPtr, AssetsManager am, AssetsFileInstance relativeTo, out AssetTree node) + { + var typeName = pPtr.TemplateField.Type; + if (!(typeName.StartsWith("PPtr<") && typeName.EndsWith(">"))) + { + node = default; + return false; + } + + var pathIdRef = pPtr["m_PathID"].AsLong; + if (pathIdRef == 0) + { + node = default; + return false; + } + + var fileIdRef = pPtr["m_FileID"].AsInt; + var ext = am.GetExtAsset(relativeTo, fileIdRef, pathIdRef); + + //we don't want to process monobehaviours as thats a project in itself + if (ext.info.TypeId == (int)AssetClassID.MonoBehaviour) + { + node = default; + return false; + } + + node = new AssetTree + { + name = ext.GetName(am).ToLower(), + sourceData = ext, + FileId = fileIdRef, + PathId = pathIdRef, + }; + + return true; + } + + public static bool TryRemapPPtr(this AssetTypeValueField pPtr, IDictionary<(int fileId, long pathId), (int fileId, long pathId)> map) + { + string typeName = pPtr.TemplateField.Type; + if (!(typeName.StartsWith("PPtr<") && typeName.EndsWith(">"))) return false; + + var fileIdField = pPtr["m_FileID"]; + var pathIdField = pPtr["m_PathID"]; + var pathId = pathIdField.AsLong; + var fileId = fileIdField.AsInt; + if (!map.ContainsKey((fileId, pathId))) return false; + + var newPPtr = map[(fileId, pathId)]; + fileIdField.AsInt = newPPtr.fileId; + pathIdField.AsLong = newPPtr.pathId; + return true; + } + } } diff --git a/Editor/Utility/Extensions.cs b/Editor/Utility/Extensions.cs index 3a2c302..0653611 100644 --- a/Editor/Utility/Extensions.cs +++ b/Editor/Utility/Extensions.cs @@ -1,5 +1,6 @@ using AssetsTools.NET; using AssetsTools.NET.Extra; +using AssetsTools.NET.Texture; using BundleKit.Assets; using System; using System.Collections.Generic; @@ -33,41 +34,20 @@ public static UnityEngine.BuildCompression AsBuildCompression(this Compression c } public static bool IsNullOrEmpty(this ICollection collection) => collection == null || collection.Count == 0; - - public static IEnumerable CollectAssetTrees(this AssetsFileInstance assetsFileInst, AssetsManager am, Regex[] nameRegex, AssetClassID assetClass, UpdateLog Update) - { - // Iterate over all requested Class types and collect the data required to copy over the required asset information - // This step will recurse over dependencies so all required assets will become available from the resulting bundle - Update("Collecting Asset Trees", log: false); - var fileInfos = assetsFileInst.table.GetAssetsOfType((int)assetClass); - for (var x = 0; x < fileInfos.Count; x++) - { - var assetFileInfo = fileInfos[x]; - - var name = AssetHelper.GetAssetNameFast(assetsFileInst.file, am.classFile, assetFileInfo); - // If a name Regex filter is applied, and it does not match, continue - int i = 0; - for (; i < nameRegex.Length; i++) - if (nameRegex[i] != null && nameRegex[i].IsMatch(name)) - break; - if (nameRegex.Length != 0 && i == nameRegex.Length) continue; - - var tree = assetsFileInst.GetHierarchy(am, 0, assetFileInfo.index); - Update("Collecting Asset Trees", $"({assetClass}) {tree.name}", log: true); - - yield return tree; - } - } - public static AssetTree GetHierarchy(this AssetsFileInstance inst, AssetsManager am, int fileId, long pathId) + public static AssetTree GetDependencies( + this AssetsFileInstance inst, AssetsManager am, ResourceManagerDb resourceManagerDb, + int fileId, long pathId, bool recurseFiles) { var fieldStack = new Stack<(AssetsFileInstance file, AssetTypeValueField field, AssetTree node)>(); var baseAsset = am.GetExtAsset(inst, fileId, pathId); - var baseField = baseAsset.instance.GetBaseField(); + var baseField = baseAsset.baseField; + resourceManagerDb.TryGetName(inst.name, pathId, out var rmName); var root = new AssetTree { name = baseAsset.GetName(am), - assetExternal = baseAsset, + resourceManagerName = rmName, + sourceData = baseAsset, FileId = fileId, PathId = pathId, Children = new List() @@ -79,46 +59,46 @@ public static AssetTree GetHierarchy(this AssetsFileInstance inst, AssetsManager while (fieldStack.Any()) { var current = fieldStack.Pop(); - foreach (var child in current.field.children) + foreach (var child in current.field.Children) { - //not a value (ie not an int) - if (!child.templateField.hasValue) + //is a pptr + if (!child.TemplateField.IsArray && child.TryParsePPtr(am, current.file, out var pPtrDest)) { - //not array of values either - if (child.templateField.isArray && child.templateField.children[1].valueType != EnumValueTypes.ValueType_None) - continue; - - string typeName = child.templateField.type; - //is a pptr - if (typeName.StartsWith("PPtr<") && typeName.EndsWith(">")) + if (!root.Children.Contains(pPtrDest) && root != pPtrDest) { - var pathIdRef = child.Get("m_PathID").GetValue().AsInt64(); - if (pathIdRef == 0) - continue; - - var fileIdRef = child.Get("m_FileID").GetValue().AsInt(); - var ext = am.GetExtAsset(current.file, fileIdRef, pathIdRef); + root.Children.Add(pPtrDest); - //we don't want to process monobehaviours as thats a project in itself - if (ext.info.curFileType == (int)AssetClassID.MonoBehaviour) - continue; + // recurse through dependencies + if (((AssetClassID)pPtrDest.sourceData.info.TypeId).CanHaveDependencies() && recurseFiles) + fieldStack.Push((pPtrDest.sourceData.file, pPtrDest.sourceData.baseField, pPtrDest)); + } - var node = new AssetTree + // Dependencies can be circular, so skip already visited dependencies. + } + else if (child.TemplateField.IsArray) + { + // is PPtr array, eg m_Dependencies + if (child.TemplateField.Children[1].Type.StartsWith("PPtr<")) + { + foreach (var pPtr in child.Children) { - name = ext.GetName(am), - assetExternal = ext, - FileId = fileIdRef, - PathId = pathIdRef, - Children = new List() - }; - current.node.Children.Add(node); - - //recurse through dependencies - fieldStack.Push((ext.file, ext.instance.GetBaseField(), node)); + if (pPtr.TryParsePPtr(am, current.file, out var pPtrArrayNode) + && !root.Children.Contains(pPtrArrayNode) && root != pPtrArrayNode) + { + root.Children.Add(pPtrArrayNode); + if (((AssetClassID)pPtrArrayNode.sourceData.info.TypeId).CanHaveDependencies() && recurseFiles) + fieldStack.Push((pPtrArrayNode.sourceData.file, pPtrArrayNode.sourceData.baseField, pPtrArrayNode)); + } + } } else + { + // The array might be a struct type that contains a PPtr. fieldStack.Push((current.file, child, current.node)); + } } + else + fieldStack.Push((current.file, child, current.node)); } } @@ -157,18 +137,18 @@ public static void ImportTextureData(this TextureFile texFile, Dictionary + /// Returns false for some types known to be unable to have dependencies. True otherwise. + /// This is mainly to avoid loading and parsing large serialized objects. + /// + public static bool CanHaveDependencies(this AssetClassID classId) => classId switch + { + AssetClassID.Mesh or AssetClassID.Texture2D or AssetClassID.ComputeShader => false, + _ => true, + }; + + /// + /// Check if any filter's assetClass assetFileInfo's TypeId. + /// + /// This is a pre-check to avoid a more expensive name fetch. + /// + public static bool MatchesAnyClass(this Filter[] filters, AssetFileInfo assetFileInfo) + { + foreach (var filter in filters) + { + if ((AssetClassID)assetFileInfo.TypeId == filter.assetClass) + { + return true; + } + } + + return false; + } + + /// + /// Test if the combination of AssetClassID and asset name matches any filter. + /// + public static bool AnyMatch(this Filter[] filters, AssetFileInfo assetFileInfo, string name) + { + if (name is null) + { + return false; + } + + foreach (var filter in filters) + { + if (filter.Match(assetFileInfo, name)) + { + return true; + } + } + + return false; + } + } } diff --git a/Templates/TemplateBundle2018_1_16f1.meta b/LICENSE.meta similarity index 74% rename from Templates/TemplateBundle2018_1_16f1.meta rename to LICENSE.meta index f815f1c..56f7ae8 100644 --- a/Templates/TemplateBundle2018_1_16f1.meta +++ b/LICENSE.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: dc37d80e89bcc3e499b9315b5e5d84d9 +guid: 5685f94f7ad31804685f1fe9f864739c DefaultImporter: externalObjects: {} userData: diff --git a/Library/AssetRipper.TextureDecoder.dll b/Library/AssetRipper.TextureDecoder.dll new file mode 100644 index 0000000..ac9dec2 Binary files /dev/null and b/Library/AssetRipper.TextureDecoder.dll differ diff --git a/Library/AssetRipper.TextureDecoder.dll.meta b/Library/AssetRipper.TextureDecoder.dll.meta new file mode 100644 index 0000000..5b2ae0c --- /dev/null +++ b/Library/AssetRipper.TextureDecoder.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 98cbea9cc27cdb54e973e7ae4f86e29b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/AssetsTools.NET.Texture.dll b/Library/AssetsTools.NET.Texture.dll new file mode 100644 index 0000000..7a8dc2b Binary files /dev/null and b/Library/AssetsTools.NET.Texture.dll differ diff --git a/Library/AssetsTools.NET.Texture.dll.meta b/Library/AssetsTools.NET.Texture.dll.meta new file mode 100644 index 0000000..075c4bc --- /dev/null +++ b/Library/AssetsTools.NET.Texture.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 07d48fe70ebb4b14c912556c3e4ee8bf +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/AssetsTools.NET.Texture.pdb b/Library/AssetsTools.NET.Texture.pdb new file mode 100644 index 0000000..ceaf623 Binary files /dev/null and b/Library/AssetsTools.NET.Texture.pdb differ diff --git a/Library/AssetsTools.NET.Texture.pdb.meta b/Library/AssetsTools.NET.Texture.pdb.meta new file mode 100644 index 0000000..f8b7348 --- /dev/null +++ b/Library/AssetsTools.NET.Texture.pdb.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f89d1cbdb95d1e047a95803e087033e4 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/AssetsTools.NET.dll b/Library/AssetsTools.NET.dll index 0bd4b7c..12a2259 100644 Binary files a/Library/AssetsTools.NET.dll and b/Library/AssetsTools.NET.dll differ diff --git a/Library/AssetsTools.NET.dll.meta b/Library/AssetsTools.NET.dll.meta index b1a9fd1..8fabb55 100644 --- a/Library/AssetsTools.NET.dll.meta +++ b/Library/AssetsTools.NET.dll.meta @@ -12,7 +12,7 @@ PluginImporter: validateReferences: 0 platformData: - first: - '': Any + : Any second: enabled: 0 settings: @@ -59,7 +59,7 @@ PluginImporter: second: enabled: 1 settings: - CPU: x86_64 + CPU: AnyCPU - first: Standalone: LinuxUniversal second: diff --git a/Library/AssetsTools.NET.pdb b/Library/AssetsTools.NET.pdb index 57e2e4b..a9fb743 100644 Binary files a/Library/AssetsTools.NET.pdb and b/Library/AssetsTools.NET.pdb differ diff --git a/Library/StbImageSharp.dll b/Library/StbImageSharp.dll new file mode 100644 index 0000000..5ae3bf4 Binary files /dev/null and b/Library/StbImageSharp.dll differ diff --git a/Library/StbImageSharp.dll.meta b/Library/StbImageSharp.dll.meta new file mode 100644 index 0000000..3334174 --- /dev/null +++ b/Library/StbImageSharp.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 84c26111c744e404c8dcc982baef1b3f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/StbImageWriteSharp.dll b/Library/StbImageWriteSharp.dll new file mode 100644 index 0000000..69b5982 Binary files /dev/null and b/Library/StbImageWriteSharp.dll differ diff --git a/Library/StbImageWriteSharp.dll.meta b/Library/StbImageWriteSharp.dll.meta new file mode 100644 index 0000000..c5559ee --- /dev/null +++ b/Library/StbImageWriteSharp.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6490ee66eafc238499edbb8a680afe88 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/System.Buffers.dll b/Library/System.Buffers.dll new file mode 100644 index 0000000..c0970c0 Binary files /dev/null and b/Library/System.Buffers.dll differ diff --git a/Library/System.Buffers.dll.meta b/Library/System.Buffers.dll.meta new file mode 100644 index 0000000..045c4b9 --- /dev/null +++ b/Library/System.Buffers.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ef288d51295f50e418782a8eaaf9c540 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/System.Half.dll b/Library/System.Half.dll new file mode 100644 index 0000000..aecddbf Binary files /dev/null and b/Library/System.Half.dll differ diff --git a/Library/System.Half.dll.meta b/Library/System.Half.dll.meta new file mode 100644 index 0000000..3faad5f --- /dev/null +++ b/Library/System.Half.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a4e5142a1aceaf34298e3c3480a00b4f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/System.Memory.dll b/Library/System.Memory.dll new file mode 100644 index 0000000..1e6aef8 Binary files /dev/null and b/Library/System.Memory.dll differ diff --git a/Library/System.Memory.dll.meta b/Library/System.Memory.dll.meta new file mode 100644 index 0000000..343d544 --- /dev/null +++ b/Library/System.Memory.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ae851330ccde2b348bf98628eb36b109 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/System.Numerics.Vectors.dll b/Library/System.Numerics.Vectors.dll new file mode 100644 index 0000000..a808165 Binary files /dev/null and b/Library/System.Numerics.Vectors.dll differ diff --git a/Library/System.Numerics.Vectors.dll.meta b/Library/System.Numerics.Vectors.dll.meta new file mode 100644 index 0000000..d52043b --- /dev/null +++ b/Library/System.Numerics.Vectors.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 64e37f57b94b7b64e9694e958929ddf6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/System.Runtime.CompilerServices.Unsafe.dll b/Library/System.Runtime.CompilerServices.Unsafe.dll new file mode 100644 index 0000000..b17135b Binary files /dev/null and b/Library/System.Runtime.CompilerServices.Unsafe.dll differ diff --git a/Library/System.Runtime.CompilerServices.Unsafe.dll.meta b/Library/System.Runtime.CompilerServices.Unsafe.dll.meta new file mode 100644 index 0000000..ca96023 --- /dev/null +++ b/Library/System.Runtime.CompilerServices.Unsafe.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ff4ed804b419f594983ec4d47e3dd74e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Library/classdata.tpk b/Library/classdata.tpk index d1e5870..35bb900 100644 Binary files a/Library/classdata.tpk and b/Library/classdata.tpk differ diff --git a/Library/classdatabase_license.txt.meta b/Library/classdatabase_license.txt.meta index 38c76a3..9770849 100644 --- a/Library/classdatabase_license.txt.meta +++ b/Library/classdatabase_license.txt.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4d760fd7b371acb4fb58e65d376c181f +guid: 6355cd3e353b8144990493320bd18b15 TextScriptImporter: externalObjects: {} userData: diff --git a/Templates/TemplateBundle2018_1_16f1 b/Templates/TemplateBundle2018_1_16f1 deleted file mode 100644 index f3e13d1..0000000 Binary files a/Templates/TemplateBundle2018_1_16f1 and /dev/null differ