diff --git a/Il2CppDumper/Il2CppCSharpDumper.cs b/Il2CppDumper/Il2CppCSharpDumper.cs index c913489..89507ba 100644 --- a/Il2CppDumper/Il2CppCSharpDumper.cs +++ b/Il2CppDumper/Il2CppCSharpDumper.cs @@ -84,6 +84,8 @@ namespace Il2CppInspector var del = type.GetMethod("Invoke"); // IL2CPP doesn't seem to retain return type attributes //writer.Write(del.ReturnType.CustomAttributes.ToString(prefix, "return: ")); + if (del.RequiresUnsafeContext) + writer.Write("unsafe "); writer.Write($"delegate {del.ReturnType.CSharpName} {type.CSharpTypeDeclarationName}("); writer.Write(del.GetParametersString()); writer.Write($"); // TypeDefIndex: {type.Index}; {del.VirtualAddress.ToAddressString()}\n"); diff --git a/Il2CppInspector/Reflection/FieldInfo.cs b/Il2CppInspector/Reflection/FieldInfo.cs index a802a09..11efb74 100644 --- a/Il2CppInspector/Reflection/FieldInfo.cs +++ b/Il2CppInspector/Reflection/FieldInfo.cs @@ -128,6 +128,8 @@ namespace Il2CppInspector.Reflection { public string GetModifierString() { var modifiers = new StringBuilder(GetAccessModifierString()); + if (FieldType.RequiresUnsafeContext || GetCustomAttributes("System.Runtime.CompilerServices.FixedBufferAttribute").Any()) + modifiers.Append("unsafe "); if (IsLiteral) modifiers.Append("const "); // All const fields are also static by implication diff --git a/Il2CppInspector/Reflection/MethodBase.cs b/Il2CppInspector/Reflection/MethodBase.cs index 026eff8..80f6d52 100644 --- a/Il2CppInspector/Reflection/MethodBase.cs +++ b/Il2CppInspector/Reflection/MethodBase.cs @@ -47,6 +47,8 @@ namespace Il2CppInspector.Reflection public bool IsStatic => (Attributes & MethodAttributes.Static) == MethodAttributes.Static; public bool IsVirtual => (Attributes & MethodAttributes.Virtual) == MethodAttributes.Virtual; + public virtual bool RequiresUnsafeContext => DeclaredParameters.Any(p => p.ParameterType.RequiresUnsafeContext); + // TODO: GetMethodBody() public string CSharpName => @@ -132,6 +134,8 @@ namespace Il2CppInspector.Reflection var modifiers = new StringBuilder(GetAccessModifierString()); + if (RequiresUnsafeContext) + modifiers.Append("unsafe "); if (IsAbstract) modifiers.Append("abstract "); // Methods that implement interfaces are IsVirtual && IsFinal with MethodAttributes.NewSlot (don't show 'virtual sealed' for these) diff --git a/Il2CppInspector/Reflection/MethodInfo.cs b/Il2CppInspector/Reflection/MethodInfo.cs index e59da54..f5e67e3 100644 --- a/Il2CppInspector/Reflection/MethodInfo.cs +++ b/Il2CppInspector/Reflection/MethodInfo.cs @@ -20,6 +20,8 @@ namespace Il2CppInspector.Reflection private readonly int returnTypeUsage; public TypeInfo ReturnType => Assembly.Model.GetTypeFromUsage(returnTypeUsage, MemberTypes.TypeInfo); + public override bool RequiresUnsafeContext => base.RequiresUnsafeContext || ReturnType.RequiresUnsafeContext; + // IL2CPP doesn't seem to retain return type custom attributes public MethodInfo(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(pkg, methodIndex, declaringType) { diff --git a/Il2CppInspector/Reflection/TypeInfo.cs b/Il2CppInspector/Reflection/TypeInfo.cs index da5be57..315ea71 100644 --- a/Il2CppInspector/Reflection/TypeInfo.cs +++ b/Il2CppInspector/Reflection/TypeInfo.cs @@ -164,6 +164,9 @@ namespace Il2CppInspector.Reflection { public bool IsSpecialName => (Attributes & TypeAttributes.SpecialName) == TypeAttributes.SpecialName; public bool IsValueType => BaseType?.FullName == "System.ValueType"; + // Helper function for determining if using this type as a field, parameter etc. requires that field or method to be declared as unsafe + public bool RequiresUnsafeContext => IsPointer || (HasElementType && ElementType.RequiresUnsafeContext); + // May get overridden by Il2CppType-based constructor below public override MemberTypes MemberType { get; } = MemberTypes.TypeInfo;