private static void traverseDirectory(int parentIndex, string path, List <BuildReportAsset> usedAssets, int heirarchyDepth, ref int directoriesTraversed, SortedDictionary <AHSerializableSystemType, bool> validTypeList) { directoriesTraversed++; EditorUtility.DisplayProgressBar( "Traversing Directories", "(" + directoriesTraversed + " of " + m_NumberOfDirectories + ") Analyzing " + path.Substring(path.IndexOf("/Assets") + 1), (float)directoriesTraversed / (float)m_NumberOfDirectories); //Get the settings to exclude certain folders or suffixes foreach (UnityEngine.Object dir in AHMainWindow.Instance.settings.m_DirectoryExcludes) { //TODO Can this be done more elegantly int startingIndex = Application.dataPath.Length - 6; string relativePath = path.Substring(startingIndex, path.Length - startingIndex); UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath(relativePath, typeof(UnityEngine.Object)); if (dir == obj) { //This folder was exluded return; } } //Exclude types and folders that should not be reviewed //TODO perhaps improve performance of this step (Also use String.Contains(excluder, StringComparison.OrdinalIgnoreCase)) might be better not to use LINQ string[] assetsInDirectory = Directory.GetFiles(path, "*.*", SearchOption.TopDirectoryOnly) .Where(name => !name.EndsWith(".meta", StringComparison.OrdinalIgnoreCase) && (!name.EndsWith(".unity", StringComparison.OrdinalIgnoreCase)) && (!name.EndsWith("thumbs.db", StringComparison.OrdinalIgnoreCase)) && (!name.EndsWith(".orig", StringComparison.OrdinalIgnoreCase)) && (!name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) && (!name.EndsWith(".mdb", StringComparison.OrdinalIgnoreCase)) && (!name.Contains(Path.DirectorySeparatorChar + "helper" + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) && (!name.Contains(Path.DirectorySeparatorChar + "plugins" + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) && (!name.Contains(Path.DirectorySeparatorChar + "streamingassets" + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) && (!name.Contains(Path.DirectorySeparatorChar + "resources" + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) && (!name.Contains(Path.DirectorySeparatorChar + "editor default resources" + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) && (!name.Contains(Path.DirectorySeparatorChar + "editor" + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) && (!name.EndsWith(@".ds_store", StringComparison.OrdinalIgnoreCase)) && (!name.EndsWith(@".workspace.mel", StringComparison.OrdinalIgnoreCase)) && (!name.EndsWith(@".mayaswatches", StringComparison.OrdinalIgnoreCase))) .ToArray(); //TODO this could also be improved for performance for (int i = 0; i < assetsInDirectory.Length; i++) { assetsInDirectory[i] = assetsInDirectory[i].Substring(assetsInDirectory[i].IndexOf("/Assets") + 1); assetsInDirectory[i] = assetsInDirectory[i].Replace(@"\", "/"); } //Remove the assets we ignore through settings && Find any assets that does not live in UsedAssets List //TODO for performance reasons, perhaps dont to this for each folder, but just once, when finished? //That would mean to do folder creation and populating unused assets lists after all folders are traversed var result = assetsInDirectory.Where(p => (!AHMainWindow.Instance.settings.m_AssetGUIDExcludes.Any(p2 => UnityEditor.AssetDatabase.GUIDToAssetPath(p2) == p)) && !usedAssets.Any(p2 => UnityEditor.AssetDatabase.GUIDToAssetPath(p2.GUID) == p)); //Create new folder object AHProjectFolderInfo afInfo = new AHProjectFolderInfo(); //TODO this could also be improved for performance afInfo.DirectoryName = path.Substring(path.IndexOf("/Assets") + 1).Replace(@"\", "/"); afInfo.ParentIndex = parentIndex; if (heirarchyDepth == 0) { afInfo.FoldOut = true; } //Add to static list AHMainWindow.Instance.AddProjectFolderInfo(afInfo); if (parentIndex != -1) { AHMainWindow.Instance.GetFolderList()[parentIndex].AddChildFolder(afInfo); } UnityEngine.Object objToFind; foreach (string assetName in result) { bool bExclude = false; foreach (string excluder in AHMainWindow.Instance.settings.m_AssetSubstringExcludes) { //Exlude Asset Exclude substrings from settings //If we find an excluded asset just continue to next iteration in loop if (assetName.Contains(excluder, StringComparison.OrdinalIgnoreCase)) { bExclude = true; } } if (bExclude) { continue; } objToFind = AssetDatabase.LoadAssetAtPath(assetName, typeof(UnityEngine.Object)); if (objToFind == null) { Debug.LogWarning("Couldnt find " + assetName); continue; } AHSerializableSystemType assetType = new AHSerializableSystemType(objToFind.GetType()); if (assetType.SystemType != typeof(MonoScript) && (!AHMainWindow.Instance.settings.m_AssetTypeExcludes.Contains(assetType))) { AssetObjectInfo newAssetInfo = new AssetObjectInfo(assetName, assetType); afInfo.AddAsset(newAssetInfo); } objToFind = null; //Memory leak safeguard //This have heavy performance implications if (AHMainWindow.Instance.settings.m_MemoryCleanupActive) { UnloadUnused(); } } string[] nextLevelDirectories = System.IO.Directory.GetDirectories(path, "*.*", System.IO.SearchOption.TopDirectoryOnly); //Memory leak safeguard per folder if (!AHMainWindow.Instance.settings.m_MemoryCleanupActive) { UnloadUnused(); } foreach (string nld in nextLevelDirectories) { traverseDirectory(AHMainWindow.Instance.GetFolderList().IndexOf(afInfo), nld, usedAssets, (heirarchyDepth + 1), ref directoriesTraversed, validTypeList); } }