diff --git a/Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs b/Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs index 73817a15..f22168ae 100644 --- a/Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs +++ b/Il2CppInterop.Generator/Contexts/FieldRewriteContext.cs @@ -64,9 +64,13 @@ private string UnmangleFieldName(FieldDefinition field, GeneratorOptions options if (!field.Name.IsObfuscated(options)) { - if (!field.Name.IsInvalidInSource()) - return field.Name!; - return field.Name.FilterInvalidInSourceChars(); + string name = !field.Name.IsInvalidInSource() ? field.Name! : field.Name.FilterInvalidInSourceChars(); + while (field.DeclaringType!.Events.Any(e => e.Name == name) + || field.DeclaringType!.Fields.Any(f => f.Name == name && f != field)) + { + name += "_"; // Backing fields for events have the same name as the event. + } + return name; } if (renamedFieldCounts == null) throw new ArgumentNullException(nameof(renamedFieldCounts)); diff --git a/Il2CppInterop.Generator/Extensions/StringEx.cs b/Il2CppInterop.Generator/Extensions/StringEx.cs index 9d9287e5..4df792e5 100644 --- a/Il2CppInterop.Generator/Extensions/StringEx.cs +++ b/Il2CppInterop.Generator/Extensions/StringEx.cs @@ -61,8 +61,11 @@ public static string FilterInvalidInSourceChars(this Utf8String? str) return str?.Value.FilterInvalidInSourceChars() ?? ""; } - public static bool IsInvalidInSource(this string str) + public static bool IsInvalidInSource([NotNullWhen(true)] this string? str) { + if (str is null) + return false; + for (var i = 0; i < str.Length; i++) { var it = str[i]; @@ -73,9 +76,9 @@ public static bool IsInvalidInSource(this string str) return false; } - public static bool IsInvalidInSource(this Utf8String? str) + public static bool IsInvalidInSource([NotNullWhen(true)] this Utf8String? str) { - return IsInvalidInSource(str?.Value ?? ""); + return IsInvalidInSource(str?.Value); } public static bool IsObfuscated([NotNullWhen(true)] this string? str, GeneratorOptions options) diff --git a/Il2CppInterop.Generator/Passes/Pass71GenerateEvents.cs b/Il2CppInterop.Generator/Passes/Pass71GenerateEvents.cs new file mode 100644 index 00000000..315bdcaa --- /dev/null +++ b/Il2CppInterop.Generator/Passes/Pass71GenerateEvents.cs @@ -0,0 +1,60 @@ +using AsmResolver.DotNet; +using Il2CppInterop.Generator.Contexts; +using Il2CppInterop.Generator.Extensions; + +namespace Il2CppInterop.Generator.Passes; + +public static class Pass71GenerateEvents +{ + public static void DoPass(RewriteGlobalContext context) + { + foreach (var assemblyContext in context.Assemblies) + foreach (var typeContext in assemblyContext.Types) + { + var type = typeContext.OriginalType; + var eventCountsByName = new Dictionary(); + + foreach (var oldEvent in type.Events) + { + var unmangledEventName = UnmangleEventName(assemblyContext, oldEvent, typeContext.NewType, eventCountsByName); + + var eventType = assemblyContext.RewriteTypeRef(oldEvent.EventType?.ToTypeSignature()); + var @event = new EventDefinition(unmangledEventName, oldEvent.Attributes, eventType.ToTypeDefOrRef()); + + typeContext.NewType.Events.Add(@event); + + @event.SetSemanticMethods( + oldEvent.AddMethod is null ? null : typeContext.GetMethodByOldMethod(oldEvent.AddMethod).NewMethod, + oldEvent.RemoveMethod is null ? null : typeContext.GetMethodByOldMethod(oldEvent.RemoveMethod).NewMethod, + oldEvent.FireMethod is null ? null : typeContext.GetMethodByOldMethod(oldEvent.FireMethod).NewMethod); + } + } + } + + private static string UnmangleEventName(AssemblyRewriteContext assemblyContext, EventDefinition @event, + ITypeDefOrRef declaringType, Dictionary countsByBaseName) + { + if (assemblyContext.GlobalContext.Options.PassthroughNames || + !@event.Name.IsObfuscated(assemblyContext.GlobalContext.Options)) return @event.Name!; + + var baseName = "event_" + assemblyContext.RewriteTypeRef(@event.EventType?.ToTypeSignature()).GetUnmangledName(@event.DeclaringType); + + countsByBaseName.TryGetValue(baseName, out var index); + countsByBaseName[baseName] = index + 1; + + var unmangleEventName = baseName + "_" + index; + + if (assemblyContext.GlobalContext.Options.RenameMap.TryGetValue( + declaringType.GetNamespacePrefix() + "." + declaringType.Name + "::" + unmangleEventName, out var newNameByType)) + { + unmangleEventName = newNameByType; + } + else if (assemblyContext.GlobalContext.Options.RenameMap.TryGetValue( + declaringType.GetNamespacePrefix() + "::" + unmangleEventName, out var newName)) + { + unmangleEventName = newName; + } + + return unmangleEventName; + } +} diff --git a/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs b/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs index 3fce73fb..a0044bb8 100644 --- a/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs +++ b/Il2CppInterop.Generator/Passes/Pass80UnstripMethods.cs @@ -124,6 +124,16 @@ public static void DoPass(RewriteGlobalContext context) var property = GetOrCreateProperty(unityMethod, newMethod); property.SetMethod = newMethod; } + else if (unityMethod.IsAddMethod) + { + var @event = GetOrCreateEvent(unityMethod, newMethod); + @event.AddMethod = newMethod; + } + else if (unityMethod.IsRemoveMethod) + { + var @event = GetOrCreateEvent(unityMethod, newMethod); + @event.RemoveMethod = newMethod; + } var paramsMethod = context.CreateParamsMethod(unityMethod, newMethod, imports, type => ResolveTypeInNewAssemblies(context, type, imports)); @@ -171,6 +181,21 @@ private static PropertyDefinition GetOrCreateProperty(MethodDefinition unityMeth return newProperty; } + private static EventDefinition GetOrCreateEvent(MethodDefinition unityMethod, MethodDefinition newMethod) + { + var unityEvent = + unityMethod.DeclaringType!.Events.Single( + it => it.AddMethod == unityMethod || it.RemoveMethod == unityMethod); + var newEvent = newMethod.DeclaringType!.Events.SingleOrDefault(it => it.Name == unityEvent.Name); + if (newEvent == null) + { + newEvent = new EventDefinition(unityEvent.Name, unityEvent.Attributes, newMethod.Signature!.ParameterTypes.Single().ToTypeDefOrRef()); + newMethod.DeclaringType.Events.Add(newEvent); + } + + return newEvent; + } + internal static TypeSignature? ResolveTypeInNewAssemblies(RewriteGlobalContext context, TypeSignature? unityType, RuntimeAssemblyReferences imports) { diff --git a/Il2CppInterop.Generator/Runners/InteropAssemblyGenerator.cs b/Il2CppInterop.Generator/Runners/InteropAssemblyGenerator.cs index b1c66b7c..3744bfb3 100644 --- a/Il2CppInterop.Generator/Runners/InteropAssemblyGenerator.cs +++ b/Il2CppInterop.Generator/Runners/InteropAssemblyGenerator.cs @@ -153,6 +153,11 @@ public void Run(GeneratorOptions options) Pass70GenerateProperties.DoPass(rewriteContext); } + using (new TimingCookie("Creating events")) + { + Pass71GenerateEvents.DoPass(rewriteContext); + } + if (options.UnityBaseLibsDir != null) { using (new TimingCookie("Unstripping types"))