using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor; using UnityEngine; namespace HeurekaGames.AssetHunterPRO { [Serializable] public class AH_DuplicateDataManager : ScriptableSingleton, ISerializationCallbackReceiver { [Serializable] public class DuplicateAssetData { public List Guids; private List paths; private Texture2D preview; public Texture2D Preview { get { if (preview != null) return preview; else { var loadedObj = AssetDatabase.LoadMainAssetAtPath(Paths[0]); return preview = AssetPreview.GetAssetPreview(loadedObj); } } } public List Paths { get { if (paths != null) return paths; else return this.paths = this.Guids.Select(x => AssetDatabase.GUIDToAssetPath(x)).ToList(); } } public DuplicateAssetData(List guids) { this.Guids = guids; } } [SerializeField] public bool IsDirty = true; public bool RequiresScrollviewRebuild { get; internal set; } public bool HasCache { get; private set; } [SerializeField] private Dictionary duplicateDict = new Dictionary(); #region serializationHelpers [SerializeField] private List _duplicateDictKeys = new List(); [SerializeField] private List _duplicateDictValues = new List(); public Dictionary Entries { get {return duplicateDict; } } #endregion internal bool HasDuplicates() { return duplicateDict.Count > 0; } public void OnBeforeSerialize() { _duplicateDictKeys.Clear(); _duplicateDictValues.Clear(); foreach (var kvp in duplicateDict) { _duplicateDictKeys.Add(kvp.Key); _duplicateDictValues.Add(kvp.Value); } } public void OnAfterDeserialize() { duplicateDict = new Dictionary(); for (int i = 0; i != Math.Min(_duplicateDictKeys.Count, _duplicateDictValues.Count); i++) duplicateDict.Add(_duplicateDictKeys[i], new DuplicateAssetData(_duplicateDictValues[i].Guids)); } internal void RefreshData() { //We need to analyze the scrollview to optimize how we draw it RequiresScrollviewRebuild = true; duplicateDict = new Dictionary(); var hashDict = new Dictionary>(); var paths = AssetDatabase.GetAllAssetPaths(); var pathCount = paths.Length; using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) { string assetPathGuid; string hash; UnityEngine.Object LoadedObj; int maxReadCount = 30;//We dont need to read every line using streamreader. We only need the m_name property, and that comes early in the file int lineCounter = 0; for (int i = 0; i < pathCount; i++) { var path = paths[i]; if (AssetDatabase.IsValidFolder(path) || !path.StartsWith("Assets")) //Slow, could be done recusively continue; if (EditorUtility.DisplayCancelableProgressBar("Finding duplicates", path, ((float)i / (float)pathCount))) { duplicateDict = new Dictionary(); break; } assetPathGuid = AssetDatabase.AssetPathToGUID(path); LoadedObj = AssetDatabase.LoadMainAssetAtPath(path); string line = ""; bool foundName = false; if (path.EndsWith("LightingData.asset") || path.Contains("NavMesh")) Debug.Log("LOOK HERE"); if (LoadedObj != null) { try { using (FileStream stream = File.OpenRead(path)) { //We need to loop through certain native types (such as materials) to remove name from metadata - if we dont they wont have same hash if (AssetDatabase.IsNativeAsset(LoadedObj) && !LoadedObj.GetType().IsSubclassOf(typeof(ScriptableObject))) { string appendString = ""; using (StreamReader sr = new StreamReader(stream)) { //bool foundFileName = false; lineCounter = 0; while (!sr.EndOfStream) { lineCounter++; if (lineCounter >= maxReadCount) appendString += sr.ReadToEnd(); else { line = sr.ReadLine(); foundName = line.Contains(LoadedObj.name); if (!foundName)//we want to ignore the m_name property, since that modifies the hashvalue appendString += line; else { appendString += sr.ReadToEnd(); } } } } hash = BitConverter.ToString(System.Text.UnicodeEncoding.Unicode.GetBytes(appendString)); //hash = appendString.GetHashCode().ToString(); } else { hash = BitConverter.ToString(md5.ComputeHash(stream)); } if (!hashDict.ContainsKey(hash)) hashDict.Add(hash, new List() { assetPathGuid }); else hashDict[hash].Add(assetPathGuid); } } catch (Exception e) { Debug.LogError(e); } } } foreach (var pair in hashDict) { if (pair.Value.Count > 1) { duplicateDict.Add(pair.Key, new DuplicateAssetData(pair.Value)); } } IsDirty = false; HasCache = true; EditorUtility.ClearProgressBar(); } } } }