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
}
}