123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749 |
- //#define FR2_DEBUG
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using UnityEditor;
- using UnityEngine;
- using CBParams = System.Collections.Generic.List<System.Collections.Generic.List<string>>;
- using Object = UnityEngine.Object;
- namespace vietlabs.fr2
- {
- internal class FR2_DuplicateTree2 : IRefDraw
- {
- private const float TimeDelayDelete = .5f;
- private static readonly FR2_FileCompare fc = new FR2_FileCompare();
- private readonly FR2_TreeUI2.GroupDrawer groupDrawer;
- private CBParams cacheAssetList;
- public bool caseSensitive = false;
- private Dictionary<string, List<FR2_Ref>> dicIndex; //index, list
- private bool dirty;
- private int excludeCount;
- private string guidPressDelete;
- internal List<FR2_Ref> list;
- internal Dictionary<string, FR2_Ref> refs;
- public int scanExcludeByIgnoreCount;
- public int scanExcludeByTypeCount;
- private string searchTerm = "";
- private float TimePressDelete;
- public FR2_DuplicateTree2(IWindow window)
- {
- this.window = window;
- groupDrawer = new FR2_TreeUI2.GroupDrawer(DrawGroup, DrawAsset);
- }
- public IWindow window { get; set; }
- public bool Draw(Rect rect)
- {
-
- return false;
- }
-
- public bool DrawLayout()
- {
- if (dirty)
- {
- RefreshView(cacheAssetList);
- }
-
- if (fc.nChunks2 > 0 && fc.nScaned < fc.nChunks2)
- {
- Rect rect = GUILayoutUtility.GetRect(1, Screen.width, 18f, 18f);
- float p = fc.nScaned / (float) fc.nChunks2;
- EditorGUI.ProgressBar(rect, p, string.Format("Scanning {0} / {1}", fc.nScaned, fc.nChunks2));
- GUILayout.FlexibleSpace();
- return true;
- }
- if (groupDrawer.hasValidTree)
- {
- groupDrawer.tree.itemPaddingRight = 60f;
- }
- groupDrawer.DrawLayout();
- DrawHeader();
- return false;
- }
- public int ElementCount()
- {
- return list == null ? 0 : list.Count;
- }
- private void DrawAsset(Rect r, string guid)
- {
- FR2_Ref rf;
- if (!refs.TryGetValue(guid, out rf))
- {
- return;
- }
- rf.asset.Draw(r, false,
- FR2_Setting.GroupMode != FR2_RefDrawer.Mode.Folder,
- FR2_Setting.ShowFileSize,
- FR2_Setting.s.displayAssetBundleName,
- FR2_Setting.s.displayAtlasName,
- FR2_Setting.s.showUsedByClassed,
- window);
- Texture tex = AssetDatabase.GetCachedIcon(rf.asset.assetPath);
- if (tex == null)
- {
- return;
- }
- Rect drawR = r;
- drawR.x = drawR.x + drawR.width; // (groupDrawer.TreeNoScroll() ? 60f : 70f) ;
- drawR.width = 40f;
- drawR.y += 1;
- drawR.height -= 2;
- if (GUI.Button(drawR, "Use", EditorStyles.miniButton))
- {
- if (FR2_Export.IsMergeProcessing)
- {
- Debug.LogWarning("Previous merge is processing");
- }
- else
- {
- //AssetDatabase.SaveAssets();
- //EditorGUIUtility.systemCopyBuffer = rf.asset.guid;
- //EditorGUIUtility.systemCopyBuffer = rf.asset.guid;
- // Debug.Log("guid: " + rf.asset.guid + " systemCopyBuffer " + EditorGUIUtility.systemCopyBuffer);
- int index = rf.index;
- Selection.objects = list.Where(x => x.index == index)
- .Select(x => FR2_Unity.LoadAssetAtPath<Object>(x.asset.assetPath)).ToArray();
- FR2_Export.MergeDuplicate(rf.asset.guid);
- }
- }
- if (rf.asset.UsageCount() > 0)
- {
- return;
- }
- drawR.x -= 25;
- drawR.width = 20;
- if (wasPreDelete(guid))
- {
- Color col = GUI.color;
- GUI.color = Color.red;
- if (GUI.Button(drawR, "X", EditorStyles.miniButton))
- {
- guidPressDelete = null;
- AssetDatabase.DeleteAsset(rf.asset.assetPath);
- }
- GUI.color = col;
- window.WillRepaint = true;
- }
- else
- {
- if (GUI.Button(drawR, "X", EditorStyles.miniButton))
- {
- guidPressDelete = guid;
- TimePressDelete = Time.realtimeSinceStartup;
- window.WillRepaint = true;
- }
- }
- }
- private bool wasPreDelete(string guid)
- {
- if (guidPressDelete == null || guid != guidPressDelete)
- {
- return false;
- }
- if (Time.realtimeSinceStartup - TimePressDelete < TimeDelayDelete)
- {
- return true;
- }
- guidPressDelete = null;
- return false;
- }
- private void DrawGroup(Rect r, string label, int childCount)
- {
- // GUI.Label(r, label + " (" + childCount + ")", EditorStyles.boldLabel);
- FR2_Asset asset = dicIndex[label][0].asset;
- Texture tex = AssetDatabase.GetCachedIcon(asset.assetPath);
- Rect rect = r;
- if (tex != null)
- {
- rect.width = 16f;
- GUI.DrawTexture(rect, tex);
- }
- rect = r;
- rect.xMin += 16f;
- GUI.Label(rect, asset.assetName, EditorStyles.boldLabel);
- rect = r;
- rect.xMin += rect.width - 50f;
- GUI.Label(rect, FR2_Helper.GetfileSizeString(asset.fileSize), EditorStyles.miniLabel);
- rect = r;
- rect.xMin += rect.width - 70f;
- GUI.Label(rect, childCount.ToString(), EditorStyles.miniLabel);
- rect = r;
- rect.xMin += rect.width - 70f;
- }
- // private List<FR2_DuplicateFolder> duplicated;
- public void Reset(CBParams assetList)
- {
- fc.Reset(assetList, OnUpdateView, RefreshView);
- }
- private void OnUpdateView(CBParams assetList) { }
- public bool isExclueAnyItem()
- {
- return excludeCount > 0 || scanExcludeByTypeCount > 0;
- }
- public bool isExclueAnyItemByIgnoreFolder()
- {
- return scanExcludeByIgnoreCount > 0;
- }
- // void OnActive
- private void RefreshView(CBParams assetList)
- {
- cacheAssetList = assetList;
- dirty = false;
- list = new List<FR2_Ref>();
- refs = new Dictionary<string, FR2_Ref>();
- dicIndex = new Dictionary<string, List<FR2_Ref>>();
- if (assetList == null)
- {
- return;
- }
- int minScore = searchTerm.Length;
- string term1 = searchTerm;
- if (!caseSensitive)
- {
- term1 = term1.ToLower();
- }
- string term2 = term1.Replace(" ", string.Empty);
- excludeCount = 0;
- for (var i = 0; i < assetList.Count; i++)
- {
- var lst = new List<FR2_Ref>();
- for (var j = 0; j < assetList[i].Count; j++)
- {
- string guid = AssetDatabase.AssetPathToGUID(assetList[i][j]);
- if (string.IsNullOrEmpty(guid))
- {
- continue;
- }
- if (refs.ContainsKey(guid))
- {
- continue;
- }
-
- FR2_Asset asset = FR2_Cache.Api.Get(guid);
- if (asset == null) continue;
- if (!asset.assetPath.StartsWith("Assets/")) continue; // ignore builtin, packages, ...
- var fr2 = new FR2_Ref(i, 0, asset, null);
- if (FR2_Setting.IsTypeExcluded(fr2.type))
- {
- excludeCount++;
- continue; //skip this one
- }
- if (string.IsNullOrEmpty(searchTerm))
- {
- fr2.matchingScore = 0;
- list.Add(fr2);
- lst.Add(fr2);
- refs.Add(guid, fr2);
- continue;
- }
- //calculate matching score
- string name1 = fr2.asset.assetName;
- if (!caseSensitive)
- {
- name1 = name1.ToLower();
- }
- string name2 = name1.Replace(" ", string.Empty);
- int score1 = FR2_Unity.StringMatch(term1, name1);
- int score2 = FR2_Unity.StringMatch(term2, name2);
- fr2.matchingScore = Mathf.Max(score1, score2);
- if (fr2.matchingScore > minScore)
- {
- list.Add(fr2);
- lst.Add(fr2);
- refs.Add(guid, fr2);
- }
- }
- dicIndex.Add(i.ToString(), lst);
- }
- ResetGroup();
- }
- private void ResetGroup()
- {
- groupDrawer.Reset(list,
- rf => rf.asset.guid
- , GetGroup, SortGroup);
- if (window != null)
- {
- window.Repaint();
- }
- }
- private string GetGroup(FR2_Ref rf)
- {
- return rf.index.ToString();
- }
- private void SortGroup(List<string> groups)
- {
- // groups.Sort( (item1, item2) =>
- // {
- // if (item1 == "Others" || item2 == "Selection") return 1;
- // if (item2 == "Others" || item1 == "Selection") return -1;
- // return item1.CompareTo(item2);
- // });
- }
- public void SetDirty()
- {
- dirty = true;
- }
- public void RefreshSort() { }
- private void DrawHeader()
- {
- var text = groupDrawer.hasValidTree ? "Rescan" : "Scan";
-
- if (GUILayout.Button(text))
- {
- FR2_Cache.onReady -= OnCacheReady;
- FR2_Cache.onReady += OnCacheReady;
- FR2_Cache.Api.Check4Changes(false);
- }
- }
-
- private void OnCacheReady()
- {
- scanExcludeByTypeCount = 0;
- Reset(FR2_Cache.Api.ScanSimilar(IgnoreTypeWhenScan, IgnoreFolderWhenScan));
- FR2_Cache.onReady -= OnCacheReady;
- }
- private void IgnoreTypeWhenScan()
- {
- scanExcludeByTypeCount++;
- }
- private void IgnoreFolderWhenScan()
- {
- scanExcludeByIgnoreCount++;
- }
- }
- internal class FR2_FileCompare
- {
- public static HashSet<FR2_Chunk> HashChunksNotComplete;
- internal static int streamClosedCount;
- private CBParams cacheList;
- public List<FR2_Head> deads = new List<FR2_Head>();
- public List<FR2_Head> heads = new List<FR2_Head>();
- public int nChunks;
- public int nChunks2;
- public int nScaned;
- public Action<CBParams> OnCompareComplete;
- public Action<CBParams> OnCompareUpdate;
- private int streamCount;
- public void Reset(CBParams list, Action<CBParams> onUpdate, Action<CBParams> onComplete)
- {
- nChunks = 0;
- nScaned = 0;
- nChunks2 = 0;
- streamCount = streamClosedCount = 0;
- HashChunksNotComplete = new HashSet<FR2_Chunk>();
- if (heads.Count > 0)
- {
- for (var i = 0; i < heads.Count; i++)
- {
- heads[i].CloseChunk();
- }
- }
- deads.Clear();
- heads.Clear();
- OnCompareUpdate = onUpdate;
- OnCompareComplete = onComplete;
- if (list.Count <= 0)
- {
- OnCompareComplete(new CBParams());
- return;
- }
- cacheList = list;
- for (var i = 0; i < list.Count; i++)
- {
- var file = new FileInfo(list[i][0]);
- int nChunk = Mathf.CeilToInt(file.Length / (float) FR2_Head.chunkSize);
- nChunks2 += nChunk;
- }
- // for(int i =0;i< list.Count;i++)
- // {
- // AddHead(list[i]);
- // }
- AddHead(cacheList[cacheList.Count - 1]);
- cacheList.RemoveAt(cacheList.Count - 1);
- EditorApplication.update -= ReadChunkAsync;
- EditorApplication.update += ReadChunkAsync;
- }
- public FR2_FileCompare AddHead(List<string> files)
- {
- if (files.Count < 2)
- {
- Debug.LogWarning("Something wrong ! head should not contains < 2 elements");
- }
- var chunkList = new List<FR2_Chunk>();
- for (var i = 0; i < files.Count; i++)
- {
- streamCount++;
- // Debug.Log("new stream " + files[i]);
- chunkList.Add(new FR2_Chunk
- {
- file = files[i],
- stream = new FileStream(files[i], FileMode.Open, FileAccess.Read),
- buffer = new byte[FR2_Head.chunkSize]
- });
- }
- var file = new FileInfo(files[0]);
- int nChunk = Mathf.CeilToInt(file.Length / (float) FR2_Head.chunkSize);
- heads.Add(new FR2_Head
- {
- fileSize = file.Length,
- currentChunk = 0,
- nChunk = nChunk,
- chunkList = chunkList
- });
- nChunks += nChunk;
- return this;
- }
- private bool checkCompleteAllCurFile()
- {
- return streamClosedCount + HashChunksNotComplete.Count >= streamCount; //-1 for safe
- }
- private void ReadChunkAsync()
- {
- bool alive = ReadChunk();
- HashChunksNotComplete.RemoveWhere(x => x.stream == null || !x.stream.CanRead);
- if (cacheList.Count > 0 && checkCompleteAllCurFile()) //complete all chunk
- {
- int numCall = FR2_Cache.priority; // - 2;
- if (numCall <= 0)
- {
- numCall = 1;
- }
- for (var i = 0; i < numCall; i++)
- {
- if (cacheList.Count <= 0)
- {
- break;
- }
- AddHead(cacheList[cacheList.Count - 1]);
- cacheList.RemoveAt(cacheList.Count - 1);
- }
- }
- var update = false;
- for (int i = heads.Count - 1; i >= 0; i--)
- {
- FR2_Head h = heads[i];
- if (h.isDead)
- {
- h.CloseChunk();
- heads.RemoveAt(i);
- if (h.chunkList.Count > 1)
- {
- update = true;
- deads.Add(h);
- }
- }
- }
- if (update)
- {
- Trigger(OnCompareUpdate);
- }
- if (!alive && cacheList.Count <= 0 && checkCompleteAllCurFile()
- ) //&& cacheList.Count <= 0 complete all chunk and cache list empty
- {
- foreach (FR2_Chunk item in HashChunksNotComplete)
- {
- if (item.stream != null && item.stream.CanRead)
- {
- item.stream.Close();
- item.stream = null;
- }
- }
- HashChunksNotComplete.Clear();
- // Debug.Log("complete ");
- nScaned = nChunks;
- EditorApplication.update -= ReadChunkAsync;
- Trigger(OnCompareComplete);
- }
- }
- private void Trigger(Action<CBParams> cb)
- {
- if (cb == null)
- {
- return;
- }
- CBParams list = deads.Select(item => item.GetFiles()).ToList();
- //#if FR2_DEBUG
- // Debug.Log("Callback ! " + deads.Count + ":" + heads.Count);
- //#endif
- cb(list);
- }
- private bool ReadChunk()
- {
- var alive = false;
- for (var i = 0; i < heads.Count; i++)
- {
- FR2_Head h = heads[i];
- if (h.isDead)
- {
- Debug.LogWarning("Should never be here : " + h.chunkList[0].file);
- continue;
- }
- nScaned++;
- alive = true;
- h.ReadChunk();
- h.CompareChunk(heads);
- break;
- }
- //if (!alive) return false;
- //alive = false;
- //for (var i = 0; i < heads.Count; i++)
- //{
- // var h = heads[i];
- // if (h.isDead) continue;
- // h.CompareChunk(heads);
- // alive |= !h.isDead;
- //}
- return alive;
- }
- }
- internal class FR2_Head
- {
- public const int chunkSize = 10240;
- public List<FR2_Chunk> chunkList;
- public int currentChunk;
- public long fileSize;
- public int nChunk;
- public int size; //last stream read size
- public bool isDead
- {
- get { return currentChunk == nChunk || chunkList.Count == 1; }
- }
- public List<string> GetFiles()
- {
- return chunkList.Select(item => item.file).ToList();
- }
- public void AddToDict(byte b, FR2_Chunk chunk, Dictionary<byte, List<FR2_Chunk>> dict)
- {
- List<FR2_Chunk> list;
- if (!dict.TryGetValue(b, out list))
- {
- list = new List<FR2_Chunk>();
- dict.Add(b, list);
- }
- list.Add(chunk);
- }
- public void CloseChunk()
- {
- for (var i = 0; i < chunkList.Count; i++)
- {
- // Debug.Log("stream close");
- FR2_FileCompare.streamClosedCount++;
- chunkList[i].stream.Close();
- chunkList[i].stream = null;
- }
- }
- public void ReadChunk()
- {
- #if FR2_DEBUG
- if (currentChunk == 0) Debug.LogWarning("Read <" + chunkList[0].file + "> " + currentChunk + ":" + nChunk);
- #endif
- if (currentChunk == nChunk)
- {
- Debug.LogWarning("Something wrong, should dead <" + isDead + ">");
- return;
- }
- int from = currentChunk * chunkSize;
- size = (int) Mathf.Min(fileSize - from, chunkSize);
- for (var i = 0; i < chunkList.Count; i++)
- {
- FR2_Chunk chunk = chunkList[i];
- chunk.size = size;
- chunk.stream.Read(chunk.buffer, 0, size);
- }
- currentChunk++;
- }
- public void CompareChunk(List<FR2_Head> heads)
- {
- int idx = chunkList.Count;
- byte[] buffer = chunkList[idx - 1].buffer;
- while (--idx >= 0)
- {
- FR2_Chunk chunk = chunkList[idx];
- int diff = FirstDifferentIndex(buffer, chunk.buffer, size);
- if (diff == -1)
- {
- continue;
- }
- #if FR2_DEBUG
- Debug.Log(string.Format(
- " --> Different found at : idx={0} diff={1} size={2} chunk={3}",
- idx, diff, size, currentChunk));
- #endif
- byte v = buffer[diff];
- var d = new Dictionary<byte, List<FR2_Chunk>>(); //new heads
- chunkList.RemoveAt(idx);
- FR2_FileCompare.HashChunksNotComplete.Add(chunk);
- AddToDict(chunk.buffer[diff], chunk, d);
- for (int j = idx - 1; j >= 0; j--)
- {
- FR2_Chunk tChunk = chunkList[j];
- byte tValue = tChunk.buffer[diff];
- if (tValue == v)
- {
- continue;
- }
- idx--;
- FR2_FileCompare.HashChunksNotComplete.Add(tChunk);
- chunkList.RemoveAt(j);
- AddToDict(tChunk.buffer[diff], tChunk, d);
- }
- foreach (KeyValuePair<byte, List<FR2_Chunk>> item in d)
- {
- List<FR2_Chunk> list = item.Value;
- if (list.Count == 1)
- {
- #if FR2_DEBUG
- Debug.Log(" --> Dead head found for : " + list[0].file);
- #endif
- }
- else if (list.Count > 1) // 1 : dead head
- {
- #if FR2_DEBUG
- Debug.Log(" --> NEW HEAD : " + list[0].file);
- #endif
- heads.Add(new FR2_Head
- {
- nChunk = nChunk,
- fileSize = fileSize,
- currentChunk = currentChunk - 1,
- chunkList = list
- });
- }
- }
- }
- }
- internal static int FirstDifferentIndex(byte[] arr1, byte[] arr2, int maxIndex)
- {
- for (var i = 0; i < maxIndex; i++)
- {
- if (arr1[i] != arr2[i])
- {
- return i;
- }
- }
- return -1;
- }
- }
- internal class FR2_Chunk
- {
- public byte[] buffer;
- public string file;
- public long size;
- public FileStream stream;
- }
- }
|