198 lines
7.9 KiB
C#
198 lines
7.9 KiB
C#
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<AH_DuplicateDataManager>, ISerializationCallbackReceiver
|
|
{
|
|
[Serializable]
|
|
public class DuplicateAssetData
|
|
{
|
|
public List<string> Guids;
|
|
private List<string> 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<string> Paths
|
|
{
|
|
get
|
|
{
|
|
if (paths != null)
|
|
return paths;
|
|
else
|
|
return this.paths = this.Guids.Select(x => AssetDatabase.GUIDToAssetPath(x)).ToList();
|
|
}
|
|
}
|
|
|
|
public DuplicateAssetData(List<string> guids)
|
|
{
|
|
this.Guids = guids;
|
|
}
|
|
}
|
|
|
|
[SerializeField] public bool IsDirty = true;
|
|
|
|
public bool RequiresScrollviewRebuild { get; internal set; }
|
|
public bool HasCache { get; private set; }
|
|
|
|
[SerializeField] private Dictionary<string, DuplicateAssetData> duplicateDict = new Dictionary<string, DuplicateAssetData>();
|
|
|
|
#region serializationHelpers
|
|
[SerializeField] private List<string> _duplicateDictKeys = new List<string>();
|
|
[SerializeField] private List<DuplicateAssetData> _duplicateDictValues = new List<DuplicateAssetData>();
|
|
|
|
public Dictionary<string, DuplicateAssetData> 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<string, DuplicateAssetData>();
|
|
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<string, DuplicateAssetData>();
|
|
var hashDict = new Dictionary<string, List<string>>();
|
|
|
|
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<string, DuplicateAssetData>();
|
|
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<string>() { 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();
|
|
}
|
|
}
|
|
}
|
|
}
|