using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; namespace AssetUsageDetectorNamespace { public partial class AssetUsageDetector { #region Helper Classes private class CacheEntry { public enum Result { Unknown = 0, No = 1, Yes = 2 }; public string hash; public string[] dependencies; public long[] fileSizes; public bool verified; public Result searchResult; public CacheEntry( string path ) { Verify( path ); } public CacheEntry( string hash, string[] dependencies, long[] fileSizes ) { this.hash = hash; this.dependencies = dependencies; this.fileSizes = fileSizes; } public void Verify( string path ) { string hash = AssetDatabase.GetAssetDependencyHash( path ).ToString(); if( this.hash != hash ) { this.hash = hash; Refresh( path ); } verified = true; } public void Refresh( string path ) { dependencies = AssetDatabase.GetDependencies( path, false ); if( fileSizes == null || fileSizes.Length != dependencies.Length ) fileSizes = new long[dependencies.Length]; int length = dependencies.Length; for( int i = 0; i < length; i++ ) { if( !string.IsNullOrEmpty( dependencies[i] ) ) { FileInfo assetFile = new FileInfo( dependencies[i] ); fileSizes[i] = assetFile.Exists ? assetFile.Length : 0L; } else { // This dependency is empty which causes issues when passed to FileInfo constructor // Find a non-empty dependency and move it to this index for( int j = length - 1; j > i; j--, length-- ) { if( !string.IsNullOrEmpty( dependencies[j] ) ) { dependencies[i--] = dependencies[j]; break; } } length--; } } if( length != fileSizes.Length ) { Array.Resize( ref dependencies, length ); Array.Resize( ref fileSizes, length ); } } } #endregion // An optimization to fetch the dependencies of an asset only once (key is the path of the asset) private Dictionary assetDependencyCache; private CacheEntry lastRefreshedCacheEntry; private string CachePath { get { return Application.dataPath + "/../Library/AssetUsageDetector.cache"; } } // Path of the cache file public void SaveCache() { if( assetDependencyCache == null ) return; try { using( FileStream stream = new FileStream( CachePath, FileMode.Create ) ) using( BinaryWriter writer = new BinaryWriter( stream ) ) { writer.Write( assetDependencyCache.Count ); foreach( var keyValuePair in assetDependencyCache ) { CacheEntry cacheEntry = keyValuePair.Value; string[] dependencies = cacheEntry.dependencies; long[] fileSizes = cacheEntry.fileSizes; writer.Write( keyValuePair.Key ); writer.Write( cacheEntry.hash ); writer.Write( dependencies.Length ); for( int i = 0; i < dependencies.Length; i++ ) { writer.Write( dependencies[i] ); writer.Write( fileSizes[i] ); } } } } catch( Exception e ) { Debug.LogException( e ); } } private void LoadCache() { if( File.Exists( CachePath ) ) { using( FileStream stream = new FileStream( CachePath, FileMode.Open, FileAccess.Read ) ) using( BinaryReader reader = new BinaryReader( stream ) ) { try { int cacheSize = reader.ReadInt32(); assetDependencyCache = new Dictionary( cacheSize ); for( int i = 0; i < cacheSize; i++ ) { string assetPath = reader.ReadString(); string hash = reader.ReadString(); int dependenciesLength = reader.ReadInt32(); string[] dependencies = new string[dependenciesLength]; long[] fileSizes = new long[dependenciesLength]; for( int j = 0; j < dependenciesLength; j++ ) { dependencies[j] = reader.ReadString(); fileSizes[j] = reader.ReadInt64(); } assetDependencyCache[assetPath] = new CacheEntry( hash, dependencies, fileSizes ); } } catch( Exception e ) { assetDependencyCache = null; Debug.LogWarning( "Couldn't load cache (probably cache format has changed in an update), will regenerate cache.\n" + e.ToString() ); } } } // Generate cache for all assets for the first time if( assetDependencyCache == null ) { assetDependencyCache = new Dictionary( 1024 * 8 ); string[] allAssets = AssetDatabase.GetAllAssetPaths(); if( allAssets.Length > 0 ) { double startTime = EditorApplication.timeSinceStartup; try { for( int i = 0; i < allAssets.Length; i++ ) { if( i % 30 == 0 && EditorUtility.DisplayCancelableProgressBar( "Please wait...", "Generating cache for the first time (optional)", (float) i / allAssets.Length ) ) { EditorUtility.ClearProgressBar(); Debug.LogWarning( "Initial cache generation cancelled, cache will be generated on the fly as more and more assets are searched." ); break; } assetDependencyCache[allAssets[i]] = new CacheEntry( allAssets[i] ); } EditorUtility.ClearProgressBar(); Debug.Log( "Cache generated in " + ( EditorApplication.timeSinceStartup - startTime ).ToString( "F2" ) + " seconds" ); Debug.Log( "You can always reset the cache by deleting " + Path.GetFullPath( CachePath ) ); SaveCache(); } catch( Exception e ) { EditorUtility.ClearProgressBar(); Debug.LogException( e ); } } } } } }