public void Decompile(StreamWriter writer, Config config) { //dump image for (var imageIndex = 0; imageIndex < metadata.imageDefs.Length; imageIndex++) { var imageDef = metadata.imageDefs[imageIndex]; writer.Write($"// Image {imageIndex}: {metadata.GetStringFromIndex(imageDef.nameIndex)} - {imageDef.typeStart}\n"); } //dump type for (var imageIndex = 0; imageIndex < metadata.imageDefs.Length; imageIndex++) { try { var imageDef = metadata.imageDefs[imageIndex]; var typeEnd = imageDef.typeStart + imageDef.typeCount; for (int idx = imageDef.typeStart; idx < typeEnd; idx++) { var typeDef = metadata.typeDefs[idx]; var isStruct = false; var isEnum = false; var extends = new List <string>(); if (typeDef.parentIndex >= 0) { var parent = il2Cpp.types[typeDef.parentIndex]; var parentName = GetTypeName(parent); if (parentName == "ValueType") { isStruct = true; } else if (parentName == "Enum") { isEnum = true; } else if (parentName != "object") { extends.Add(parentName); } } if (typeDef.interfaces_count > 0) { for (int i = 0; i < typeDef.interfaces_count; i++) { var @interface = il2Cpp.types[metadata.interfaceIndices[typeDef.interfacesStart + i]]; extends.Add(GetTypeName(@interface)); } } writer.Write($"\n// Namespace: {metadata.GetStringFromIndex(typeDef.namespaceIndex)}\n"); if (config.DumpAttribute) { writer.Write(GetCustomAttribute(imageDef, typeDef.customAttributeIndex, typeDef.token)); } if (config.DumpAttribute && (typeDef.flags & TYPE_ATTRIBUTE_SERIALIZABLE) != 0) { writer.Write("[Serializable]\n"); } var visibility = typeDef.flags & TYPE_ATTRIBUTE_VISIBILITY_MASK; switch (visibility) { case TYPE_ATTRIBUTE_PUBLIC: case TYPE_ATTRIBUTE_NESTED_PUBLIC: writer.Write("public "); break; case TYPE_ATTRIBUTE_NOT_PUBLIC: case TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM: case TYPE_ATTRIBUTE_NESTED_ASSEMBLY: writer.Write("internal "); break; case TYPE_ATTRIBUTE_NESTED_PRIVATE: writer.Write("private "); break; case TYPE_ATTRIBUTE_NESTED_FAMILY: writer.Write("protected "); break; case TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM: writer.Write("protected internal "); break; } if ((typeDef.flags & TYPE_ATTRIBUTE_ABSTRACT) != 0 && (typeDef.flags & TYPE_ATTRIBUTE_SEALED) != 0) { writer.Write("static "); } else if ((typeDef.flags & TYPE_ATTRIBUTE_INTERFACE) == 0 && (typeDef.flags & TYPE_ATTRIBUTE_ABSTRACT) != 0) { writer.Write("abstract "); } else if (!isStruct && !isEnum && (typeDef.flags & TYPE_ATTRIBUTE_SEALED) != 0) { writer.Write("sealed "); } if ((typeDef.flags & TYPE_ATTRIBUTE_INTERFACE) != 0) { writer.Write("interface "); } else if (isStruct) { writer.Write("struct "); } else if (isEnum) { writer.Write("enum "); } else { writer.Write("class "); } var typeName = GetTypeName(typeDef); writer.Write($"{typeName}"); if (extends.Count > 0) { writer.Write($" : {string.Join(", ", extends)}"); } if (config.DumpTypeDefIndex) { writer.Write($" // TypeDefIndex: {idx}\n{{"); } else { writer.Write("\n{"); } //dump field if (config.DumpField && typeDef.field_count > 0) { writer.Write("\n\t// Fields\n"); var fieldEnd = typeDef.fieldStart + typeDef.field_count; for (var i = typeDef.fieldStart; i < fieldEnd; ++i) { var fieldDef = metadata.fieldDefs[i]; var fieldType = il2Cpp.types[fieldDef.typeIndex]; var fieldDefaultValue = metadata.GetFieldDefaultValueFromIndex(i); if (config.DumpAttribute) { writer.Write(GetCustomAttribute(imageDef, fieldDef.customAttributeIndex, fieldDef.token, "\t")); } writer.Write("\t"); var access = fieldType.attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK; switch (access) { case FIELD_ATTRIBUTE_PRIVATE: writer.Write("private "); break; case FIELD_ATTRIBUTE_PUBLIC: writer.Write("public "); break; case FIELD_ATTRIBUTE_FAMILY: writer.Write("protected "); break; case FIELD_ATTRIBUTE_ASSEMBLY: case FIELD_ATTRIBUTE_FAM_AND_ASSEM: writer.Write("internal "); break; case FIELD_ATTRIBUTE_FAM_OR_ASSEM: writer.Write("protected internal "); break; } if ((fieldType.attrs & FIELD_ATTRIBUTE_LITERAL) != 0) { writer.Write("const "); } else { if ((fieldType.attrs & FIELD_ATTRIBUTE_STATIC) != 0) { writer.Write("static "); } if ((fieldType.attrs & FIELD_ATTRIBUTE_INIT_ONLY) != 0) { writer.Write("readonly "); } } writer.Write($"{GetTypeName(fieldType)} {metadata.GetStringFromIndex(fieldDef.nameIndex)}"); if (fieldDefaultValue != null && fieldDefaultValue.dataIndex != -1) { if (TryGetDefaultValue(fieldDefaultValue.typeIndex, fieldDefaultValue.dataIndex, out var value)) { writer.Write($" = "); if (value is string str) { writer.Write($"\"{str.ToEscapedString()}\""); } else if (value is char c) { var v = (int)c; writer.Write($"'\\x{v:x}'"); } else if (value != null) { writer.Write($"{value}"); } } else { writer.Write($" /*Metadata offset 0x{value:X}*/"); } } if (config.DumpFieldOffset) { writer.Write("; // 0x{0:X}\n", il2Cpp.GetFieldOffsetFromIndex(idx, i - typeDef.fieldStart, i)); } else { writer.Write(";\n"); } } } //dump property if (config.DumpProperty && typeDef.property_count > 0) { writer.Write("\n\t// Properties\n"); var propertyEnd = typeDef.propertyStart + typeDef.property_count; for (var i = typeDef.propertyStart; i < propertyEnd; ++i) { var propertyDef = metadata.propertyDefs[i]; if (config.DumpAttribute) { writer.Write(GetCustomAttribute(imageDef, propertyDef.customAttributeIndex, propertyDef.token, "\t")); } writer.Write("\t"); if (propertyDef.get >= 0) { var methodDef = metadata.methodDefs[typeDef.methodStart + propertyDef.get]; writer.Write(GetModifiers(methodDef)); var propertyType = il2Cpp.types[methodDef.returnType]; writer.Write($"{GetTypeName(propertyType)} {metadata.GetStringFromIndex(propertyDef.nameIndex)} {{ "); } else if (propertyDef.set > 0) { var methodDef = metadata.methodDefs[typeDef.methodStart + propertyDef.set]; writer.Write(GetModifiers(methodDef)); var parameterDef = metadata.parameterDefs[methodDef.parameterStart]; var propertyType = il2Cpp.types[parameterDef.typeIndex]; writer.Write($"{GetTypeName(propertyType)} {metadata.GetStringFromIndex(propertyDef.nameIndex)} {{ "); } if (propertyDef.get >= 0) { writer.Write("get; "); } if (propertyDef.set >= 0) { writer.Write("set; "); } writer.Write("}"); writer.Write("\n"); } } //dump method if (config.DumpMethod && typeDef.method_count > 0) { writer.Write("\n\t// Methods\n"); var methodEnd = typeDef.methodStart + typeDef.method_count; for (var i = typeDef.methodStart; i < methodEnd; ++i) { var methodDef = metadata.methodDefs[i]; if (config.DumpAttribute) { writer.Write(GetCustomAttribute(imageDef, methodDef.customAttributeIndex, methodDef.token, "\t")); } writer.Write("\t"); if (methodDef.slot != 0xffff) { writer.Write("/* [{0,2}:0x{1:X4}] */ ", methodDef.slot, il2Cpp.GetVTableOffsetFromSlot(methodDef.slot)); } writer.Write(GetModifiers(methodDef)); var methodReturnType = il2Cpp.types[methodDef.returnType]; var methodName = metadata.GetStringFromIndex(methodDef.nameIndex); if (methodReturnType.byref == 1) { writer.Write("ref "); } writer.Write($"{GetTypeName(methodReturnType)} {methodName}("); var parameterStrs = new List <string>(); for (var j = 0; j < methodDef.parameterCount; ++j) { var parameterStr = ""; var parameterDef = metadata.parameterDefs[methodDef.parameterStart + j]; var parameterName = metadata.GetStringFromIndex(parameterDef.nameIndex); var parameterType = il2Cpp.types[parameterDef.typeIndex]; var parameterTypeName = GetTypeName(parameterType); if (parameterType.byref == 1) { if ((parameterType.attrs & PARAM_ATTRIBUTE_OUT) != 0 && (parameterType.attrs & PARAM_ATTRIBUTE_IN) == 0) { parameterStr += "out "; } else if ((parameterType.attrs & PARAM_ATTRIBUTE_OUT) == 0 && (parameterType.attrs & PARAM_ATTRIBUTE_IN) != 0) { parameterStr += "in "; } else { parameterStr += "ref "; } } else { if ((parameterType.attrs & PARAM_ATTRIBUTE_IN) != 0) { parameterStr += "[In] "; } if ((parameterType.attrs & PARAM_ATTRIBUTE_OUT) != 0) { parameterStr += "[Out] "; } } parameterStr += $"{parameterTypeName} {parameterName}"; var parameterDefault = metadata.GetParameterDefaultValueFromIndex(methodDef.parameterStart + j); if (parameterDefault != null && parameterDefault.dataIndex != -1) { if (TryGetDefaultValue(parameterDefault.typeIndex, parameterDefault.dataIndex, out var value)) { parameterStr += " = "; if (value is string str) { parameterStr += $"\"{str.ToEscapedString()}\""; } else if (value is char c) { var v = (int)c; parameterStr += $"'\\x{v:x}'"; } else if (value != null) { parameterStr += $"{value}"; } } else { parameterStr += $" /*Metadata offset 0x{value:X}*/"; } } parameterStrs.Add(parameterStr); } writer.Write(string.Join(", ", parameterStrs)); writer.Write(") { }"); if (config.DumpMethodOffset) { var methodPointer = il2Cpp.GetMethodPointer(methodDef.methodIndex, i, imageIndex, methodDef.token); if (methodPointer > 0) { var fixedMethodPointer = il2Cpp.FixPointer(methodPointer); writer.Write(" // RVA: 0x{0:X} Offset: 0x{1:X}\n", fixedMethodPointer, il2Cpp.MapVATR(methodPointer)); } else { writer.Write(" // -1\n"); } } else { writer.Write("\n"); } } } writer.Write("}\n"); } } catch (Exception e) { Console.WriteLine("ERROR: Some errors in dumping"); writer.Write("/*"); writer.Write(e); writer.Write("*/\n}\n"); } } writer.Close(); }