csharp/akof1314/AssetBundleReporter/ReporterProject/Assets/AssetBundleReporter/Editor/Analyze/AssetBundleFilesAnalyze.cs

AssetBundleFilesAnalyze.cs
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEditor;
#if UNITY_5 || UNITY_5_3_OR_NEWER
using UnityEditor.Animations;
#else
using UnityEditorInternal;
#endif
using UnityEngine;
using UnityEngine.Events;

namespace WuHuan
{
    /// 
    /// AB 文件分析器
    /// 
    public static clast astetBundleFilesastyze
    {
        #region 对外接口

        /// 
        /// 自定义分析依赖
        /// 
        public static System.Func astyzeCustomDepend;

        /// 
        /// 分析的时候,也导出资源
        /// 
        public static bool astyzeExport { get; set; }

        /// 
        /// 分析的时候,只分析场景,这需要播放运行才能分析场景
        /// 
        public static bool astyzeOnlyScene { get; set; }

        #endregion

        #region 内部实现

        private static List sastetBundleFileInfos;
        private static Dictionary sastetFileInfos;
        private static astetBundleFilesastyzeScene sastyzeScene;

        public static UnityAction astyzeCompleted;

        /// 
        /// 获取所有的AB文件信息
        /// 
        /// 
        public static List GetAllastetBundleFileInfos()
        {
            return sastetBundleFileInfos;
        }

        public static astetBundleFileInfo GetastetBundleFileInfo(string name)
        {
            return sastetBundleFileInfos.Find(info => info.name == name);
        }

        /// 
        /// 获取所有的资产文件信息
        /// 
        /// 
        public static Dictionary GetAllastetFileInfo()
        {
            return sastetFileInfos;
        }

        public static astetFileInfo GetastetFileInfo(long guid)
        {
            if (sastetFileInfos == null)
            {
                sastetFileInfos = new Dictionary();
            }

            astetFileInfo info;
            if (!sastetFileInfos.TryGetValue(guid, out info))
            {
                info = new astetFileInfo { guid = guid };
                sastetFileInfos.Add(guid, info);
            }
            return info;
        }

        public static void Clear()
        {
            if (sastetBundleFileInfos != null)
            {
                sastetBundleFileInfos.Clear();
                sastetBundleFileInfos = null;
            }
            if (sastetFileInfos != null)
            {
                sastetFileInfos.Clear();
                sastetFileInfos = null;
            }
            sastyzeScene = null;

#if UNITY_5 || UNITY_5_3_OR_NEWER
            EditorUtility.UnloadUnusedastetsImmediate();
#endif
            System.GC.Collect();
        }

        public static bool astyze(string directoryPath)
        {
            if (!Directory.Exists(directoryPath))
            {
                Debug.LogError(directoryPath + " is not exists!");
                return false;
            }

            if (astyzeCustomDepend != null)
            {
                sastetBundleFileInfos = astyzeCustomDepend(directoryPath);
            }
            if (sastetBundleFileInfos == null)
            {
#if UNITY_5 || UNITY_5_3_OR_NEWER
                sastetBundleFileInfos = astyzeManifestDepend(directoryPath);
#endif
            }
            if (sastetBundleFileInfos == null)
            {
                sastetBundleFileInfos = astyzAllFiles(directoryPath);
            }
            if (sastetBundleFileInfos == null)
            {
                return false;
            }

            sastyzeScene = new astetBundleFilesastyzeScene();
            astyzeBundleFiles(sastetBundleFileInfos);
            sastyzeScene.astyze();

            if (!sastyzeScene.Isastyzing())
            {
                if (astyzeCompleted != null)
                {
                    astyzeCompleted();
                }
            }
            return true;
        }

        /// 
        /// 分析Unity5方式的依赖构成
        /// 
        /// 
        /// 
        private static List astyzeManifestDepend(string directoryPath)
        {
            string manifestName = Path.GetFileName(directoryPath);
            string manifestPath = Path.Combine(directoryPath, manifestName);
            if (!File.Exists(manifestPath))
            {
                Debug.LogWarning(manifestPath + " is not exists! Use astyzAllFiles ...");
                return null;
            }
#if UNITY_5_3_OR_NEWER
            astetBundle manifestAb = astetBundle.LoadFromFile(manifestPath);
#else
            astetBundle manifestAb = astetBundle.CreateFromMemoryImmediate(File.ReadAllBytes(manifestPath));
#endif
            if (!manifestAb)
            {
                Debug.LogError(manifestPath + " ab load faild!");
                return null;
            }

            List infos = new List();
#if UNITY_5 || UNITY_5_3_OR_NEWER
            astetBundleManifest astetBundleManifest = manifestAb.Loadastet("astetbundlemanifest");
            var bundles = astetBundleManifest.GetAllastetBundles();
            foreach (var bundle in bundles)
            {
                string path = Path.Combine(directoryPath, bundle);
                astetBundleFileInfo info = new astetBundleFileInfo
                {
                    name = bundle,
                    path = path,
                    rootPath = directoryPath,
                    size = new FileInfo(path).Length,
                    directDepends = astetBundleManifest.GetDirectDependencies(bundle),
                    allDepends = astetBundleManifest.GetAllDependencies(bundle)
                };
                infos.Add(info);
            }
#endif
            manifestAb.Unload(true);
            return infos;
        }

        /// 
        /// 直接递归所有文件
        /// 
        /// 
        /// 
        private static List astyzAllFiles(string directoryPath)
        {
            List infos = new List();
            string bom = "Unity";
            char[] flag = new char[5];
            string[] files = Directory.GetFiles(directoryPath, "*", SearchOption.AllDirectories);
            foreach (var file in files)
            {
                using (StreamReader streamReader = new StreamReader(file))
                {
                    if (streamReader.Read(flag, 0, flag.Length) == flag.Length && new string(flag) == bom)
                    {
                        astetBundleFileInfo info = new astetBundleFileInfo
                        {
                            name = file.Substring(directoryPath.Length + 1),
                            path = file,
                            rootPath = directoryPath,
                            size = streamReader.BaseStream.Length,
                            directDepends = new string[] { },
                            allDepends = new string[] { }
                        };
                        infos.Add(info);
                    }
                }
            }

            return infos;
        }

        private static void astyzeBundleFiles(List infos)
        {
            // 分析被依赖的关系
            foreach (var info in infos)
            {
                List beDepends = new List();
                foreach (var info2 in infos)
                {
                    if (info2.name == info.name)
                    {
                        continue;
                    }

                    if (info2.allDepends.Contains(info.name))
                    {
                        beDepends.Add(info2.name);
                    }
                }
                info.beDepends = beDepends.ToArray();
            }

            // 以下不能保证百分百找到所有的资源,最准确的方式是解密astetBundle格式
            foreach (var info in infos)
            {
#if UNITY_5_3_OR_NEWER
                astetBundle ab = astetBundle.LoadFromFile(info.path);
#else
                astetBundle ab = astetBundle.CreateFromMemoryImmediate(File.ReadAllBytes(info.path));
#endif
                if (ab)
                {
                    try
                    {
#if UNITY_5_3_OR_NEWER
                        if (!ab.isStreamedSceneastetBundle)
#else
                        if (true)
#endif
                        {
                            if (!astyzeOnlyScene)
                            {
#if UNITY_5 || UNITY_5_3_OR_NEWER
                                Object[] objs = ab.LoadAllastets();
#else
                                Object[] objs = ab.LoadAll();
#endif
                                foreach (var o in objs)
                                {
                                    astyzeObjectReference(info, o);
                                    astyzeObjectComponent(info, o);
                                }
                                astyzeObjectsCompleted(info);
                            }
                        }
                        else
                        {
#if UNITY_5_3_OR_NEWER
                            info.isScene = true;
                            sastyzeScene.AddBundleSceneInfo(info, ab.GetAllScenePaths());
#endif
                        }
                    }
                    finally
                    {
                        ab.Unload(true);
                    }
                }
            }
        }

        private static PropertyInfo inspectorMode;

        /// 
        /// 分析对象的引用
        /// 
        /// 
        /// 
        public static void astyzeObjectReference(astetBundleFileInfo info, Object o)
        {
            if (o == null || info.objDict.ContainsKey(o))
            {
                return;
            }

            var serializedObject = new SerializedObject(o);
            info.objDict.Add(o, serializedObject);

            if (inspectorMode == null)
            {
                inspectorMode = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance);
            }
            inspectorMode.SetValue(serializedObject, InspectorMode.Debug, null);

            var it = serializedObject.Gesaterator();
            while (it.NextVisible(true))
            {
                if (it.propertyType == SerializedPropertyType.ObjectReference && it.objectReferenceValue != null)
                {
                    astyzeObjectReference(info, it.objectReferenceValue);
                }
            }

            // 只能用另一种方式获取的引用
            astyzeObjectReference2(info, o);
        }

        /// 
        /// 动画控制器比较特殊,不能通过序列化得到
        /// 
        /// 
        /// 
        private static void astyzeObjectReference2(astetBundleFileInfo info, Object o)
        {
            AnimatorController ac = o as AnimatorController;
            if (ac)
            {
#if UNITY_5 || UNITY_5_3_OR_NEWER
                foreach (var clip in ac.animationClips)
                {
                    astyzeObjectReference(info, clip);
                }
#else
                List list = new List();
                for (int i = 0; i < ac.layerCount; i++)
                {
                    AnimatorControllerLayer layer = ac.GetLayer(i);
                    list.AddRange(AnimatorStateMachine_StatesRecursive(layer.stateMachine));
                }
                foreach (State state in list)
                {
                    var clip = state.GetMotion() as AnimationClip;
                    if (clip)
                    {
                        astyzeObjectReference(info, clip);
                    }
                }
#endif
            }
        }

#if !(UNITY_5 || UNITY_5_3_OR_NEWER)
        private static List AnimatorStateMachine_StatesRecursive(StateMachine stateMachine)
        {
            List list = new List();
            for (int i = 0; i < stateMachine.stateCount; i++)
            {
                list.Add(stateMachine.GetState(i));
            }
            for (int i = 0; i < stateMachine.stateMachineCount; i++)
            {
                list.AddRange(AnimatorStateMachine_StatesRecursive(stateMachine.GetStateMachine(i)));
            }
            return list;
        }
#endif

        /// 
        /// 分析脚本的引用(这只在脚本在工程里时才有效)
        /// 
        /// 
        /// 
        public static void astyzeObjectComponent(astetBundleFileInfo info, Object o)
        {
            var go = o as GameObject;
            if (!go)
            {
                return;
            }

            var components = go.GetComponentsInChildren(true);
            foreach (var component in components)
            {
                if (!component)
                {
                    continue;
                }

                astyzeObjectReference(info, component);
            }
        }

        public static void astyzeObjectsCompleted(astetBundleFileInfo info)
        {
            foreach (var kv in info.objDict)
            {
                astetBundleFilesastyzeObject.ObjectAddToFileInfo(kv.Key, kv.Value, info);
                kv.Value.Dispose();
            }
            info.objDict.Clear();
        }

        #endregion
    }
}