diff --git a/AssetStudioUtility/AssetStudioUtility.csproj b/AssetStudioUtility/AssetStudioUtility.csproj index 780f4d6..c5d11ce 100644 --- a/AssetStudioUtility/AssetStudioUtility.csproj +++ b/AssetStudioUtility/AssetStudioUtility.csproj @@ -34,6 +34,9 @@ MinimumRecommendedRules.ruleset + + ..\packages\Mono.Cecil.0.11.3\lib\net40\Mono.Cecil.dll + @@ -43,15 +46,6 @@ - - Libraries\Unity.Cecil.dll - - - Libraries\Unity.CecilTools.dll - - - Libraries\Unity.SerializationLogic.dll - @@ -85,6 +79,14 @@ + + + + + + + + @@ -104,5 +106,8 @@ Texture2DDecoderWrapper + + + \ No newline at end of file diff --git a/AssetStudioUtility/Libraries/Unity.Cecil.dll b/AssetStudioUtility/Libraries/Unity.Cecil.dll deleted file mode 100644 index 4166cfc..0000000 Binary files a/AssetStudioUtility/Libraries/Unity.Cecil.dll and /dev/null differ diff --git a/AssetStudioUtility/Libraries/Unity.CecilTools.dll b/AssetStudioUtility/Libraries/Unity.CecilTools.dll deleted file mode 100644 index 2c1ce70..0000000 Binary files a/AssetStudioUtility/Libraries/Unity.CecilTools.dll and /dev/null differ diff --git a/AssetStudioUtility/Libraries/Unity.SerializationLogic.dll b/AssetStudioUtility/Libraries/Unity.SerializationLogic.dll deleted file mode 100644 index 7af54d9..0000000 Binary files a/AssetStudioUtility/Libraries/Unity.SerializationLogic.dll and /dev/null differ diff --git a/AssetStudioUtility/TypeDefinitionConverter.cs b/AssetStudioUtility/TypeDefinitionConverter.cs index d796f0e..28f4f03 100644 --- a/AssetStudioUtility/TypeDefinitionConverter.cs +++ b/AssetStudioUtility/TypeDefinitionConverter.cs @@ -26,22 +26,22 @@ namespace AssetStudio { var nodes = new List(); - Stack baseTypes = new Stack(); - TypeReference baseType = TypeDef.BaseType; - while (!UnitySerializationLogic.IsNonSerialized(baseType)) + var baseTypes = new Stack(); + var lastBaseType = TypeDef.BaseType; + while (!UnitySerializationLogic.IsNonSerialized(lastBaseType)) { - GenericInstanceType genericInstanceType = baseType as GenericInstanceType; + var genericInstanceType = lastBaseType as GenericInstanceType; if (genericInstanceType != null) { TypeResolver.Add(genericInstanceType); } - baseTypes.Push(baseType); - baseType = baseType.Resolve().BaseType; + baseTypes.Push(lastBaseType); + lastBaseType = lastBaseType.Resolve().BaseType; } while (baseTypes.Count > 0) { - TypeReference typeReference = baseTypes.Pop(); - TypeDefinition typeDefinition = typeReference.Resolve(); + var typeReference = baseTypes.Pop(); + var typeDefinition = typeReference.Resolve(); foreach (var fieldDefinition in typeDefinition.Fields.Where(WillUnitySerialize)) { if (!IsHiddenByParentClass(baseTypes, fieldDefinition, TypeDef)) @@ -50,15 +50,15 @@ namespace AssetStudio } } - var genericInstanceType2 = typeReference as GenericInstanceType; - if (genericInstanceType2 != null) + var genericInstanceType = typeReference as GenericInstanceType; + if (genericInstanceType != null) { - TypeResolver.Remove(genericInstanceType2); + TypeResolver.Remove(genericInstanceType); } } - foreach (FieldDefinition fieldDefinition2 in FilteredFields()) + foreach (var field in FilteredFields()) { - nodes.AddRange(ProcessingFieldRef(fieldDefinition2)); + nodes.AddRange(ProcessingFieldRef(field)); } return nodes; @@ -66,75 +66,60 @@ namespace AssetStudio private bool WillUnitySerialize(FieldDefinition fieldDefinition) { - bool result; try { - TypeReference typeReference = TypeResolver.Resolve(fieldDefinition.FieldType); - if (UnitySerializationLogic.ShouldNotTryToResolve(typeReference)) + var resolvedFieldType = TypeResolver.Resolve(fieldDefinition.FieldType); + if (UnitySerializationLogic.ShouldNotTryToResolve(resolvedFieldType)) { - result = false; + return false; } - else + if (!UnityEngineTypePredicates.IsUnityEngineObject(resolvedFieldType)) { - if (!UnityEngineTypePredicates.IsUnityEngineObject(typeReference)) + if (resolvedFieldType.FullName == fieldDefinition.DeclaringType.FullName) { - if (typeReference.FullName == fieldDefinition.DeclaringType.FullName) - { - return false; - } + return false; } - result = UnitySerializationLogic.WillUnitySerialize(fieldDefinition, TypeResolver); } + return UnitySerializationLogic.WillUnitySerialize(fieldDefinition, TypeResolver); } catch (Exception ex) { throw new Exception(string.Format("Exception while processing {0} {1}, error {2}", fieldDefinition.FieldType.FullName, fieldDefinition.FullName, ex.Message)); } - return result; } private static bool IsHiddenByParentClass(IEnumerable parentTypes, FieldDefinition fieldDefinition, TypeDefinition processingType) { - return processingType.Fields.Any((FieldDefinition f) => f.Name == fieldDefinition.Name) || parentTypes.Any((TypeReference t) => t.Resolve().Fields.Any((FieldDefinition f) => f.Name == fieldDefinition.Name)); + return processingType.Fields.Any(f => f.Name == fieldDefinition.Name) || parentTypes.Any(t => t.Resolve().Fields.Any(f => f.Name == fieldDefinition.Name)); } private IEnumerable FilteredFields() { - foreach (var f in TypeDef.Fields.Where(WillUnitySerialize)) - { - if (UnitySerializationLogic.IsSupportedCollection(f.FieldType) || !f.FieldType.IsGenericInstance || UnitySerializationLogic.ShouldImplementIDeserializable(f.FieldType.Resolve())) - { - yield return f; - } - } - - yield break; + return TypeDef.Fields.Where(WillUnitySerialize).Where(f => + UnitySerializationLogic.IsSupportedCollection(f.FieldType) || + !f.FieldType.IsGenericInstance || + UnitySerializationLogic.ShouldImplementIDeserializable(f.FieldType.Resolve())); } private FieldReference ResolveGenericFieldReference(FieldReference fieldRef) { - FieldReference field = new FieldReference(fieldRef.Name, fieldRef.FieldType, ResolveDeclaringType(fieldRef.DeclaringType)); + var field = new FieldReference(fieldRef.Name, fieldRef.FieldType, ResolveDeclaringType(fieldRef.DeclaringType)); return TypeDef.Module.ImportReference(field); } private TypeReference ResolveDeclaringType(TypeReference declaringType) { - TypeDefinition typeDefinition = declaringType.Resolve(); - TypeReference result; + var typeDefinition = declaringType.Resolve(); if (typeDefinition == null || !typeDefinition.HasGenericParameters) { - result = typeDefinition; + return typeDefinition; } - else + var genericInstanceType = new GenericInstanceType(typeDefinition); + foreach (var genericParameter in typeDefinition.GenericParameters) { - GenericInstanceType genericInstanceType = new GenericInstanceType(typeDefinition); - foreach (GenericParameter item in typeDefinition.GenericParameters) - { - genericInstanceType.GenericArguments.Add(item); - } - result = TypeResolver.Resolve(genericInstanceType); + genericInstanceType.GenericArguments.Add(genericParameter); } - return result; + return TypeResolver.Resolve(genericInstanceType); } private List ProcessingFieldRef(FieldReference fieldDef) @@ -155,7 +140,6 @@ namespace AssetStudio private static bool RequiresAlignment(TypeReference typeRef) { - bool result; switch (typeRef.MetadataType) { case MetadataType.Boolean: @@ -164,13 +148,10 @@ namespace AssetStudio case MetadataType.Byte: case MetadataType.Int16: case MetadataType.UInt16: - result = true; - break; + return true; default: - result = (UnitySerializationLogic.IsSupportedCollection(typeRef) && RequiresAlignment(CecilUtils.ElementTypeOfCollection(typeRef))); - break; + return UnitySerializationLogic.IsSupportedCollection(typeRef) && RequiresAlignment(CecilUtils.ElementTypeOfCollection(typeRef)); } - return result; } private static bool IsSystemString(TypeReference typeRef) diff --git a/AssetStudioUtility/Unity.CecilTools/CecilUtils.cs b/AssetStudioUtility/Unity.CecilTools/CecilUtils.cs new file mode 100644 index 0000000..a7e3cb0 --- /dev/null +++ b/AssetStudioUtility/Unity.CecilTools/CecilUtils.cs @@ -0,0 +1,65 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Unity.CecilTools.Extensions; + +namespace Unity.CecilTools +{ + public static class CecilUtils + { + public static MethodDefinition FindInTypeExplicitImplementationFor(MethodDefinition interfaceMethod, TypeDefinition typeDefinition) + { + return typeDefinition.Methods.SingleOrDefault(m => m.Overrides.Any(o => o.CheckedResolve().SameAs(interfaceMethod))); + } + + public static IEnumerable AllInterfacesImplementedBy(TypeDefinition typeDefinition) + { + return TypeAndBaseTypesOf(typeDefinition).SelectMany(t => t.Interfaces).Select(i => i.InterfaceType.CheckedResolve()).Distinct(); + } + + public static IEnumerable TypeAndBaseTypesOf(TypeReference typeReference) + { + while (typeReference != null) + { + var typeDefinition = typeReference.CheckedResolve(); + yield return typeDefinition; + typeReference = typeDefinition.BaseType; + } + } + + public static IEnumerable BaseTypesOf(TypeReference typeReference) + { + return TypeAndBaseTypesOf(typeReference).Skip(1); + } + + public static bool IsGenericList(TypeReference type) + { + return type.Name == "List`1" && type.SafeNamespace() == "System.Collections.Generic"; + } + + public static bool IsGenericDictionary(TypeReference type) + { + if (type is GenericInstanceType) + type = ((GenericInstanceType)type).ElementType; + + return type.Name == "Dictionary`2" && type.SafeNamespace() == "System.Collections.Generic"; + } + + public static TypeReference ElementTypeOfCollection(TypeReference type) + { + var at = type as ArrayType; + if (at != null) + return at.ElementType; + + if (IsGenericList(type)) + return ((GenericInstanceType)type).GenericArguments.Single(); + + throw new ArgumentException(); + } + } +} diff --git a/AssetStudioUtility/Unity.CecilTools/ElementType.cs b/AssetStudioUtility/Unity.CecilTools/ElementType.cs new file mode 100644 index 0000000..a3d858d --- /dev/null +++ b/AssetStudioUtility/Unity.CecilTools/ElementType.cs @@ -0,0 +1,21 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using Mono.Cecil; + +namespace Unity.CecilTools +{ + static public class ElementType + { + public static TypeReference For(TypeReference byRefType) + { + var refType = byRefType as TypeSpecification; + if (refType != null) + return refType.ElementType; + + throw new ArgumentException(string.Format("TypeReference isn't a TypeSpecification {0} ", byRefType)); + } + } +} diff --git a/AssetStudioUtility/Unity.CecilTools/Extensions/MethodDefinitionExtensions.cs b/AssetStudioUtility/Unity.CecilTools/Extensions/MethodDefinitionExtensions.cs new file mode 100644 index 0000000..c99d8e7 --- /dev/null +++ b/AssetStudioUtility/Unity.CecilTools/Extensions/MethodDefinitionExtensions.cs @@ -0,0 +1,50 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using Mono.Cecil; + +namespace Unity.CecilTools.Extensions +{ + static class MethodDefinitionExtensions + { + public static bool SameAs(this MethodDefinition self, MethodDefinition other) + { + // FIXME: should be able to compare MethodDefinition references directly + return self.FullName == other.FullName; + } + + public static string PropertyName(this MethodDefinition self) + { + return self.Name.Substring(4); + } + + public static bool IsConversionOperator(this MethodDefinition method) + { + if (!method.IsSpecialName) + return false; + + return method.Name == "op_Implicit" || method.Name == "op_Explicit"; + } + + public static bool IsSimpleSetter(this MethodDefinition original) + { + return original.IsSetter && original.Parameters.Count == 1; + } + + public static bool IsSimpleGetter(this MethodDefinition original) + { + return original.IsGetter && original.Parameters.Count == 0; + } + + public static bool IsSimplePropertyAccessor(this MethodDefinition method) + { + return method.IsSimpleGetter() || method.IsSimpleSetter(); + } + + public static bool IsDefaultConstructor(MethodDefinition m) + { + return m.IsConstructor && !m.IsStatic && m.Parameters.Count == 0; + } + } +} diff --git a/AssetStudioUtility/Unity.CecilTools/Extensions/ResolutionExtensions.cs b/AssetStudioUtility/Unity.CecilTools/Extensions/ResolutionExtensions.cs new file mode 100644 index 0000000..29d8506 --- /dev/null +++ b/AssetStudioUtility/Unity.CecilTools/Extensions/ResolutionExtensions.cs @@ -0,0 +1,36 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using Mono.Cecil; + +namespace Unity.CecilTools.Extensions +{ + public static class ResolutionExtensions + { + public static TypeDefinition CheckedResolve(this TypeReference type) + { + return Resolve(type, reference => reference.Resolve()); + } + + public static MethodDefinition CheckedResolve(this MethodReference method) + { + return Resolve(method, reference => reference.Resolve()); + } + + private static TDefinition Resolve(TReference reference, Func resolve) + where TReference : MemberReference + where TDefinition : class, IMemberDefinition + { + if (reference.Module == null) + throw new ResolutionException(reference); + + var definition = resolve(reference); + if (definition == null) + throw new ResolutionException(reference); + + return definition; + } + } +} diff --git a/AssetStudioUtility/Unity.CecilTools/Extensions/TypeDefinitionExtensions.cs b/AssetStudioUtility/Unity.CecilTools/Extensions/TypeDefinitionExtensions.cs new file mode 100644 index 0000000..fea4610 --- /dev/null +++ b/AssetStudioUtility/Unity.CecilTools/Extensions/TypeDefinitionExtensions.cs @@ -0,0 +1,47 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Mono.Cecil; + +namespace Unity.CecilTools.Extensions +{ + public static class TypeDefinitionExtensions + { + public static bool IsSubclassOf(this TypeDefinition type, string baseTypeName) + { + var baseType = type.BaseType; + if (baseType == null) + return false; + if (baseType.FullName == baseTypeName) + return true; + + var baseTypeDef = baseType.Resolve(); + if (baseTypeDef == null) + return false; + + return IsSubclassOf(baseTypeDef, baseTypeName); + } + + public static bool IsSubclassOf(this TypeDefinition type, params string[] baseTypeNames) + { + var baseType = type.BaseType; + if (baseType == null) + return false; + + for (int i = 0; i < baseTypeNames.Length; i++) + if (baseType.FullName == baseTypeNames[i]) + return true; + + var baseTypeDef = baseType.Resolve(); + if (baseTypeDef == null) + return false; + + return IsSubclassOf(baseTypeDef, baseTypeNames); + } + } +} diff --git a/AssetStudioUtility/Unity.CecilTools/Extensions/TypeReferenceExtensions.cs b/AssetStudioUtility/Unity.CecilTools/Extensions/TypeReferenceExtensions.cs new file mode 100644 index 0000000..65bf501 --- /dev/null +++ b/AssetStudioUtility/Unity.CecilTools/Extensions/TypeReferenceExtensions.cs @@ -0,0 +1,53 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using Mono.Cecil; + +namespace Unity.CecilTools.Extensions +{ + public static class TypeReferenceExtensions + { + public static string SafeNamespace(this TypeReference type) + { + if (type.IsGenericInstance) + return ((GenericInstanceType)type).ElementType.SafeNamespace(); + if (type.IsNested) + return type.DeclaringType.SafeNamespace(); + return type.Namespace; + } + + public static bool IsAssignableTo(this TypeReference typeRef, string typeName) + { + try + { + if (typeRef.IsGenericInstance) + return ElementType.For(typeRef).IsAssignableTo(typeName); + + if (typeRef.FullName == typeName) + return true; + + return typeRef.CheckedResolve().IsSubclassOf(typeName); + } + catch (AssemblyResolutionException) // If we can't resolve our typeref or one of its base types, + { // let's assume it is not assignable to our target type + return false; + } + } + + public static bool IsEnum(this TypeReference type) + { + return type.IsValueType && !type.IsPrimitive && type.CheckedResolve().IsEnum; + } + + public static bool IsStruct(this TypeReference type) + { + return type.IsValueType && !type.IsPrimitive && !type.IsEnum() && !IsSystemDecimal(type); + } + + private static bool IsSystemDecimal(TypeReference type) + { + return type.FullName == "System.Decimal"; + } + } +} diff --git a/AssetStudioUtility/Unity.SerializationLogic/UnityEngineTypePredicates.cs b/AssetStudioUtility/Unity.SerializationLogic/UnityEngineTypePredicates.cs new file mode 100644 index 0000000..73e39ee --- /dev/null +++ b/AssetStudioUtility/Unity.SerializationLogic/UnityEngineTypePredicates.cs @@ -0,0 +1,168 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System.Collections.Generic; +using Unity.CecilTools.Extensions; +using Mono.Cecil; + +namespace Unity.SerializationLogic +{ + public class UnityEngineTypePredicates + { + private static readonly HashSet TypesThatShouldHaveHadSerializableAttribute = new HashSet + { + "Vector3", + "Vector2", + "Vector4", + "Rect", + "RectInt", + "Quaternion", + "Matrix4x4", + "Color", + "Color32", + "LayerMask", + "Bounds", + "BoundsInt", + "Vector3Int", + "Vector2Int", + }; + + private const string Gradient = "UnityEngine.Gradient"; + private const string GUIStyle = "UnityEngine.GUIStyle"; + private const string RectOffset = "UnityEngine.RectOffset"; + protected const string UnityEngineObject = "UnityEngine.Object"; + public const string MonoBehaviour = "UnityEngine.MonoBehaviour"; + public const string ScriptableObject = "UnityEngine.ScriptableObject"; + protected const string Matrix4x4 = "UnityEngine.Matrix4x4"; + protected const string Color32 = "UnityEngine.Color32"; + private const string SerializeFieldAttribute = "UnityEngine.SerializeField"; + private const string SerializeReferenceAttribute = "UnityEngine.SerializeReference"; + + private static string[] serializableClasses = new[] + { + "UnityEngine.AnimationCurve", + "UnityEngine.Gradient", + "UnityEngine.GUIStyle", + "UnityEngine.RectOffset" + }; + + private static string[] serializableStructs = new[] + { + // NOTE: assumes all types here are NOT interfaces + "UnityEngine.Color32", + "UnityEngine.Matrix4x4", + "UnityEngine.Rendering.SphericalHarmonicsL2", + "UnityEngine.PropertyName", + }; + + public static bool IsMonoBehaviour(TypeReference type) + { + return IsMonoBehaviour(type.CheckedResolve()); + } + + private static bool IsMonoBehaviour(TypeDefinition typeDefinition) + { + return typeDefinition.IsSubclassOf(MonoBehaviour); + } + + public static bool IsScriptableObject(TypeReference type) + { + return IsScriptableObject(type.CheckedResolve()); + } + + private static bool IsScriptableObject(TypeDefinition temp) + { + return temp.IsSubclassOf(ScriptableObject); + } + + public static bool IsColor32(TypeReference type) + { + return type.IsAssignableTo(Color32); + } + + //Do NOT remove these, cil2as still depends on these in 4.x + public static bool IsMatrix4x4(TypeReference type) + { + return type.IsAssignableTo(Matrix4x4); + } + + public static bool IsGradient(TypeReference type) + { + return type.IsAssignableTo(Gradient); + } + + public static bool IsGUIStyle(TypeReference type) + { + return type.IsAssignableTo(GUIStyle); + } + + public static bool IsRectOffset(TypeReference type) + { + return type.IsAssignableTo(RectOffset); + } + + public static bool IsSerializableUnityClass(TypeReference type) + { + foreach (var unityClasses in serializableClasses) + { + if (type.IsAssignableTo(unityClasses)) + return true; + } + return false; + } + + public static bool IsSerializableUnityStruct(TypeReference type) + { + foreach (var unityStruct in serializableStructs) + { + // NOTE: structs cannot inherit from structs, and can only inherit from interfaces + // since we know all types in serializableStructs are not interfaces, + // we can just do a direct comparison. + if (type.FullName == unityStruct) + return true; + } + + if (type.FullName.IndexOf("UnityEngine.LazyLoadReference`1") == 0) + return true; + + return false; + } + + public static bool IsUnityEngineObject(TypeReference type) + { + //todo: somehow solve this elegantly. CheckedResolve() drops the [] of a type. + if (type.IsArray) + return false; + + if (type.FullName == UnityEngineObject) + return true; + + var typeDefinition = type.Resolve(); + if (typeDefinition == null) + return false; + + return typeDefinition.IsSubclassOf(UnityEngineObject); + } + + public static bool ShouldHaveHadSerializableAttribute(TypeReference type) + { + return IsUnityEngineValueType(type); + } + + public static bool IsUnityEngineValueType(TypeReference type) + { + return type.SafeNamespace() == "UnityEngine" && TypesThatShouldHaveHadSerializableAttribute.Contains(type.Name); + } + + public static bool IsSerializeFieldAttribute(TypeReference attributeType) + { + return attributeType.FullName == SerializeFieldAttribute; + } + + public static bool IsSerializeReferenceAttribute(TypeReference attributeType) + { + return attributeType.FullName == SerializeReferenceAttribute; + } + } +} diff --git a/AssetStudioUtility/Unity.SerializationLogic/UnitySerializationLogic.cs b/AssetStudioUtility/Unity.SerializationLogic/UnitySerializationLogic.cs new file mode 100644 index 0000000..1d141ab --- /dev/null +++ b/AssetStudioUtility/Unity.SerializationLogic/UnitySerializationLogic.cs @@ -0,0 +1,612 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Collections.Generic; +using Unity.CecilTools; +using Unity.CecilTools.Extensions; + +namespace Unity.SerializationLogic +{ + internal class GenericInstanceHolder + { + public int Count; + public IGenericInstance GenericInstance; + } + + public class TypeResolver + { + private readonly IGenericInstance _typeDefinitionContext; + private readonly IGenericInstance _methodDefinitionContext; + private readonly Dictionary _context = new Dictionary(); + + public TypeResolver() + { + } + + public TypeResolver(IGenericInstance typeDefinitionContext) + { + _typeDefinitionContext = typeDefinitionContext; + } + + public TypeResolver(GenericInstanceMethod methodDefinitionContext) + { + _methodDefinitionContext = methodDefinitionContext; + } + + public TypeResolver(IGenericInstance typeDefinitionContext, IGenericInstance methodDefinitionContext) + { + _typeDefinitionContext = typeDefinitionContext; + _methodDefinitionContext = methodDefinitionContext; + } + + public void Add(GenericInstanceType genericInstanceType) + { + Add(ElementTypeFor(genericInstanceType).FullName, genericInstanceType); + } + + public void Remove(GenericInstanceType genericInstanceType) + { + Remove(genericInstanceType.ElementType.FullName, genericInstanceType); + } + + public void Add(GenericInstanceMethod genericInstanceMethod) + { + Add(ElementTypeFor(genericInstanceMethod).FullName, genericInstanceMethod); + } + + private static MemberReference ElementTypeFor(TypeSpecification genericInstanceType) + { + return genericInstanceType.ElementType; + } + + private static MemberReference ElementTypeFor(MethodSpecification genericInstanceMethod) + { + return genericInstanceMethod.ElementMethod; + } + + public void Remove(GenericInstanceMethod genericInstanceMethod) + { + Remove(genericInstanceMethod.ElementMethod.FullName, genericInstanceMethod); + } + + public TypeReference Resolve(TypeReference typeReference) + { + var genericParameter = typeReference as GenericParameter; + if (genericParameter != null) + { + var resolved = ResolveGenericParameter(genericParameter); + if (genericParameter == resolved) // Resolving failed, return what we have. + return resolved; + + return Resolve(resolved); + } + + var arrayType = typeReference as ArrayType; + if (arrayType != null) + return new ArrayType(Resolve(arrayType.ElementType), arrayType.Rank); + + var pointerType = typeReference as PointerType; + if (pointerType != null) + return new PointerType(Resolve(pointerType.ElementType)); + + var byReferenceType = typeReference as ByReferenceType; + if (byReferenceType != null) + return new ByReferenceType(Resolve(byReferenceType.ElementType)); + + var genericInstanceType = typeReference as GenericInstanceType; + if (genericInstanceType != null) + { + var newGenericInstanceType = new GenericInstanceType(Resolve(genericInstanceType.ElementType)); + foreach (var genericArgument in genericInstanceType.GenericArguments) + newGenericInstanceType.GenericArguments.Add(Resolve(genericArgument)); + return newGenericInstanceType; + } + + var pinnedType = typeReference as PinnedType; + if (pinnedType != null) + return new PinnedType(Resolve(pinnedType.ElementType)); + + var reqModifierType = typeReference as RequiredModifierType; + if (reqModifierType != null) + return Resolve(reqModifierType.ElementType); + + var optModifierType = typeReference as OptionalModifierType; + if (optModifierType != null) + return new OptionalModifierType(Resolve(optModifierType.ModifierType), Resolve(optModifierType.ElementType)); + + var sentinelType = typeReference as SentinelType; + if (sentinelType != null) + return new SentinelType(Resolve(sentinelType.ElementType)); + + var funcPtrType = typeReference as FunctionPointerType; + if (funcPtrType != null) + throw new NotSupportedException("Function pointer types are not supported by the SerializationWeaver"); + + if (typeReference is TypeSpecification) + throw new NotSupportedException(); + + return typeReference; + } + + private TypeReference ResolveGenericParameter(GenericParameter genericParameter) + { + if (genericParameter.Owner == null) + throw new NotSupportedException(); + + var memberReference = genericParameter.Owner as MemberReference; + if (memberReference == null) + throw new NotSupportedException(); + + var key = memberReference.FullName; + if (!_context.ContainsKey(key)) + { + if (genericParameter.Type == GenericParameterType.Type) + { + if (_typeDefinitionContext != null) + return _typeDefinitionContext.GenericArguments[genericParameter.Position]; + + return genericParameter; + } + + if (_methodDefinitionContext != null) + return _methodDefinitionContext.GenericArguments[genericParameter.Position]; + + return genericParameter; + } + + return GenericArgumentAt(key, genericParameter.Position); + } + + private TypeReference GenericArgumentAt(string key, int position) + { + return _context[key].GenericInstance.GenericArguments[position]; + } + + private void Add(string key, IGenericInstance value) + { + GenericInstanceHolder oldValue; + + if (_context.TryGetValue(key, out oldValue)) + { + var memberReference = value as MemberReference; + if (memberReference == null) + throw new NotSupportedException(); + + var storedValue = (MemberReference)oldValue.GenericInstance; + + if (storedValue.FullName != memberReference.FullName) + throw new ArgumentException("Duplicate key!", "key"); + + oldValue.Count++; + return; + } + + _context.Add(key, new GenericInstanceHolder { Count = 1, GenericInstance = value }); + } + + private void Remove(string key, IGenericInstance value) + { + GenericInstanceHolder oldValue; + + if (_context.TryGetValue(key, out oldValue)) + { + var memberReference = value as MemberReference; + if (memberReference == null) + throw new NotSupportedException(); + + var storedValue = (MemberReference)oldValue.GenericInstance; + + if (storedValue.FullName != memberReference.FullName) + throw new ArgumentException("Invalid value!", "value"); + + oldValue.Count--; + if (oldValue.Count == 0) + _context.Remove(key); + + return; + } + + throw new ArgumentException("Invalid key!", "key"); + } + } + + public static class UnitySerializationLogic + { + public static bool WillUnitySerialize(FieldDefinition fieldDefinition) + { + return WillUnitySerialize(fieldDefinition, new TypeResolver(null)); + } + + public static bool WillUnitySerialize(FieldDefinition fieldDefinition, TypeResolver typeResolver) + { + if (fieldDefinition == null) + return false; + + //skip static, const and NotSerialized fields before even checking the type + if (fieldDefinition.IsStatic || IsConst(fieldDefinition) || fieldDefinition.IsNotSerialized || fieldDefinition.IsInitOnly) + return false; + + // The field must have correct visibility/decoration to be serialized. + if (!fieldDefinition.IsPublic && + !ShouldHaveHadAllFieldsPublic(fieldDefinition) && + !HasSerializeFieldAttribute(fieldDefinition) && + !HasSerializeReferenceAttribute(fieldDefinition)) + return false; + + // Don't try to resolve types that come from Windows assembly, + // as serialization weaver will fail to resolve that (due to it being in platform specific SDKs) + if (ShouldNotTryToResolve(fieldDefinition.FieldType)) + return false; + + if (IsFixedBuffer(fieldDefinition)) + return true; + + // Resolving types is more complex and slower than checking their names or attributes, + // thus keep those checks below + var typeReference = typeResolver.Resolve(fieldDefinition.FieldType); + + //the type of the field must be serializable in the first place. + + if (typeReference.MetadataType == MetadataType.String) + return true; + + if (typeReference.IsValueType) + return IsValueTypeSerializable(typeReference); + + if (typeReference is ArrayType || CecilUtils.IsGenericList(typeReference)) + { + if (!HasSerializeReferenceAttribute(fieldDefinition)) + return IsSupportedCollection(typeReference); + } + + + if (!IsReferenceTypeSerializable(typeReference) && !HasSerializeReferenceAttribute(fieldDefinition)) + return false; + + if (IsDelegate(typeReference)) + return false; + + return true; + } + + private static bool IsDelegate(TypeReference typeReference) + { + return typeReference.IsAssignableTo("System.Delegate"); + } + + public static bool ShouldFieldBePPtrRemapped(FieldDefinition fieldDefinition) + { + return ShouldFieldBePPtrRemapped(fieldDefinition, new TypeResolver(null)); + } + + public static bool ShouldFieldBePPtrRemapped(FieldDefinition fieldDefinition, TypeResolver typeResolver) + { + if (!WillUnitySerialize(fieldDefinition, typeResolver)) + return false; + + return CanTypeContainUnityEngineObjectReference(typeResolver.Resolve(fieldDefinition.FieldType)); + } + + private static bool CanTypeContainUnityEngineObjectReference(TypeReference typeReference) + { + if (IsUnityEngineObject(typeReference)) + return true; + + if (typeReference.IsEnum()) + return false; + + if (IsSerializablePrimitive(typeReference)) + return false; + + if (IsSupportedCollection(typeReference)) + return CanTypeContainUnityEngineObjectReference(CecilUtils.ElementTypeOfCollection(typeReference)); + + var definition = typeReference.Resolve(); + if (definition == null) + return false; + + return HasFieldsThatCanContainUnityEngineObjectReferences(definition, new TypeResolver(typeReference as GenericInstanceType)); + } + + private static bool HasFieldsThatCanContainUnityEngineObjectReferences(TypeDefinition definition, TypeResolver typeResolver) + { + return AllFieldsFor(definition, typeResolver).Where(kv => kv.Value.Resolve(kv.Key.FieldType).Resolve() != definition).Any(kv => CanFieldContainUnityEngineObjectReference(definition, kv.Key, kv.Value)); + } + + private static IEnumerable> AllFieldsFor(TypeDefinition definition, TypeResolver typeResolver) + { + var baseType = definition.BaseType; + + if (baseType != null) + { + var genericBaseInstanceType = baseType as GenericInstanceType; + if (genericBaseInstanceType != null) + typeResolver.Add(genericBaseInstanceType); + foreach (var kv in AllFieldsFor(baseType.Resolve(), typeResolver)) + yield return kv; + if (genericBaseInstanceType != null) + typeResolver.Remove(genericBaseInstanceType); + } + + foreach (var fieldDefinition in definition.Fields) + yield return new KeyValuePair(fieldDefinition, typeResolver); + } + + private static bool CanFieldContainUnityEngineObjectReference(TypeReference typeReference, FieldDefinition t, TypeResolver typeResolver) + { + if (typeResolver.Resolve(t.FieldType) == typeReference) + return false; + + if (!WillUnitySerialize(t, typeResolver)) + return false; + + if (UnityEngineTypePredicates.IsUnityEngineValueType(typeReference)) + return false; + + return true; + } + + private static bool IsConst(FieldDefinition fieldDefinition) + { + return fieldDefinition.IsLiteral && !fieldDefinition.IsInitOnly; + } + + public static bool HasSerializeFieldAttribute(FieldDefinition field) + { + //return FieldAttributes(field).Any(UnityEngineTypePredicates.IsSerializeFieldAttribute); + foreach (var attribute in FieldAttributes(field)) + if (UnityEngineTypePredicates.IsSerializeFieldAttribute(attribute)) + return true; + return false; + } + + public static bool HasSerializeReferenceAttribute(FieldDefinition field) + { + foreach (var attribute in FieldAttributes(field)) + if (UnityEngineTypePredicates.IsSerializeReferenceAttribute(attribute)) + return true; + return false; + } + + private static IEnumerable FieldAttributes(FieldDefinition field) + { + return field.CustomAttributes.Select(_ => _.AttributeType); + } + + public static bool ShouldNotTryToResolve(TypeReference typeReference) + { + var typeReferenceScopeName = typeReference.Scope.Name; + if (typeReferenceScopeName == "Windows") + { + return true; + } + + if (typeReferenceScopeName == "mscorlib") + { + var resolved = typeReference.Resolve(); + return resolved == null; + } + + try + { // This will throw an exception if typereference thinks it's referencing a .dll, + // but actually there's .winmd file in the current directory. RRW will fix this + // at a later step, so we will not try to resolve this type. This is OK, as any + // type defined in a winmd cannot be serialized. + typeReference.Resolve(); + } + catch + { + return true; + } + + return false; + } + + private static bool IsFieldTypeSerializable(TypeReference typeReference, FieldDefinition fieldDefinition) + { + return IsTypeSerializable(typeReference) || IsSupportedCollection(typeReference) || IsFixedBuffer(fieldDefinition); + } + + private static bool IsValueTypeSerializable(TypeReference typeReference) + { + if (typeReference.IsPrimitive) + return IsSerializablePrimitive(typeReference); + return UnityEngineTypePredicates.IsSerializableUnityStruct(typeReference) || + typeReference.IsEnum() || + ShouldImplementIDeserializable(typeReference); + } + + private static bool IsReferenceTypeSerializable(TypeReference typeReference) + { + if (typeReference.MetadataType == MetadataType.String) + return IsSerializablePrimitive(typeReference); + + if (IsGenericDictionary(typeReference)) + return false; + + if (IsUnityEngineObject(typeReference) || + ShouldImplementIDeserializable(typeReference) || + UnityEngineTypePredicates.IsSerializableUnityClass(typeReference)) + return true; + + return false; + } + + private static bool IsTypeSerializable(TypeReference typeReference) + { + if (typeReference.MetadataType == MetadataType.String) + return true; + if (typeReference.IsValueType) + return IsValueTypeSerializable(typeReference); + return IsReferenceTypeSerializable(typeReference); + } + + private static bool IsGenericDictionary(TypeReference typeReference) + { + var current = typeReference; + + if (current != null) + { + if (CecilUtils.IsGenericDictionary(current)) + return true; + } + + return false; + } + + public static bool IsFixedBuffer(FieldDefinition fieldDefinition) + { + return GetFixedBufferAttribute(fieldDefinition) != null; + } + + public static CustomAttribute GetFixedBufferAttribute(FieldDefinition fieldDefinition) + { + if (!fieldDefinition.HasCustomAttributes) + return null; + + return fieldDefinition.CustomAttributes.SingleOrDefault(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.FixedBufferAttribute"); + } + + public static int GetFixedBufferLength(FieldDefinition fieldDefinition) + { + var fixedBufferAttribute = GetFixedBufferAttribute(fieldDefinition); + + if (fixedBufferAttribute == null) + throw new ArgumentException(string.Format("Field '{0}' is not a fixed buffer field.", fieldDefinition.FullName)); + + var size = (Int32)fixedBufferAttribute.ConstructorArguments[1].Value; + + return size; + } + + public static int PrimitiveTypeSize(TypeReference type) + { + switch (type.MetadataType) + { + case MetadataType.Boolean: + case MetadataType.Byte: + case MetadataType.SByte: + return 1; + + case MetadataType.Char: + case MetadataType.Int16: + case MetadataType.UInt16: + return 2; + + case MetadataType.Int32: + case MetadataType.UInt32: + case MetadataType.Single: + return 4; + + case MetadataType.Int64: + case MetadataType.UInt64: + case MetadataType.Double: + return 8; + + default: + throw new ArgumentException(string.Format("Unsupported {0}", type.MetadataType)); + } + } + + private static bool IsSerializablePrimitive(TypeReference typeReference) + { + switch (typeReference.MetadataType) + { + case MetadataType.SByte: + case MetadataType.Byte: + case MetadataType.Char: + case MetadataType.Int16: + case MetadataType.UInt16: + case MetadataType.Int64: + case MetadataType.UInt64: + case MetadataType.Int32: + case MetadataType.UInt32: + case MetadataType.Single: + case MetadataType.Double: + case MetadataType.Boolean: + case MetadataType.String: + return true; + } + return false; + } + + public static bool IsSupportedCollection(TypeReference typeReference) + { + if (!(typeReference is ArrayType || CecilUtils.IsGenericList(typeReference))) + return false; + + // We don't support arrays like byte[,] etc + if (typeReference.IsArray && ((ArrayType)typeReference).Rank > 1) + return false; + + return IsTypeSerializable(CecilUtils.ElementTypeOfCollection(typeReference)); + } + + private static bool ShouldHaveHadAllFieldsPublic(FieldDefinition field) + { + return UnityEngineTypePredicates.IsUnityEngineValueType(field.DeclaringType); + } + + private static bool IsUnityEngineObject(TypeReference typeReference) + { + return UnityEngineTypePredicates.IsUnityEngineObject(typeReference); + } + + public static bool IsNonSerialized(TypeReference typeDeclaration) + { + if (typeDeclaration == null) + return true; + if (typeDeclaration.HasGenericParameters) + return true; + if (typeDeclaration.MetadataType == MetadataType.Object) + return true; + var fullName = typeDeclaration.FullName; + if (fullName.StartsWith("System.")) //can this be done better? + return true; + if (typeDeclaration.IsArray) + return true; + if (typeDeclaration.FullName == UnityEngineTypePredicates.MonoBehaviour) + return true; + if (typeDeclaration.FullName == UnityEngineTypePredicates.ScriptableObject) + return true; + if (typeDeclaration.IsEnum()) + return true; + return false; + } + + public static bool ShouldImplementIDeserializable(TypeReference typeDeclaration) + { + if (typeDeclaration.FullName == "UnityEngine.ExposedReference`1") + return true; + + if (IsNonSerialized(typeDeclaration)) + return false; + + try + { + if (UnityEngineTypePredicates.ShouldHaveHadSerializableAttribute(typeDeclaration)) + return true; + + var resolvedTypeDeclaration = typeDeclaration.CheckedResolve(); + if (resolvedTypeDeclaration.IsValueType) + { + return resolvedTypeDeclaration.IsSerializable && !resolvedTypeDeclaration.CustomAttributes.Any(a => a.AttributeType.FullName.Contains("System.Runtime.CompilerServices.CompilerGenerated")); + } + else + { + return (resolvedTypeDeclaration.IsSerializable && !resolvedTypeDeclaration.CustomAttributes.Any(a => a.AttributeType.FullName.Contains("System.Runtime.CompilerServices.CompilerGenerated"))) || + resolvedTypeDeclaration.IsSubclassOf(UnityEngineTypePredicates.MonoBehaviour, UnityEngineTypePredicates.ScriptableObject); + } + } + catch (Exception) + { + return false; + } + } + } +} diff --git a/AssetStudioUtility/packages.config b/AssetStudioUtility/packages.config new file mode 100644 index 0000000..85c0c98 --- /dev/null +++ b/AssetStudioUtility/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file