| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795 | using System;using UnityEngine;using UnityEditor;using UnityEngine.Assertions;using System.Collections.Generic;using System.Linq;using UnityEditor.IMGUI.Controls;using AssetBundleBrowser.AssetBundleDataSource;namespace AssetBundleBrowser.AssetBundleModel{    /// <summary>    /// Static class holding model data for Asset Bundle Browser tool. Data in Model is read from DataSource, but is not pushed.      ///     /// If not using a custom DataSource, then the data comes from the AssetDatabase.  If you wish to alter the data from code,     ///  you should just push changes to the AssetDatabase then tell the Model to Rebuild(). If needed, you can also loop over    ///  Update() until it returns true to force all sub-items to refresh.    ///      /// </summary>    public static class Model    {        const string k_NewBundleBaseName = "newbundle";        const string k_NewVariantBaseName = "newvariant";        internal static /*const*/ Color k_LightGrey = Color.grey * 1.5f;        private static ABDataSource s_DataSource;        private static BundleFolderConcreteInfo s_RootLevelBundles = new BundleFolderConcreteInfo("", null);        private static List<ABMoveData> s_MoveData = new List<ABMoveData>();        private static List<BundleInfo> s_BundlesToUpdate = new List<BundleInfo>();        private static Dictionary<string, AssetInfo> s_GlobalAssetList = new Dictionary<string, AssetInfo>();        private static Dictionary<string, HashSet<string>> s_DependencyTracker = new Dictionary<string, HashSet<string>>();        private static bool s_InErrorState = false;        const string k_DefaultEmptyMessage = "Drag assets here or right-click to begin creating bundles.";        const string k_ProblemEmptyMessage = "There was a problem parsing the list of bundles. See console.";        private static string s_EmptyMessageString;        static private Texture2D s_folderIcon = null;        static private Texture2D s_bundleIcon = null;        static private Texture2D s_sceneIcon = null;        /// <summary>        /// If using a custom source of asset bundles, you can implement your own ABDataSource and set it here as the active        ///  DataSource.  This will allow you to use the Browser with data that you provide.        ///          /// If no custom DataSource is provided, then the Browser will create one that feeds off of and into the         ///  AssetDatabase.        ///          /// </summary>        public static ABDataSource DataSource        {            get            {                if (s_DataSource == null)                {                    s_DataSource = new AssetDatabaseABDataSource ();                }                return s_DataSource;            }            set { s_DataSource = value; }        }        /// <summary>        /// Update will loop over bundles that need updating and update them. It will only update one bundle        ///  per frame and will continue on the same bundle next frame until that bundle is marked as doneUpdating.        ///  By default, this will cause a very slow collection of dependency data as it will only update one bundle per        /// </summary>        public static bool Update()        {            bool shouldRepaint = false;            ExecuteAssetMove(false);     //this should never do anything. just a safety check.            //TODO - look into EditorApplication callback functions.                        int size = s_BundlesToUpdate.Count;            if (size > 0)            {                s_BundlesToUpdate[size - 1].Update();                s_BundlesToUpdate.RemoveAll(item => item.doneUpdating == true);                if (s_BundlesToUpdate.Count == 0)                {                    shouldRepaint = true;                    foreach(var bundle in s_RootLevelBundles.GetChildList())                    {                        bundle.RefreshDupeAssetWarning();                    }                }            }            return shouldRepaint;        }        internal static void ForceReloadData(TreeView tree)        {            s_InErrorState = false;            Rebuild();            tree.Reload();            bool doneUpdating = s_BundlesToUpdate.Count == 0;            EditorUtility.DisplayProgressBar("Updating Bundles", "", 0);            int fullBundleCount = s_BundlesToUpdate.Count;            while (!doneUpdating && !s_InErrorState)            {                int currCount = s_BundlesToUpdate.Count;                EditorUtility.DisplayProgressBar("Updating Bundles", s_BundlesToUpdate[currCount-1].displayName, (float)(fullBundleCount- currCount) / (float)fullBundleCount);                doneUpdating = Update();            }            EditorUtility.ClearProgressBar();        }                /// <summary>        /// Clears and rebuilds model data.          /// </summary>        public static void Rebuild()        {            s_RootLevelBundles = new BundleFolderConcreteInfo("", null);            s_MoveData = new List<ABMoveData>();            s_BundlesToUpdate = new List<BundleInfo>();            s_GlobalAssetList = new Dictionary<string, AssetInfo>();            Refresh();        }        internal static void AddBundlesToUpdate(IEnumerable<BundleInfo> bundles)        {            foreach(var bundle in bundles)            {                bundle.ForceNeedUpdate();                s_BundlesToUpdate.Add(bundle);            }        }        internal static void Refresh()        {            s_EmptyMessageString = k_ProblemEmptyMessage;            if (s_InErrorState)                return;            var bundleList = ValidateBundleList();            if(bundleList != null)            {                s_EmptyMessageString = k_DefaultEmptyMessage;                foreach (var bundleName in bundleList)                {                    AddBundleToModel(bundleName);                }                AddBundlesToUpdate(s_RootLevelBundles.GetChildList());            }            if(s_InErrorState)            {                s_RootLevelBundles = new BundleFolderConcreteInfo("", null);                s_EmptyMessageString = k_ProblemEmptyMessage;            }        }        internal static string[] ValidateBundleList()        {            var bundleList = DataSource.GetAllAssetBundleNames();            bool valid = true;            HashSet<string> bundleSet = new HashSet<string>();            int index = 0;            bool attemptedBundleReset = false;            while(index < bundleList.Length)            {                var name = bundleList[index];                if (!bundleSet.Add(name))                {                    LogError("Two bundles share the same name: " + name);                    valid = false;                }                int lastDot = name.LastIndexOf('.');                if (lastDot > -1)                {                    var bunName = name.Substring(0, lastDot);                    var extraDot = bunName.LastIndexOf('.');                    if(extraDot > -1)                    {                        if(attemptedBundleReset)                        {                            var message = "Bundle name '" + bunName + "' contains a period.";                            message += "  Internally Unity keeps 'bundleName' and 'variantName' separate, but externally treat them as 'bundleName.variantName'.";                            message += "  If a bundleName contains a period, the build will (probably) succeed, but this tool cannot tell which portion is bundle and which portion is variant.";                            LogError(message);                            valid = false;                        }                        else                        {                            if (!DataSource.IsReadOnly ())                            {                                DataSource.RemoveUnusedAssetBundleNames();                            }                            index = 0;                            bundleSet.Clear();                            bundleList = DataSource.GetAllAssetBundleNames();                            attemptedBundleReset = true;                            continue;                        }                    }                    if (bundleList.Contains(bunName))                    {                        //there is a bundle.none and a bundle.variant coexisting.  Need to fix that or return an error.                        if (attemptedBundleReset)                        {                            valid = false;                            var message = "Bundle name '" + bunName + "' exists without a variant as well as with variant '" + name.Substring(lastDot+1) + "'.";                            message += " That is an illegal state that will not build and must be cleaned up.";                            LogError(message);                        }                        else                        {                            if (!DataSource.IsReadOnly ())                            {                                DataSource.RemoveUnusedAssetBundleNames();                            }                            index = 0;                            bundleSet.Clear();                            bundleList = DataSource.GetAllAssetBundleNames();                            attemptedBundleReset = true;                            continue;                        }                    }                }                index++;            }            if (valid)                return bundleList;            else                return null;        }        internal static bool BundleListIsEmpty()        {            return (s_RootLevelBundles.GetChildList().Count() == 0);        }        internal static string GetEmptyMessage()        {            return s_EmptyMessageString;        }        internal static BundleInfo CreateEmptyBundle(BundleFolderInfo folder = null, string newName = null)        {            if ((folder as BundleVariantFolderInfo) != null)                return CreateEmptyVariant(folder as BundleVariantFolderInfo);            folder = (folder == null) ? s_RootLevelBundles : folder;            string name = GetUniqueName(folder, newName);            BundleNameData nameData;            nameData = new BundleNameData(folder.m_Name.bundleName, name);            return AddBundleToFolder(folder, nameData);        }        internal static BundleInfo CreateEmptyVariant(BundleVariantFolderInfo folder)        {            string name = GetUniqueName(folder, k_NewVariantBaseName);            string variantName = folder.m_Name.bundleName + "." + name;            BundleNameData nameData = new BundleNameData(variantName);            return AddBundleToFolder(folder.parent, nameData);        }        internal static BundleFolderInfo CreateEmptyBundleFolder(BundleFolderConcreteInfo folder = null)        {            folder = (folder == null) ? s_RootLevelBundles : folder;            string name = GetUniqueName(folder) + "/dummy";            BundleNameData nameData = new BundleNameData(folder.m_Name.bundleName, name);            return AddFoldersToBundle(s_RootLevelBundles, nameData);        }        private static BundleInfo AddBundleToModel(string name)        {            if (name == null)                return null;                        BundleNameData nameData = new BundleNameData(name);            BundleFolderInfo folder = AddFoldersToBundle(s_RootLevelBundles, nameData);            BundleInfo currInfo = AddBundleToFolder(folder, nameData);            return currInfo;        }        private static BundleFolderConcreteInfo AddFoldersToBundle(BundleFolderInfo root, BundleNameData nameData)        {            BundleInfo currInfo = root;            var folder = currInfo as BundleFolderConcreteInfo;            var size = nameData.pathTokens.Count;            for (var index = 0; index < size; index++)            {                if (folder != null)                {                    currInfo = folder.GetChild(nameData.pathTokens[index]);                    if (currInfo == null)                    {                        currInfo = new BundleFolderConcreteInfo(nameData.pathTokens, index + 1, folder);                        folder.AddChild(currInfo);                    }                    folder = currInfo as BundleFolderConcreteInfo;                    if (folder == null)                    {                        s_InErrorState = true;                        LogFolderAndBundleNameConflict(currInfo.m_Name.fullNativeName);                        break;                    }                }            }            return currInfo as BundleFolderConcreteInfo;        }        private static void LogFolderAndBundleNameConflict(string name)        {            var message = "Bundle '";            message += name;            message += "' has a name conflict with a bundle-folder.";            message += "Display of bundle data and building of bundles will not work.";            message += "\nDetails: If you name a bundle 'x/y', then the result of your build will be a bundle named 'y' in a folder named 'x'.  You thus cannot also have a bundle named 'x' at the same level as the folder named 'x'.";            LogError(message);        }        private static BundleInfo AddBundleToFolder(BundleFolderInfo root, BundleNameData nameData)        {            BundleInfo currInfo = root.GetChild(nameData.shortName);            if (!System.String.IsNullOrEmpty(nameData.variant))            {                if(currInfo == null)                {                    currInfo = new BundleVariantFolderInfo(nameData.bundleName, root);                    root.AddChild(currInfo);                }                var folder = currInfo as BundleVariantFolderInfo;                if (folder == null)                {                    var message = "Bundle named " + nameData.shortName;                    message += " exists both as a standard bundle, and a bundle with variants.  ";                    message += "This message is not supported for display or actual bundle building.  ";                    message += "You must manually fix bundle naming in the inspector.";                                        LogError(message);                    return null;                }                                                currInfo = folder.GetChild(nameData.variant);                if (currInfo == null)                {                    currInfo = new BundleVariantDataInfo(nameData.fullNativeName, folder);                    folder.AddChild(currInfo);                }                            }            else            {                if (currInfo == null)                {                    currInfo = new BundleDataInfo(nameData.fullNativeName, root);                    root.AddChild(currInfo);                }                else                {                    var dataInfo = currInfo as BundleDataInfo;                    if (dataInfo == null)                    {                        s_InErrorState = true;                        LogFolderAndBundleNameConflict(nameData.fullNativeName);                    }                }            }            return currInfo;        }        private static string GetUniqueName(BundleFolderInfo folder, string suggestedName = null)        {            suggestedName = (suggestedName == null) ? k_NewBundleBaseName : suggestedName;            string name = suggestedName;            int index = 1;            bool foundExisting = (folder.GetChild(name) != null);            while (foundExisting)            {                name = suggestedName + index;                index++;                foundExisting = (folder.GetChild(name) != null);            }            return name;        }        internal static BundleTreeItem CreateBundleTreeView()        {            return s_RootLevelBundles.CreateTreeView(-1);        }        internal static AssetTreeItem CreateAssetListTreeView(IEnumerable<AssetBundleModel.BundleInfo> selectedBundles)        {            var root = new AssetTreeItem();            if (selectedBundles != null)            {                foreach (var bundle in selectedBundles)                {                    bundle.AddAssetsToNode(root);                }            }            return root;        }        internal static bool HandleBundleRename(BundleTreeItem item, string newName)        {            var originalName = new BundleNameData(item.bundle.m_Name.fullNativeName);            var findDot = newName.LastIndexOf('.');            var findSlash = newName.LastIndexOf('/');            var findBSlash = newName.LastIndexOf('\\');            if (findDot == 0 || findSlash == 0 || findBSlash == 0)                return false; //can't start a bundle with a / or .            bool result = item.bundle.HandleRename(newName, 0);            if (findDot > 0 || findSlash > 0 || findBSlash > 0)            {                item.bundle.parent.HandleChildRename(newName, string.Empty);            }            ExecuteAssetMove();            var node = FindBundle(originalName);            if (node != null)            {                var message = "Failed to rename bundle named: ";                message += originalName.fullNativeName;                message += ".  Most likely this is due to the bundle being assigned to a folder in your Assets directory, AND that folder is either empty or only contains assets that are explicitly assigned elsewhere.";                Debug.LogError(message);            }            return result;          }        internal static void HandleBundleReparent(IEnumerable<BundleInfo> bundles, BundleFolderInfo parent)        {            parent = (parent == null) ? s_RootLevelBundles : parent;            foreach (var bundle in bundles)            {                bundle.HandleReparent(parent.m_Name.bundleName, parent);            }            ExecuteAssetMove();        }        internal static void HandleBundleMerge(IEnumerable<BundleInfo> bundles, BundleDataInfo target)        {            foreach (var bundle in bundles)            {                bundle.HandleDelete(true, target.m_Name.bundleName, target.m_Name.variant);            }            ExecuteAssetMove();        }        internal static void HandleBundleDelete(IEnumerable<BundleInfo> bundles)        {            var nameList = new List<BundleNameData>();            foreach (var bundle in bundles)            {                nameList.Add(bundle.m_Name);                bundle.HandleDelete(true);            }            ExecuteAssetMove();            //check to see if any bundles are still there...            foreach(var name in nameList)            {                var node = FindBundle(name);                if(node != null)                {                    var message = "Failed to delete bundle named: ";                    message += name.fullNativeName;                    message += ".  Most likely this is due to the bundle being assigned to a folder in your Assets directory, AND that folder is either empty or only contains assets that are explicitly assigned elsewhere.";                    Debug.LogError(message);                }            }        }        internal static BundleInfo FindBundle(BundleNameData name)        {            BundleInfo currNode = s_RootLevelBundles;            foreach (var token in name.pathTokens)            {                if(currNode is BundleFolderInfo)                {                    currNode = (currNode as BundleFolderInfo).GetChild(token);                    if (currNode == null)                        return null;                }                else                {                    return null;                }            }            if(currNode is BundleFolderInfo)            {                currNode = (currNode as BundleFolderInfo).GetChild(name.shortName);                if(currNode is BundleVariantFolderInfo)                {                    currNode = (currNode as BundleVariantFolderInfo).GetChild(name.variant);                }                return currNode;            }            else            {                return null;            }        }        internal static BundleInfo HandleDedupeBundles(IEnumerable<BundleInfo> bundles, bool onlyOverlappedAssets)        {            var newBundle = CreateEmptyBundle();            HashSet<string> dupeAssets = new HashSet<string>();            HashSet<string> fullAssetList = new HashSet<string>();            //if they were just selected, then they may still be updating.            bool doneUpdating = s_BundlesToUpdate.Count == 0;            while (!doneUpdating)                doneUpdating = Update();            foreach (var bundle in bundles)            {                foreach (var asset in bundle.GetDependencies())                {                    if (onlyOverlappedAssets)                    {                        if (!fullAssetList.Add(asset.fullAssetName))                            dupeAssets.Add(asset.fullAssetName);                    }                    else                    {                        if (asset.IsMessageSet(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles))                            dupeAssets.Add(asset.fullAssetName);                    }                }            }            if (dupeAssets.Count == 0)                return null;                        MoveAssetToBundle(dupeAssets, newBundle.m_Name.bundleName, string.Empty);            ExecuteAssetMove();            return newBundle;        }        internal static BundleInfo HandleConvertToVariant(BundleDataInfo bundle)        {            bundle.HandleDelete(true, bundle.m_Name.bundleName, k_NewVariantBaseName);            ExecuteAssetMove();            var root = bundle.parent.GetChild(bundle.m_Name.shortName) as BundleVariantFolderInfo;            if (root != null)                return root.GetChild(k_NewVariantBaseName);            else            {                //we got here because the converted bundle was empty.                var vfolder = new BundleVariantFolderInfo(bundle.m_Name.bundleName, bundle.parent);                var vdata = new BundleVariantDataInfo(bundle.m_Name.bundleName + "." + k_NewVariantBaseName, vfolder);                bundle.parent.AddChild(vfolder);                vfolder.AddChild(vdata);                return vdata;            }        }        internal class ABMoveData        {            internal string assetName;            internal string bundleName;            internal string variantName;            internal ABMoveData(string asset, string bundle, string variant)            {                assetName = asset;                bundleName = bundle;                variantName = variant;            }            internal void Apply()            {                if (!DataSource.IsReadOnly ())                {                    DataSource.SetAssetBundleNameAndVariant(assetName, bundleName, variantName);                }            }        }        internal static void MoveAssetToBundle(AssetInfo asset, string bundleName, string variant)        {            s_MoveData.Add(new ABMoveData(asset.fullAssetName, bundleName, variant));        }        internal static void MoveAssetToBundle(string assetName, string bundleName, string variant)        {            s_MoveData.Add(new ABMoveData(assetName, bundleName, variant));        }        internal static void MoveAssetToBundle(IEnumerable<AssetInfo> assets, string bundleName, string variant)        {            foreach (var asset in assets)                MoveAssetToBundle(asset, bundleName, variant);        }        internal static void MoveAssetToBundle(IEnumerable<string> assetNames, string bundleName, string variant)        {            foreach (var assetName in assetNames)                MoveAssetToBundle(assetName, bundleName, variant);        }        internal static void ExecuteAssetMove(bool forceAct=true)        {            var size = s_MoveData.Count;            if(forceAct)            {                if (size > 0)                {                    bool autoRefresh = EditorPrefs.GetBool("kAutoRefresh");                    EditorPrefs.SetBool("kAutoRefresh", false);                    EditorUtility.DisplayProgressBar("Moving assets to bundles", "", 0);                    for (int i = 0; i < size; i++)                    {                        EditorUtility.DisplayProgressBar("Moving assets to bundle " + s_MoveData[i].bundleName, System.IO.Path.GetFileNameWithoutExtension(s_MoveData[i].assetName), (float)i / (float)size);                        s_MoveData[i].Apply();                    }                    EditorUtility.ClearProgressBar();                    EditorPrefs.SetBool("kAutoRefresh", autoRefresh);                    s_MoveData.Clear();                }                if (!DataSource.IsReadOnly ())                {                    DataSource.RemoveUnusedAssetBundleNames();                }                Refresh();            }        }                //this version of CreateAsset is only used for dependent assets.         internal static AssetInfo CreateAsset(string name, AssetInfo parent)        {            if (ValidateAsset(name))            {                var bundleName = GetBundleName(name);                 return CreateAsset(name, bundleName, parent);            }            return null;        }        internal static AssetInfo CreateAsset(string name, string bundleName)        {            if(ValidateAsset(name))            {                return CreateAsset(name, bundleName, null);            }            return null;        }        private static AssetInfo CreateAsset(string name, string bundleName, AssetInfo parent)        {            if(!System.String.IsNullOrEmpty(bundleName))            {                return new AssetInfo(name, bundleName);            }            else            {                AssetInfo info = null;                if(!s_GlobalAssetList.TryGetValue(name, out info))                {                    info = new AssetInfo(name, string.Empty);                    s_GlobalAssetList.Add(name, info);                }                info.AddParent(parent.displayName);                return info;            }        }        internal static bool ValidateAsset(string name)        {            if (!name.StartsWith("Assets/"))                return false;            string ext = System.IO.Path.GetExtension(name);            if (ext == ".dll" || ext == ".cs" || ext == ".meta" || ext == ".js" || ext == ".boo")                return false;            return true;        }        internal static string GetBundleName(string asset)        {            return DataSource.GetAssetBundleName (asset);        }        internal static int RegisterAsset(AssetInfo asset, string bundle)        {            if(s_DependencyTracker.ContainsKey(asset.fullAssetName))            {                s_DependencyTracker[asset.fullAssetName].Add(bundle);                int count = s_DependencyTracker[asset.fullAssetName].Count;                if (count > 1)                    asset.SetMessageFlag(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles, true);                return count;            }            var bundles = new HashSet<string>();            bundles.Add(bundle);            s_DependencyTracker.Add(asset.fullAssetName, bundles);            return 1;                    }        internal static void UnRegisterAsset(AssetInfo asset, string bundle)        {            if (s_DependencyTracker == null || asset == null)                return;            if (s_DependencyTracker.ContainsKey(asset.fullAssetName))            {                s_DependencyTracker[asset.fullAssetName].Remove(bundle);                int count = s_DependencyTracker[asset.fullAssetName].Count;                switch (count)                {                    case 0:                        s_DependencyTracker.Remove(asset.fullAssetName);                        break;                    case 1:                        asset.SetMessageFlag(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles, false);                        break;                    default:                        break;                }            }        }        internal static IEnumerable<string> CheckDependencyTracker(AssetInfo asset)        {            if (s_DependencyTracker.ContainsKey(asset.fullAssetName))            {                return s_DependencyTracker[asset.fullAssetName];            }            return new HashSet<string>();        }                //TODO - switch local cache server on and utilize this method to stay up to date.        //static List<string> m_importedAssets = new List<string>();        //static List<string> m_deletedAssets = new List<string>();        //static List<KeyValuePair<string, string>> m_movedAssets = new List<KeyValuePair<string, string>>();        //class AssetBundleChangeListener : AssetPostprocessor        //{        //    static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)        //    {        //        m_importedAssets.AddRange(importedAssets);        //        m_deletedAssets.AddRange(deletedAssets);        //        for (int i = 0; i < movedAssets.Length; i++)        //            m_movedAssets.Add(new KeyValuePair<string, string>(movedFromAssetPaths[i], movedAssets[i]));        //        //m_dirty = true;        //    }        //}        static internal void LogError(string message)        {            Debug.LogError("AssetBundleBrowser: " + message);        }        static internal void LogWarning(string message)        {            Debug.LogWarning("AssetBundleBrowser: " + message);        }        static internal Texture2D GetFolderIcon()        {            if (s_folderIcon == null)                FindBundleIcons();            return s_folderIcon;        }        static internal Texture2D GetBundleIcon()        {            if (s_bundleIcon == null)                FindBundleIcons();            return s_bundleIcon;        }        static internal Texture2D GetSceneIcon()        {            if (s_sceneIcon == null)                FindBundleIcons();            return s_sceneIcon;        }        static private void FindBundleIcons()        {            s_folderIcon = EditorGUIUtility.FindTexture("Folder Icon");            var packagePath = System.IO.Path.GetFullPath("Packages/com.unity.assetbundlebrowser");            if (System.IO.Directory.Exists(packagePath))            {                s_bundleIcon = (Texture2D)AssetDatabase.LoadAssetAtPath("Packages/com.unity.assetbundlebrowser/Editor/Icons/ABundleBrowserIconY1756Basic.png", typeof(Texture2D));                s_sceneIcon = (Texture2D)AssetDatabase.LoadAssetAtPath("Packages/com.unity.assetbundlebrowser/Editor/Icons/ABundleBrowserIconY1756Scene.png", typeof(Texture2D));            }        }    }}
 |