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 { 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> 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 stringCache = new Dictionary(); 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(); var fieldIndexMap = ObjectPool.Get>(); 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(); 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(); } 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; 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(); var fieldIndexMap = ObjectPool.Get>(); 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 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>(); 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 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), "NodeDataWatchedFields", 0, null); if (value != null) { nodeData.WatchedFieldNames = new List(); nodeData.WatchedFields = new List(); 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 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 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>(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(); 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 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; } }