// Generate the C structure for virtual function calls in a given type (the VTable) private CppComplexType GenerateVTableStruct(TypeInfo ti) { MethodBase[] vtable; if (ti.IsInterface) { /* Interface vtables are just all of the interface methods. * You might have to type a local variable manually as an * interface vtable during an interface call, but the result * should display the correct method name (with a computed * InterfaceOffset added). */ vtable = ti.DeclaredMethods.ToArray(); } else { vtable = ti.GetVTable(); } var name = TypeNamer.GetName(ti); var namer = CreateNamespace().MakeNamer <int>((i) => vtable[i]?.Name?.ToCIdentifier() ?? "__unknown"); // Il2Cpp switched to `VirtualInvokeData *vtable` in Unity 5.3.6. // Previous versions used `MethodInfo **vtable`. // TODO: Consider adding function types. This considerably increases the script size // but can significantly help with reverse-engineering certain binaries. var vtableStruct = types.Struct(name + "__VTable"); if (UnityVersion.CompareTo("5.3.6") < 0) { for (int i = 0; i < vtable.Length; i++) { types.AddField(vtableStruct, namer.GetName(i), "MethodInfo *"); } } else { for (int i = 0; i < vtable.Length; i++) { types.AddField(vtableStruct, namer.GetName(i), "VirtualInvokeData"); } } return(vtableStruct); }
private static void CompareTo(UnityVersion l, UnityVersion r, int result) { if (!ReferenceEquals(l, null)) { Assert.AreEqual(Math.Sign(l.CompareTo(r)), Math.Sign(result)); } if (!ReferenceEquals(r, null)) { Assert.AreEqual(Math.Sign(r.CompareTo(l)), -Math.Sign(result)); } }
// Generate a C declaration for a method private string GenerateMethodDeclaration(MethodBase method, string name, TypeInfo declaringType) { string retType; if (method is MethodInfo mi) { retType = mi.ReturnType.FullName == "System.Void" ? "void" : AsCType(mi.ReturnType); } else { retType = "void"; } var paramNs = CreateNamespace(); paramNs.ReserveName("method"); var paramNamer = paramNs.MakeNamer <ParameterInfo>((pi) => pi.Name == "" ? "arg" : pi.Name.ToCIdentifier()); var paramList = new List <string>(); // Figure out the "this" param if (method.IsStatic) { // In older versions, static methods took a dummy this parameter if (UnityVersion.CompareTo("2018.3.0") < 0) { paramList.Add("void *this"); } } else { if (declaringType.IsValueType) { // Methods for structs take the boxed object as the this param paramList.Add($"struct {TypeNamer.GetName(declaringType)}__Boxed * this"); } else { paramList.Add($"{AsCType(declaringType)} this"); } } foreach (var pi in method.DeclaredParameters) { paramList.Add($"{AsCType(pi.ParameterType)} {paramNamer.GetName(pi)}"); } paramList.Add($"struct MethodInfo *method"); return($"{retType} {name}({string.Join(", ", paramList)})"); }
// Generate the overall Il2CppClass-shaped structure for the given type private void GenerateTypeStruct(StringBuilder csrc, TypeInfo ti) { var name = TypeNamer.GetName(ti); GenerateVTableStruct(csrc, ti); csrc.Append($"struct {name}__StaticFields {{\n"); var namer = CreateNamespace().MakeNamer <FieldInfo>((field) => field.Name.ToCIdentifier()); foreach (var field in ti.DeclaredFields) { if (field.IsLiteral || !field.IsStatic) { continue; } csrc.Append($" {AsCType(field.FieldType)} {namer.GetName(field)};\n"); } csrc.Append($"}};\n"); /* TODO: type the rgctx_data */ if (UnityVersion.CompareTo("5.5.0") < 0) { csrc.Append( $"struct {name}__Class {{\n" + $" struct Il2CppClass_0 _0;\n" + $" struct {name}__VTable *vtable;\n" + $" Il2CppRuntimeInterfaceOffsetPair *interfaceOffsets;\n" + $" struct {name}__StaticFields *static_fields;\n" + $" const Il2CppRGCTXData *rgctx_data;\n" + $" struct Il2CppClass_1 _1;\n" + $"}};\n"); } else { csrc.Append( $"struct {name}__Class {{\n" + $" struct Il2CppClass_0 _0;\n" + $" Il2CppRuntimeInterfaceOffsetPair *interfaceOffsets;\n" + $" struct {name}__StaticFields *static_fields;\n" + $" const Il2CppRGCTXData *rgctx_data;\n" + $" struct Il2CppClass_1 _1;\n" + $" struct {name}__VTable vtable;\n" + $"}};\n"); } }
// Determine if this range contains the specified version public bool Contains(UnityVersion version) => version.CompareTo(Min) >= 0 && (Max == null || version.CompareTo(Max) <= 0);