NLDClient-yudde/ProjectNLD/Assets/ThirdParty/Behavior Designer/Runtime/BinaryDeserialization.cs

972 lines
49 KiB
C#
Executable File

using UnityEngine;
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
#if NETFX_CORE && !UNITY_EDITOR
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
#else
using System.Security.Cryptography;
#endif
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
public static class BinaryDeserialization
{
private static GlobalVariables globalVariables = null;
private class ObjectFieldMap
{
public ObjectFieldMap(object o, FieldInfo f) { obj = o; fieldInfo = f; }
public object obj;
public FieldInfo fieldInfo;
}
private class ObjectFieldMapComparer : IEqualityComparer<ObjectFieldMap>
{
public bool Equals(ObjectFieldMap a, ObjectFieldMap b)
{
if (ReferenceEquals(a, null)) return false;
if (ReferenceEquals(b, null)) return false;
return a.obj.Equals(b.obj) && a.fieldInfo.Equals(b.fieldInfo);
}
public int GetHashCode(ObjectFieldMap a)
{
return a != null ? (a.obj.ToString().GetHashCode() + a.fieldInfo.ToString().GetHashCode()) : 0;
}
}
private static Dictionary<ObjectFieldMap, List<int>> taskIDs = null;
#if !NETFX_CORE || UNITY_EDITOR
private static SHA1 shaHash;
#endif
private static bool updatedSerialization; // 1.5.7
private static bool shaHashSerialization; // 1.5.9
private static bool strHashSerialization; // 1.5.11
private static int animationCurveAdvance = 20; // 1.5.12
private static bool enumSerialization; // 1.6.4
private static Dictionary<uint, string> stringCache = new Dictionary<uint, string>();
private static byte[] sBigEndianFourByteArray;
private static byte[] sBigEndianEightByteArray;
private static byte[] BigEndianFourByteArray { get { if (sBigEndianFourByteArray == null) { sBigEndianFourByteArray = new byte[4]; } return sBigEndianFourByteArray; } set { sBigEndianFourByteArray = value; } }
private static byte[] BigEndianEightByteArray { get { if (sBigEndianEightByteArray == null) { sBigEndianEightByteArray = new byte[8]; } return sBigEndianEightByteArray; } set { sBigEndianEightByteArray = value; } }
private static uint[] crcTable = new uint[]{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
public static void Load(BehaviorSource behaviorSource)
{
Load(behaviorSource.TaskData, behaviorSource, true);
}
public static void Load(TaskSerializationData taskData, BehaviorSource behaviorSource, bool loadTasks)
{
behaviorSource.EntryTask = null;
behaviorSource.RootTask = null;
behaviorSource.DetachedTasks = null;
behaviorSource.Variables = null;
var taskSerializationData = taskData;
FieldSerializationData fieldSerializationData;
if (taskSerializationData == null ||
((fieldSerializationData = taskSerializationData.fieldSerializationData).byteData == null || fieldSerializationData.byteData.Count == 0) &&
(fieldSerializationData.byteDataArray == null || fieldSerializationData.byteDataArray.Length == 0)) {
return;
}
if (fieldSerializationData.byteData != null && fieldSerializationData.byteData.Count > 0) {
fieldSerializationData.byteDataArray = fieldSerializationData.byteData.ToArray();
}
taskIDs = null;
var treeVersion = new Version(taskData.Version);
updatedSerialization = treeVersion.CompareTo(new Version("1.5.7")) >= 0;
enumSerialization = shaHashSerialization = strHashSerialization = false;
if (updatedSerialization) {
shaHashSerialization = treeVersion.CompareTo(new Version("1.5.9")) >= 0;
if (shaHashSerialization) {
strHashSerialization = treeVersion.CompareTo(new Version("1.5.11")) >= 0;
if (strHashSerialization) {
animationCurveAdvance = treeVersion.CompareTo(new Version("1.5.12")) >= 0 ? 16 : 20;
enumSerialization = treeVersion.CompareTo(new Version("1.6.4")) >= 0;
}
}
}
if (taskSerializationData.variableStartIndex != null) {
var variables = new List<SharedVariable>();
var fieldIndexMap = ObjectPool.Get<Dictionary<int, int>>();
for (int i = 0; i < taskSerializationData.variableStartIndex.Count; ++i) {
int startIndex = taskSerializationData.variableStartIndex[i];
int endIndex;
if (i + 1 < taskSerializationData.variableStartIndex.Count) {
endIndex = taskSerializationData.variableStartIndex[i + 1];
} else {
// tasks are added after the variables
if (taskSerializationData.startIndex != null && taskSerializationData.startIndex.Count > 0) {
endIndex = taskSerializationData.startIndex[0];
} else {
endIndex = fieldSerializationData.startIndex.Count;
}
}
// build a dictionary based off of the saved fields
fieldIndexMap.Clear();
for (int j = startIndex; j < endIndex; ++j) {
fieldIndexMap.Add(fieldSerializationData.fieldNameHash[j], fieldSerializationData.startIndex[j]);
}
var sharedVariable = BytesToSharedVariable(fieldSerializationData, fieldIndexMap, fieldSerializationData.byteDataArray, taskSerializationData.variableStartIndex[i], behaviorSource, false, 0);
if (sharedVariable != null) {
variables.Add(sharedVariable);
}
}
ObjectPool.Return(fieldIndexMap);
behaviorSource.Variables = variables;
}
// Local behavior trees do not need to consume external behavior tree tasks at edit time.
if (!loadTasks) {
return;
}
var taskList = new List<Task>();
if (taskSerializationData.types != null) {
for (int i = 0; i < taskSerializationData.types.Count; ++i) {
LoadTask(taskSerializationData, fieldSerializationData, ref taskList, ref behaviorSource);
}
}
// determine where the tasks are positioned
if (taskSerializationData.parentIndex.Count != taskList.Count) {
Debug.LogError("Deserialization Error: parent index count does not match task list count");
return;
}
// Determine where the task is positioned
for (int i = 0; i < taskSerializationData.parentIndex.Count; ++i) {
if (taskSerializationData.parentIndex[i] == -1) {
if (behaviorSource.EntryTask == null) { // the first task is always the entry task
behaviorSource.EntryTask = taskList[i];
} else {
if (behaviorSource.DetachedTasks == null) {
behaviorSource.DetachedTasks = new List<Task>();
}
behaviorSource.DetachedTasks.Add(taskList[i]);
}
} else if (taskSerializationData.parentIndex[i] == 0) { // if the parent is the entry task then assign it as the root task. The entry task isn't a "real" parent task
behaviorSource.RootTask = taskList[i];
} else {
// Add the child to the parent (if the parent index isn't -1)
if (taskSerializationData.parentIndex[i] != -1) {
var parentTask = taskList[taskSerializationData.parentIndex[i]] as ParentTask;
if (parentTask != null) {
var childIndex = parentTask.Children == null ? 0 : parentTask.Children.Count;
parentTask.AddChild(taskList[i], childIndex);
}
}
}
}
if (taskIDs != null) {
foreach (var objFieldMap in taskIDs.Keys) {
var ids = taskIDs[objFieldMap] as List<int>;
var fieldType = objFieldMap.fieldInfo.FieldType;
if (typeof(IList).IsAssignableFrom(fieldType)) { // array
if (fieldType.IsArray) {
var elementType = fieldType.GetElementType();
var idCount = 0; // The task may be null.
for (int i = 0; i < ids.Count; ++i) {
var task = taskList[ids[i]];
if (!elementType.IsAssignableFrom(task.GetType())) {
continue;
}
idCount++;
}
var insertIndex = 0;
var objectArray = Array.CreateInstance(elementType, idCount);
for (int i = 0; i < objectArray.Length; ++i) {
var task = taskList[ids[i]];
if (!elementType.IsAssignableFrom(task.GetType())) {
continue;
}
objectArray.SetValue(task, insertIndex);
insertIndex++;
}
objFieldMap.fieldInfo.SetValue(objFieldMap.obj, objectArray);
} else {
var elementType = fieldType.GetGenericArguments()[0];
var objectList = TaskUtility.CreateInstance(typeof(List<>).MakeGenericType(elementType)) as IList;
for (int i = 0; i < ids.Count; ++i) {
var task = taskList[ids[i]];
if (!elementType.IsAssignableFrom(task.GetType())) {
continue;
}
objectList.Add(task);
}
objFieldMap.fieldInfo.SetValue(objFieldMap.obj, objectList);
}
} else if (taskList.Count > ids[0]) {
objFieldMap.fieldInfo.SetValue(objFieldMap.obj, taskList[ids[0]]);
}
}
}
}
public static void Load(GlobalVariables globalVariables, string version)
{
if (globalVariables == null) {
return;
}
globalVariables.Variables = null;
FieldSerializationData fieldSerializationData;
if (globalVariables.VariableData == null ||
((fieldSerializationData = globalVariables.VariableData.fieldSerializationData).byteData == null || fieldSerializationData.byteData.Count == 0) &&
(fieldSerializationData.byteDataArray == null || fieldSerializationData.byteDataArray.Length == 0)) {
return;
}
var variableData = globalVariables.VariableData;
if (fieldSerializationData.byteData != null && fieldSerializationData.byteData.Count > 0) {
fieldSerializationData.byteDataArray = fieldSerializationData.byteData.ToArray();
}
var variableVersion = new Version(globalVariables.Version);
updatedSerialization = variableVersion.CompareTo(new Version("1.5.7")) >= 0;
enumSerialization = shaHashSerialization = strHashSerialization = false;
if (updatedSerialization) {
shaHashSerialization = variableVersion.CompareTo(new Version("1.5.9")) >= 0;
if (shaHashSerialization) {
strHashSerialization = variableVersion.CompareTo(new Version("1.5.11")) >= 0;
if (strHashSerialization) {
animationCurveAdvance = variableVersion.CompareTo(new Version("1.5.12")) >= 0 ? 16 : 20;
enumSerialization = variableVersion.CompareTo(new Version("1.6.4")) >= 0;
}
}
}
if (variableData.variableStartIndex != null) {
var variables = new List<SharedVariable>();
var fieldIndexMap = ObjectPool.Get<Dictionary<int, int>>();
for (int i = 0; i < variableData.variableStartIndex.Count; ++i) {
int startIndex = variableData.variableStartIndex[i];
int endIndex;
if (i + 1 < variableData.variableStartIndex.Count) {
endIndex = variableData.variableStartIndex[i + 1];
} else {
endIndex = fieldSerializationData.startIndex.Count;
}
// build a dictionary based off of the saved fields
fieldIndexMap.Clear();
for (int j = startIndex; j < endIndex; ++j) {
fieldIndexMap.Add(fieldSerializationData.fieldNameHash[j], fieldSerializationData.startIndex[j]);
}
var sharedVariable = BytesToSharedVariable(fieldSerializationData, fieldIndexMap, fieldSerializationData.byteDataArray, variableData.variableStartIndex[i], globalVariables, false, 0);
if (sharedVariable != null) {
variables.Add(sharedVariable);
}
}
ObjectPool.Return(fieldIndexMap);
globalVariables.Variables = variables;
}
}
public static void LoadTask(TaskSerializationData taskSerializationData, FieldSerializationData fieldSerializationData, ref List<Task> taskList, ref BehaviorSource behaviorSource)
{
int taskIndex = taskList.Count;
int startIndex = taskSerializationData.startIndex[taskIndex];
int endIndex;
if (taskIndex + 1 < taskSerializationData.startIndex.Count) {
endIndex = taskSerializationData.startIndex[taskIndex + 1];
} else {
endIndex = fieldSerializationData.startIndex.Count;
}
// build a dictionary based off of the saved fields
var fieldIndexMap = ObjectPool.Get<Dictionary<int, int>>();
fieldIndexMap.Clear();
for (int i = startIndex; i < endIndex; ++i) {
if (fieldIndexMap.ContainsKey(fieldSerializationData.fieldNameHash[i])) {
continue;
}
fieldIndexMap.Add(fieldSerializationData.fieldNameHash[i], fieldSerializationData.startIndex[i]);
}
Task task = null;
var type = TaskUtility.GetTypeWithinAssembly(taskSerializationData.types[taskIndex]);
// Change the type to an unknown type if the type doesn't exist anymore.
if (type == null) {
bool isUnknownParent = false;
for (int i = 0; i < taskSerializationData.parentIndex.Count; ++i) {
if (taskIndex == taskSerializationData.parentIndex[i]) {
isUnknownParent = true;
break;
}
}
if (isUnknownParent) {
type = typeof(UnknownParentTask);
} else {
type = typeof(UnknownTask);
}
}
task = TaskUtility.CreateInstance(type) as Task;
if (task is UnknownTask) {
var unknownTask = task as UnknownTask;
for (int i = startIndex; i < endIndex; ++i) {
unknownTask.fieldNameHash.Add(fieldSerializationData.fieldNameHash[i]);
unknownTask.startIndex.Add(fieldSerializationData.startIndex[i] - fieldSerializationData.startIndex[startIndex]);
}
for (int i = fieldSerializationData.startIndex[startIndex]; i <= fieldSerializationData.startIndex[endIndex - 1]; ++i) {
unknownTask.dataPosition.Add(fieldSerializationData.dataPosition[i] - fieldSerializationData.dataPosition[fieldSerializationData.startIndex[startIndex]]);
}
if (taskIndex + 1 < taskSerializationData.startIndex.Count && taskSerializationData.startIndex[taskIndex + 1] < fieldSerializationData.dataPosition.Count) {
endIndex = fieldSerializationData.dataPosition[taskSerializationData.startIndex[taskIndex + 1]];
} else {
endIndex = fieldSerializationData.byteDataArray.Length;
}
for (int i = fieldSerializationData.dataPosition[fieldSerializationData.startIndex[startIndex]]; i < endIndex; ++i) {
unknownTask.byteData.Add(fieldSerializationData.byteDataArray[i]);
}
unknownTask.unityObjects = fieldSerializationData.unityObjects;
}
task.Owner = behaviorSource.Owner.GetObject() as Behavior;
taskList.Add(task);
task.ID = (int)LoadField(fieldSerializationData, fieldIndexMap, typeof(int), "ID", 0, null);
task.FriendlyName = LoadField(fieldSerializationData, fieldIndexMap, typeof(string), "FriendlyName", 0, null) as string;
task.IsInstant = (bool)LoadField(fieldSerializationData, fieldIndexMap, typeof(bool), "IsInstant", 0, null);
object disabled;
if ((disabled = LoadField(fieldSerializationData, fieldIndexMap, typeof(bool), "Disabled", 0, null)) != null) {
task.Disabled = (bool)disabled;
}
#if UNITY_EDITOR || DLL_DEBUG || DLL_RELEASE
LoadNodeData(fieldSerializationData, fieldIndexMap, taskList[taskIndex]);
// give a little warning if the task is an unknown type
if (task.GetType().Equals(typeof(UnknownTask)) || task.GetType().Equals(typeof(UnknownParentTask))) {
if (!task.FriendlyName.Contains("Unknown ")) {
task.FriendlyName = string.Format("Unknown {0}", task.FriendlyName);
}
task.NodeData.Comment = "Unknown Task. Right click and Replace to locate new task.";
}
#endif
LoadFields(fieldSerializationData, fieldIndexMap, taskList[taskIndex], 0, behaviorSource);
ObjectPool.Return(fieldIndexMap);
}
#if UNITY_EDITOR || DLL_DEBUG || DLL_RELEASE
private static void LoadNodeData(FieldSerializationData fieldSerializationData, Dictionary<int, int> fieldIndexMap, Task task)
{
var nodeData = new NodeData();
nodeData.Offset = (Vector2)LoadField(fieldSerializationData, fieldIndexMap, typeof(Vector2), "NodeDataOffset", 0, null);
nodeData.Comment = LoadField(fieldSerializationData, fieldIndexMap, typeof(string), "NodeDataComment", 0, null) as string;
nodeData.IsBreakpoint = (bool)LoadField(fieldSerializationData, fieldIndexMap, typeof(bool), "NodeDataIsBreakpoint", 0, null);
nodeData.Collapsed = (bool)LoadField(fieldSerializationData, fieldIndexMap, typeof(bool), "NodeDataCollapsed", 0, null);
var value = LoadField(fieldSerializationData, fieldIndexMap, typeof(int), "NodeDataColorIndex", 0, null);
if (value != null) {
nodeData.ColorIndex = (int)value;
}
value = LoadField(fieldSerializationData, fieldIndexMap, typeof(List<string>), "NodeDataWatchedFields", 0, null);
if (value != null) {
nodeData.WatchedFieldNames = new List<string>();
nodeData.WatchedFields = new List<FieldInfo>();
var objectValues = value as IList;
for (int i = 0; i < objectValues.Count; ++i) {
var field = task.GetType().GetField((string)objectValues[i], BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null) {
nodeData.WatchedFieldNames.Add(field.Name);
nodeData.WatchedFields.Add(field);
}
}
}
task.NodeData = nodeData;
}
#endif
private static void LoadFields(FieldSerializationData fieldSerializationData, Dictionary<int, int> fieldIndexMap, object obj, int hashPrefix, IVariableSource variableSource)
{
var fields = TaskUtility.GetSerializableFields(obj.GetType());
for (int i = 0; i < fields.Length; ++i) {
// there are a variety of reasons why we can't deserialize a field
if (TaskUtility.HasAttribute(fields[i], typeof(NonSerializedAttribute)) ||
((fields[i].IsPrivate || fields[i].IsFamily) && !TaskUtility.HasAttribute(fields[i], typeof(SerializeField))) ||
(obj is ParentTask) && fields[i].Name.Equals("children")) {
continue;
}
var value = LoadField(fieldSerializationData, fieldIndexMap, fields[i].FieldType, fields[i].Name, hashPrefix, variableSource, obj, fields[i]);
if (value != null && !ReferenceEquals(value, null) && !value.Equals(null) && fields[i].FieldType.IsAssignableFrom(value.GetType())) {
fields[i].SetValue(obj, value);
}
}
}
private static object LoadField(FieldSerializationData fieldSerializationData, Dictionary<int, int> fieldIndexMap, Type fieldType, string fieldName, int hashPrefix, IVariableSource variableSource, object obj = null, FieldInfo fieldInfo = null)
{
var containsFieldHash = false;
int fieldIndex;
var fieldHash = hashPrefix;
if (shaHashSerialization) {
fieldHash += StringHash(fieldType.Name.ToString(), strHashSerialization) + StringHash(fieldName, strHashSerialization);
} else {
fieldHash += fieldType.Name.GetHashCode() + fieldName.GetHashCode();
}
containsFieldHash = fieldIndexMap.TryGetValue(fieldHash, out fieldIndex);
if (!containsFieldHash && fieldInfo != null) {
UnityEngine.Serialization.FormerlySerializedAsAttribute formallySerializedAs;
if ((formallySerializedAs = fieldInfo.GetCustomAttribute(typeof(UnityEngine.Serialization.FormerlySerializedAsAttribute), true) as UnityEngine.Serialization.FormerlySerializedAsAttribute) != null) {
fieldHash = hashPrefix + StringHash(fieldType.Name.ToString(), strHashSerialization) + StringHash(formallySerializedAs.oldName, strHashSerialization);
containsFieldHash = fieldIndexMap.TryGetValue(fieldHash, out fieldIndex);
}
}
if (!containsFieldHash) {
#if NETFX_CORE && !UNITY_EDITOR
if (fieldType.GetTypeInfo().IsAbstract) {
#else
if (fieldType.IsAbstract) {
#endif
return null;
}
if (typeof(SharedVariable).IsAssignableFrom(fieldType)) {
var sharedVariable = TaskUtility.CreateInstance(fieldType) as SharedVariable;
var sharedVariableValue = (fieldInfo.GetValue(obj) as SharedVariable);
if (sharedVariableValue != null) {
sharedVariable.SetValue(sharedVariableValue.GetValue());
}
return sharedVariable;
}
return null;
}
object value = null;
if (typeof(IList).IsAssignableFrom(fieldType)) { // array
int elementCount = BytesToInt(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
if (fieldType.IsArray) {
var elementType = fieldType.GetElementType();
if (elementType == null) {
return null;
}
var objectArray = Array.CreateInstance(elementType, elementCount);
for (int i = 0; i < elementCount; ++i) {
var objectValue = LoadField(fieldSerializationData, fieldIndexMap, elementType, i.ToString(), fieldHash / (updatedSerialization ? (i + 1) : 1), variableSource, obj, fieldInfo);
objectArray.SetValue((ReferenceEquals(objectValue, null) || objectValue.Equals(null)) ? null : objectValue, i);
}
value = objectArray;
} else {
var baseFieldType = fieldType;
#if NETFX_CORE && !UNITY_EDITOR
while (!baseFieldType.IsGenericType()) {
baseFieldType = baseFieldType.BaseType();
}
#else
while (!baseFieldType.IsGenericType) {
baseFieldType = baseFieldType.BaseType;
}
#endif
var elementType = baseFieldType.GetGenericArguments()[0];
IList objectList;
#if NETFX_CORE && !UNITY_EDITOR
if (fieldType.IsGenericType()) {
#else
if (fieldType.IsGenericType) {
#endif
objectList = TaskUtility.CreateInstance(typeof(List<>).MakeGenericType(elementType)) as IList;
} else {
objectList = TaskUtility.CreateInstance(fieldType) as IList;
}
for (int i = 0; i < elementCount; ++i) {
var objectValue = LoadField(fieldSerializationData, fieldIndexMap, elementType, i.ToString(), fieldHash / (updatedSerialization ? (i + 1) : 1), variableSource, obj, fieldInfo);
objectList.Add((ReferenceEquals(objectValue, null) || objectValue.Equals(null)) ? null : objectValue);
}
value = objectList;
}
} else if (typeof(Task).IsAssignableFrom(fieldType)) {
if (fieldInfo != null && TaskUtility.HasAttribute(fieldInfo, typeof(InspectTaskAttribute))) {
var taskTypeName = BytesToString(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex], GetFieldSize(fieldSerializationData, fieldIndex));
if (!string.IsNullOrEmpty(taskTypeName)) {
var taskType = TaskUtility.GetTypeWithinAssembly(taskTypeName);
if (taskType != null) {
value = TaskUtility.CreateInstance(taskType);
LoadFields(fieldSerializationData, fieldIndexMap, value, fieldHash, variableSource);
}
}
} else { // restore the task ids
if (taskIDs == null) {
taskIDs = new Dictionary<ObjectFieldMap, List<int>>(new ObjectFieldMapComparer());
}
int taskID = BytesToInt(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
// Add the task id
var map = new ObjectFieldMap(obj, fieldInfo);
if (taskIDs.ContainsKey(map)) {
taskIDs[map].Add(taskID);
} else {
var taskIDList = new List<int>();
taskIDList.Add(taskID);
taskIDs.Add(map, taskIDList);
}
}
} else if (typeof(SharedVariable).IsAssignableFrom(fieldType)) {
value = BytesToSharedVariable(fieldSerializationData, fieldIndexMap, fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex], variableSource, true, fieldHash);
} else if (typeof(UnityEngine.Object).IsAssignableFrom(fieldType)) {
int unityObjectIndex = BytesToInt(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
value = IndexToUnityObject(unityObjectIndex, fieldSerializationData);
#if !UNITY_EDITOR && NETFX_CORE
} else if (fieldType.Equals(typeof(int)) || (!enumSerialization && fieldType.GetTypeInfo().IsEnum)) {
#else
} else if (fieldType.Equals(typeof(int)) || (!enumSerialization && fieldType.IsEnum)) {
#endif
value = BytesToInt(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
if (fieldType.IsEnum) {
value = Enum.ToObject(fieldType, value);
}
#if !UNITY_EDITOR && NETFX_CORE
} else if (fieldType.GetTypeInfo().IsEnum) {
#else
} else if (fieldType.IsEnum) {
#endif
value = Enum.ToObject(fieldType, LoadField(fieldSerializationData, fieldIndexMap, Enum.GetUnderlyingType(fieldType), fieldName, fieldHash, variableSource, obj, fieldInfo));
} else if (fieldType.Equals(typeof(uint))) {
value = BytesToUInt(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(UInt64)) || fieldType.Equals(typeof(ulong))) {
value = BytesToULong(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(ushort))) {
value = BytesToUShort(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(float))) {
value = BytesToFloat(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(double))) {
value = BytesToDouble(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(long))) {
value = BytesToLong(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(ulong))) {
value = BytesToULong(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(bool))) {
value = BytesToBool(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(string))) {
value = BytesToString(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex], GetFieldSize(fieldSerializationData, fieldIndex));
} else if (fieldType.Equals(typeof(byte))) {
value = BytesToByte(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(Vector2))) {
value = BytesToVector2(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(Vector2Int))) {
value = BytesToVector2Int(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(Vector3))) {
value = BytesToVector3(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(Vector3Int))) {
value = BytesToVector3Int(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(Vector4))) {
value = BytesToVector4(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(Quaternion))) {
value = BytesToQuaternion(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(Color))) {
value = BytesToColor(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(Rect))) {
value = BytesToRect(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(Matrix4x4))) {
value = BytesToMatrix4x4(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(AnimationCurve))) {
value = BytesToAnimationCurve(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
} else if (fieldType.Equals(typeof(LayerMask))) {
value = BytesToLayerMask(fieldSerializationData.byteDataArray, fieldSerializationData.dataPosition[fieldIndex]);
#if !UNITY_EDITOR && NETFX_CORE
} else if (fieldType.GetTypeInfo().IsClass) {
#else
} else if (fieldType.IsClass || (fieldType.IsValueType && !fieldType.IsPrimitive)) {
#endif
value = TaskUtility.CreateInstance(fieldType);
LoadFields(fieldSerializationData, fieldIndexMap, value, fieldHash, variableSource);
return value;
}
return value;
}
public static int StringHash(string value, bool fastHash)
{
if (String.IsNullOrEmpty(value)) {
return 0;
}
if (fastHash) { // 1.5.11 and later, from https://stackoverflow.com/questions/5154970/how-do-i-create-a-hashcode-in-net-c-for-a-string-that-is-safe-to-store-in-a
var hash = 23;
var length = value.Length;
for (int i = 0; i < length; ++i) {
hash = hash * 31 + value[i];
}
return hash;
} else { // Pre 1.5.11
#if NETFX_CORE && !UNITY_EDITOR
var valueData = CryptographicBuffer.ConvertStringToBinary(value, BinaryStringEncoding.Utf8);
var shaHash = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha1);
var hashBuffer = shaHash.HashData(valueData);
byte[] hash;
CryptographicBuffer.CopyToByteArray(hashBuffer, out hash);
#else
var valueData = Encoding.UTF8.GetBytes(value);
if (shaHash == null) {
shaHash = new SHA1Managed();
}
var hash = shaHash.ComputeHash(valueData);
#endif
return BitConverter.ToInt32(hash, 0);
}
}
private static int GetFieldSize(FieldSerializationData fieldSerializationData, int fieldIndex)
{
return (fieldIndex + 1 < fieldSerializationData.dataPosition.Count ? fieldSerializationData.dataPosition[fieldIndex + 1] : fieldSerializationData.byteDataArray.Length) - fieldSerializationData.dataPosition[fieldIndex];
}
private static int BytesToInt(byte[] bytes, int dataPosition)
{
if (!BitConverter.IsLittleEndian) {
Array.Copy(bytes, dataPosition, BigEndianFourByteArray, 0, 4);
Array.Reverse(BigEndianFourByteArray);
return BitConverter.ToInt32(BigEndianFourByteArray, 0);
}
return BitConverter.ToInt32(bytes, dataPosition);
}
private static uint BytesToUInt(byte[] bytes, int dataPosition)
{
if (!BitConverter.IsLittleEndian) {
Array.Copy(bytes, dataPosition, BigEndianFourByteArray, 0, 4);
Array.Reverse(BigEndianFourByteArray);
return BitConverter.ToUInt32(BigEndianFourByteArray, 0);
}
return BitConverter.ToUInt32(bytes, dataPosition);
}
private static ulong BytesToULong(byte[] bytes, int dataPosition)
{
if (!BitConverter.IsLittleEndian) {
Array.Copy(bytes, dataPosition, BigEndianEightByteArray, 0, 8);
Array.Reverse(BigEndianEightByteArray);
return BitConverter.ToUInt64(BigEndianEightByteArray, 0);
}
return BitConverter.ToUInt64(bytes, dataPosition);
}
private static ushort BytesToUShort(byte[] bytes, int dataPosition)
{
if (!BitConverter.IsLittleEndian) {
Array.Copy(bytes, dataPosition, BigEndianFourByteArray, 0, 4);
Array.Reverse(BigEndianFourByteArray);
return BitConverter.ToUInt16(BigEndianFourByteArray, 0);
}
return BitConverter.ToUInt16(bytes, dataPosition);
}
private static float BytesToFloat(byte[] bytes, int dataPosition)
{
if (!BitConverter.IsLittleEndian) {
Array.Copy(bytes, dataPosition, BigEndianFourByteArray, 0, 4);
Array.Reverse(BigEndianFourByteArray);
return BitConverter.ToSingle(BigEndianFourByteArray, 0);
}
return BitConverter.ToSingle(bytes, dataPosition);
}
private static double BytesToDouble(byte[] bytes, int dataPosition)
{
if (!BitConverter.IsLittleEndian) {
Array.Copy(bytes, dataPosition, BigEndianEightByteArray, 0, 8);
Array.Reverse(BigEndianEightByteArray);
return BitConverter.ToDouble(BigEndianEightByteArray, 0);
}
return BitConverter.ToDouble(bytes, dataPosition);
}
private static long BytesToLong(byte[] bytes, int dataPosition)
{
if (!BitConverter.IsLittleEndian) {
Array.Copy(bytes, dataPosition, BigEndianEightByteArray, 0, 8);
Array.Reverse(BigEndianEightByteArray);
return BitConverter.ToInt64(BigEndianEightByteArray, 0);
}
return BitConverter.ToInt64(bytes, dataPosition);
}
private static bool BytesToBool(byte[] bytes, int dataPosition)
{
return BitConverter.ToBoolean(bytes, dataPosition);
}
private static string BytesToString(byte[] bytes, int dataPosition, int dataSize)
{
if (dataSize == 0)
return "";
uint crc = crc32(bytes, dataPosition, dataSize);
string s;
if (!stringCache.TryGetValue(crc, out s)) {
s = Encoding.UTF8.GetString(bytes, dataPosition, dataSize);
stringCache.Add(crc, s);
}
return s;
}
public static uint crc32(byte[] input, int dataPosition, int dataSize)
{
unchecked {
uint crc = (uint)(((uint)0) ^ (-1));
var len = input.Length;
for (var i = dataPosition; i < dataPosition + dataSize; i++) {
crc = (crc >> 8) ^ crcTable[
(crc ^ input[i]) & 0xFF
];
}
crc = (uint)(crc ^ (-1));
if (crc < 0) {
crc += (uint)4294967296;
}
return crc;
}
}
private static byte BytesToByte(byte[] bytes, int dataPosition)
{
return bytes[dataPosition];
}
private static Color BytesToColor(byte[] bytes, int dataPosition)
{
var color = Color.black;
color.r = BytesToFloat(bytes, dataPosition);
color.g = BytesToFloat(bytes, dataPosition + 4);
color.b = BytesToFloat(bytes, dataPosition + 8);
color.a = BytesToFloat(bytes, dataPosition + 12);
return color;
}
private static Vector2 BytesToVector2(byte[] bytes, int dataPosition)
{
var vector2 = Vector2.zero;
vector2.x = BytesToFloat(bytes, dataPosition);
vector2.y = BytesToFloat(bytes, dataPosition + 4);
return vector2;
}
private static Vector2Int BytesToVector2Int(byte[] bytes, int dataPosition)
{
var vector2 = Vector2Int.zero;
vector2.x = BytesToInt(bytes, dataPosition);
vector2.y = BytesToInt(bytes, dataPosition + 4);
return vector2;
}
private static Vector3 BytesToVector3(byte[] bytes, int dataPosition)
{
var vector3 = Vector3.zero;
vector3.x = BytesToFloat(bytes, dataPosition);
vector3.y = BytesToFloat(bytes, dataPosition + 4);
vector3.z = BytesToFloat(bytes, dataPosition + 8);
return vector3;
}
private static Vector3Int BytesToVector3Int(byte[] bytes, int dataPosition)
{
var vector3 = Vector3Int.zero;
vector3.x = BytesToInt(bytes, dataPosition);
vector3.y = BytesToInt(bytes, dataPosition + 4);
vector3.z = BytesToInt(bytes, dataPosition + 8);
return vector3;
}
private static Vector4 BytesToVector4(byte[] bytes, int dataPosition)
{
var vector4 = Vector4.zero;
vector4.x = BytesToFloat(bytes, dataPosition);
vector4.y = BytesToFloat(bytes, dataPosition + 4);
vector4.z = BytesToFloat(bytes, dataPosition + 8);
vector4.w = BytesToFloat(bytes, dataPosition + 12);
return vector4;
}
private static Quaternion BytesToQuaternion(byte[] bytes, int dataPosition)
{
var quaternion = Quaternion.identity;
quaternion.x = BytesToFloat(bytes, dataPosition);
quaternion.y = BytesToFloat(bytes, dataPosition + 4);
quaternion.z = BytesToFloat(bytes, dataPosition + 8);
quaternion.w = BytesToFloat(bytes, dataPosition + 12);
return quaternion;
}
private static Rect BytesToRect(byte[] bytes, int dataPosition)
{
var rect = new Rect();
rect.x = BytesToFloat(bytes, dataPosition);
rect.y = BytesToFloat(bytes, dataPosition + 4);
rect.width = BytesToFloat(bytes, dataPosition + 8);
rect.height = BytesToFloat(bytes, dataPosition + 12);
return rect;
}
private static Matrix4x4 BytesToMatrix4x4(byte[] bytes, int dataPosition)
{
var matrix4x4 = Matrix4x4.identity;
matrix4x4.m00 = BytesToFloat(bytes, dataPosition);
matrix4x4.m01 = BytesToFloat(bytes, dataPosition + 4);
matrix4x4.m02 = BytesToFloat(bytes, dataPosition + 8);
matrix4x4.m03 = BytesToFloat(bytes, dataPosition + 12);
matrix4x4.m10 = BytesToFloat(bytes, dataPosition + 16);
matrix4x4.m11 = BytesToFloat(bytes, dataPosition + 20);
matrix4x4.m12 = BytesToFloat(bytes, dataPosition + 24);
matrix4x4.m13 = BytesToFloat(bytes, dataPosition + 28);
matrix4x4.m20 = BytesToFloat(bytes, dataPosition + 32);
matrix4x4.m21 = BytesToFloat(bytes, dataPosition + 36);
matrix4x4.m22 = BytesToFloat(bytes, dataPosition + 40);
matrix4x4.m23 = BytesToFloat(bytes, dataPosition + 44);
matrix4x4.m30 = BytesToFloat(bytes, dataPosition + 48);
matrix4x4.m31 = BytesToFloat(bytes, dataPosition + 52);
matrix4x4.m32 = BytesToFloat(bytes, dataPosition + 56);
matrix4x4.m33 = BytesToFloat(bytes, dataPosition + 60);
return matrix4x4;
}
private static AnimationCurve BytesToAnimationCurve(byte[] bytes, int dataPosition)
{
var animationCurve = new AnimationCurve();
var keyCount = BytesToInt(bytes, dataPosition);
for (int i = 0; i < keyCount; ++i) {
var keyframe = new Keyframe();
keyframe.time = BytesToFloat(bytes, dataPosition + 4);
keyframe.value = BytesToFloat(bytes, dataPosition + 8);
keyframe.inTangent = BytesToFloat(bytes, dataPosition + 12);
keyframe.outTangent = BitConverter.ToSingle(bytes, dataPosition + 16);
animationCurve.AddKey(keyframe);
dataPosition += animationCurveAdvance;
}
animationCurve.preWrapMode = (WrapMode)BytesToInt(bytes, dataPosition + 4);
animationCurve.postWrapMode = (WrapMode)BytesToInt(bytes, dataPosition + 8);
return animationCurve;
}
private static LayerMask BytesToLayerMask(byte[] bytes, int dataPosition)
{
var layerMask = new LayerMask();
layerMask.value = BytesToInt(bytes, dataPosition);
return layerMask;
}
private static UnityEngine.Object IndexToUnityObject(int index, FieldSerializationData activeFieldSerializationData)
{
if (index < 0 || index >= activeFieldSerializationData.unityObjects.Count) {
return null;
}
return activeFieldSerializationData.unityObjects[index];
}
private static SharedVariable BytesToSharedVariable(FieldSerializationData fieldSerializationData, Dictionary<int, int> fieldIndexMap, byte[] bytes, int dataPosition, IVariableSource variableSource, bool fromField, int hashPrefix)
{
SharedVariable sharedVariable = null;
var variableTypeName = LoadField(fieldSerializationData, fieldIndexMap, typeof(string), "Type", hashPrefix, null) as string;
if (string.IsNullOrEmpty(variableTypeName)) {
return null;
}
var variableName = LoadField(fieldSerializationData, fieldIndexMap, typeof(string), "Name", hashPrefix, null) as string;
var isShared = Convert.ToBoolean(LoadField(fieldSerializationData, fieldIndexMap, typeof(bool), "IsShared", hashPrefix, null));
var isGlobal = Convert.ToBoolean(LoadField(fieldSerializationData, fieldIndexMap, typeof(bool), "IsGlobal", hashPrefix, null));
var isDynamic = Convert.ToBoolean(LoadField(fieldSerializationData, fieldIndexMap, typeof(bool), "IsDynamic", hashPrefix, null));
if (isShared && (!isDynamic || BehaviorManager.IsPlaying) && fromField) {
if (!isGlobal) {
sharedVariable = variableSource.GetVariable(variableName);
} else {
if (globalVariables == null) {
globalVariables = GlobalVariables.Instance;
}
if (globalVariables != null) {
sharedVariable = globalVariables.GetVariable(variableName);
}
}
}
var variableType = TaskUtility.GetTypeWithinAssembly(variableTypeName);
if (variableType == null) {
return null;
}
bool typesEqual = true;
if (sharedVariable == null || !(typesEqual = sharedVariable.GetType().Equals(variableType))) {
sharedVariable = TaskUtility.CreateInstance(variableType) as SharedVariable;
sharedVariable.Name = variableName;
sharedVariable.IsShared = isShared;
sharedVariable.IsGlobal = isGlobal;
sharedVariable.IsDynamic = isDynamic;
#if UNITY_EDITOR
sharedVariable.Tooltip = LoadField(fieldSerializationData, fieldIndexMap, typeof(string), "Tooltip", hashPrefix, null) as string;
#endif
if (!isGlobal) {
sharedVariable.PropertyMapping = LoadField(fieldSerializationData, fieldIndexMap, typeof(string), "PropertyMapping", hashPrefix, null) as string;
sharedVariable.PropertyMappingOwner = LoadField(fieldSerializationData, fieldIndexMap, typeof(GameObject), "PropertyMappingOwner", hashPrefix, null) as GameObject;
sharedVariable.InitializePropertyMapping(variableSource as BehaviorSource);
}
// if the types are not equal then this shared variable used to be a different type so it should be shared
if (!typesEqual) {
sharedVariable.IsShared = true;
}
if (isDynamic && BehaviorManager.IsPlaying) {
// The new dynamic variable has been created.
variableSource.SetVariable(variableName, sharedVariable);
}
LoadFields(fieldSerializationData, fieldIndexMap, sharedVariable, hashPrefix, variableSource);
}
return sharedVariable;
}
}