private int GetFieldOffset(Il2Cpp il2Cpp, Metadata metadata, Il2CppExecutor executor, string classname, string membername) { int typeDefIndex = 0; Il2CppTypeDefinition typeDef = FindClassEntry(metadata, executor, classname, out typeDefIndex); if (typeDef == null) { return(0); } var fieldEnd = typeDef.fieldStart + typeDef.field_count; for (var i = typeDef.fieldStart; i < fieldEnd; ++i) { var isStatic = false; var fieldDef = metadata.fieldDefs[i]; var fieldType = il2Cpp.types[fieldDef.typeIndex]; if ((fieldType.attrs & FIELD_ATTRIBUTE_STATIC) != 0) { isStatic = true; } string memberName = metadata.GetStringFromIndex(fieldDef.nameIndex); if (memberName == membername) { return(il2Cpp.GetFieldOffsetFromIndex(typeDefIndex, i - typeDef.fieldStart, i, typeDef.IsValueType, isStatic)); } } throw new Exception("Field " + membername + " not found in " + classname); }
private int GetMethodAddress(Il2Cpp il2Cpp, Metadata metadata, Il2CppExecutor executor, string methodType, string methodName) { if (il2Cpp.Version >= 27) { var sectionHelper = executor.GetSectionHelper(); foreach (var sec in sectionHelper.data) { il2Cpp.Position = sec.offset; while (il2Cpp.Position < sec.offsetEnd - il2Cpp.PointerSize) { var addr = il2Cpp.Position; var metadataValue = il2Cpp.ReadUIntPtr(); var position = il2Cpp.Position; if (metadataValue < uint.MaxValue) { var encodedToken = (uint)metadataValue; var usage = metadata.GetEncodedIndexType(encodedToken); if (usage > 0 && usage <= 6) { var decodedIndex = metadata.GetDecodedMethodIndex(encodedToken); if (metadataValue == ((usage << 29) | (decodedIndex << 1)) + 1) { var va = il2Cpp.MapRTVA(addr); if (va > 0) { switch ((Il2CppMetadataUsage)usage) { case Il2CppMetadataUsage.kIl2CppMetadataUsageMethodRef: if (decodedIndex < il2Cpp.methodSpecs.Length) { var methodSpec = il2Cpp.methodSpecs[decodedIndex]; (var methodSpecTypeName, var methodSpecMethodName) = executor.GetMethodSpecName(methodSpec, true); if (methodSpecTypeName == methodType && methodSpecMethodName == methodName) { return((int)il2Cpp.GetRVA(va)); } ; } break; } if (il2Cpp.Position != position) { il2Cpp.Position = position; } } } } } } } } return(0); }
private int GetTypeInfoAddress(Il2Cpp il2Cpp, Metadata metadata, Il2CppExecutor executor, string typeToFind) { if (il2Cpp.Version >= 27) { var sectionHelper = executor.GetSectionHelper(); foreach (var sec in sectionHelper.data) { il2Cpp.Position = sec.offset; while (il2Cpp.Position < sec.offsetEnd - il2Cpp.PointerSize) { var addr = il2Cpp.Position; var metadataValue = il2Cpp.ReadUIntPtr(); var position = il2Cpp.Position; if (metadataValue < uint.MaxValue) { var encodedToken = (uint)metadataValue; var usage = metadata.GetEncodedIndexType(encodedToken); if (usage > 0 && usage <= 6) { var decodedIndex = metadata.GetDecodedMethodIndex(encodedToken); if (metadataValue == ((usage << 29) | (decodedIndex << 1)) + 1) { var va = il2Cpp.MapRTVA(addr); if (va > 0) { switch ((Il2CppMetadataUsage)usage) { case Il2CppMetadataUsage.kIl2CppMetadataUsageTypeInfo: if (decodedIndex < il2Cpp.types.Length) { var type = il2Cpp.types[decodedIndex]; var typeName = executor.GetTypeName(type, true, false); if (typeName.Contains(typeToFind)) { return((int)il2Cpp.GetRVA(va)); } } break; } if (il2Cpp.Position != position) { il2Cpp.Position = position; } } } } } } } } return(0); }
private int GetTypeInfoAddress(Il2Cpp il2Cpp, Metadata metadata, Il2CppExecutor executor, string typeToFind) { foreach (var i in metadata.metadataUsageDic[Il2CppMetadataUsage.kIl2CppMetadataUsageTypeInfo]) { var type = il2Cpp.types[i.Value]; var typeName = executor.GetTypeName(type, true, false); if (typeName.Contains(typeToFind)) { return((int)il2Cpp.GetRVA(il2Cpp.metadataUsages[i.Key])); } } return(0); }
private int GetMethodAddress(Il2Cpp il2Cpp, Metadata metadata, Il2CppExecutor executor, string methodType, string methodName) { foreach (var i in metadata.metadataUsageDic[Il2CppMetadataUsage.kIl2CppMetadataUsageMethodRef]) { var methodSpec = il2Cpp.methodSpecs[i.Value]; (var methodSpecTypeName, var methodSpecMethodName) = executor.GetMethodSpecName(methodSpec, true); if (methodSpecTypeName == methodType && methodSpecMethodName == methodName) { return((int)il2Cpp.GetRVA(il2Cpp.metadataUsages[i.Key])); } } return(0); }
private void Dump(Metadata metadata, Il2Cpp il2Cpp, string outputDir) { WriteOutput("Dumping..."); var executor = new Il2CppExecutor(metadata, il2Cpp); var decompiler = new Il2CppDecompiler(executor); decompiler.Decompile(_config, outputDir, 1); WriteOutput("Done!"); if (_config.GenerateStruct) { WriteOutput("Generate struct..."); var scriptGenerator = new StructGenerator(executor); scriptGenerator.WriteScript(outputDir, 1); WriteOutput("Done!"); } if (_config.GenerateDummyDll) { WriteOutput("Generate dummy dll..."); DummyAssemblyExporter.Export(executor, outputDir, _config.DummyDllAddToken); WriteOutput("Done!"); Directory.SetCurrentDirectory(realPath); //Fix read-only directory permission } }
private bool ExtractCurrentData(string il2cppPath, string metadataPath, out Il2Cpp il2Cpp, out Il2CppExecutor executor, out Metadata metadata) { il2Cpp = null; executor = null; metadata = null; var metadataBytes = File.ReadAllBytes(metadataPath); metadata = new Metadata(new MemoryStream(metadataBytes)); var il2cppBytes = File.ReadAllBytes(il2cppPath); var il2cppMagic = BitConverter.ToUInt32(il2cppBytes, 0); var il2CppMemory = new MemoryStream(il2cppBytes); if (il2cppMagic != IL2CPPMAGIC_PE) { throw new Exception("Unexpected il2cpp magic number."); } il2Cpp = new PE(il2CppMemory); il2Cpp.SetProperties(metadata.Version, metadata.maxMetadataUsages); if (il2Cpp.Version >= 27 && il2Cpp is ElfBase elf && elf.IsDumped) { metadata.Address = Convert.ToUInt64(Console.ReadLine(), 16); } try { var flag = il2Cpp.PlusSearch(metadata.methodDefs.Count(x => x.methodIndex >= 0), metadata.typeDefs.Length, metadata.imageDefs.Length); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { if (!flag && il2Cpp is PE) { il2Cpp = PELoader.Load(il2cppPath); il2Cpp.SetProperties(metadata.Version, metadata.maxMetadataUsages); flag = il2Cpp.PlusSearch(metadata.methodDefs.Count(x => x.methodIndex >= 0), metadata.typeDefs.Length, metadata.imageDefs.Length); } } if (!flag) { flag = il2Cpp.Search(); } if (!flag) { flag = il2Cpp.SymbolSearch(); } if (!flag) { var codeRegistration = Convert.ToUInt64(Console.ReadLine(), 16); var metadataRegistration = Convert.ToUInt64(Console.ReadLine(), 16); il2Cpp.Init(codeRegistration, metadataRegistration); } } catch { throw new Exception("ERROR: An error occurred while processing."); } executor = new Il2CppExecutor(metadata, il2Cpp); return(true); }
public override void CustomizeSelf(AsmDefCSharpProgram program) { Il2Cpp.AddLibIl2CppAsLibraryFor(program.NativeProgram); }
private bool Init(string il2CppPath, string metadataPath, out Metadata metadata, out Il2Cpp il2Cpp) { WriteOutput("Read config...", Color.Black); if (File.Exists(realPath + "config.json")) { _config = JsonConvert.DeserializeObject <Config>(File.ReadAllText(Application.StartupPath + Path.DirectorySeparatorChar + "config.json")); } else { _config = new Config(); WriteOutput("config.json file does not exist. Using defaults", Color.Yellow); } WriteOutput("Initializing metadata..."); var metadataBytes = File.ReadAllBytes(metadataPath); metadata = new Metadata(new MemoryStream(metadataBytes)); WriteOutput($"Metadata Version: {metadata.Version}"); WriteOutput("Initializing il2cpp file..."); var il2CppBytes = File.ReadAllBytes(il2CppPath); var il2CppMagic = BitConverter.ToUInt32(il2CppBytes, 0); var il2CppMemory = new MemoryStream(il2CppBytes); switch (il2CppMagic) { default: WriteOutput("ERROR: il2cpp file not supported."); throw new NotSupportedException("ERROR: il2cpp file not supported."); case 0x6D736100: var web = new WebAssembly(il2CppMemory); il2Cpp = web.CreateMemory(); break; case 0x304F534E: var nso = new NSO(il2CppMemory); il2Cpp = nso.UnCompress(); break; case 0x905A4D: //PE il2Cpp = new PE(il2CppMemory); break; case 0x464c457f: //ELF if (il2CppBytes[4] == 2) //ELF64 { var addressValue = ""; il2Cpp = InputBox.Show("Input il2cpp dump address or leave empty to force continue:", "", ref addressValue) != DialogResult.OK ? string.IsNullOrWhiteSpace(addressValue) ? new Elf64(il2CppMemory) : new Elf64(il2CppMemory, addressValue) : new Elf64(il2CppMemory); } else { var addressValue = ""; il2Cpp = InputBox.Show("Input il2cpp dump address or leave empty to force continue:", "", ref addressValue) != DialogResult.OK ? string.IsNullOrWhiteSpace(addressValue) ? new Elf(il2CppMemory) : new Elf(il2CppMemory, addressValue) : new Elf(il2CppMemory); } break; case 0xCAFEBABE: //FAT Mach-O case 0xBEBAFECA: var machofat = new MachoFat(new MemoryStream(il2CppBytes)); WriteOutput("Select Platform: "); for (var i = 0; i < machofat.fats.Length; i++) { var fat = machofat.fats[i]; WriteOutput(fat.magic == 0xFEEDFACF ? $"{i + 1}.64bit " : $"{i + 1}.32bit "); } WriteOutput(""); var key = Console.ReadKey(true); var index = int.Parse(key.KeyChar.ToString()) - 1; var magic = machofat.fats[index % 2].magic; il2CppBytes = machofat.GetMacho(index % 2); il2CppMemory = new MemoryStream(il2CppBytes); if (magic == 0xFEEDFACF) { goto case 0xFEEDFACF; } else { goto case 0xFEEDFACE; } case 0xFEEDFACF: // 64bit Mach-O il2Cpp = new Macho64(il2CppMemory); break; case 0xFEEDFACE: // 32bit Mach-O il2Cpp = new Macho(il2CppMemory); break; } var version = _config.ForceIl2CppVersion ? _config.ForceVersion : metadata.Version; il2Cpp.SetProperties(version, metadata.maxMetadataUsages); WriteOutput($"Il2Cpp Version: {il2Cpp.Version}"); if (il2Cpp.Version >= 27 && il2Cpp is ElfBase elf && elf.IsDumped) { var metadataValue = ""; if (InputBox.Show("Input global-metadata.dat dump address:", "", ref metadataValue) != DialogResult.OK) { return(false); } metadata.Address = Convert.ToUInt64(metadataValue, 16); WriteOutput($"global-metadata.dat dump address: {metadataValue}"); } WriteOutput("Searching..."); try { var flag = il2Cpp.PlusSearch(metadata.methodDefs.Count(x => x.methodIndex >= 0), metadata.typeDefs.Length, metadata.imageDefs.Length); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !flag && il2Cpp is PE) { WriteOutput("Use custom PE loader"); il2Cpp = PELoader.Load(il2CppPath); il2Cpp.SetProperties(version, metadata.maxMetadataUsages); flag = il2Cpp.PlusSearch(metadata.methodDefs.Count(x => x.methodIndex >= 0), metadata.typeDefs.Length, metadata.imageDefs.Length); } if (!flag) { flag = il2Cpp.Search(); } if (!flag) { flag = il2Cpp.SymbolSearch(); } if (!flag) { WriteOutput("ERROR: Can't use auto mode to process file, try manual mode."); WriteOutput("Input CodeRegistration: "); var codeValue = ""; if (InputBox.Show(@"Input CodeRegistration: ", "", ref codeValue) != DialogResult.OK) { return(false); } var codeRegistration = Convert.ToUInt64(codeValue, 16); WriteOutput($"CodeRegistration: {codeValue}"); var metadataValue = ""; if (InputBox.Show("Input MetadataRegistration: ", "", ref metadataValue) != DialogResult.OK) { return(false); } var metadataRegistration = Convert.ToUInt64(metadataValue, 16); WriteOutput($"MetadataRegistration: {metadataValue}"); il2Cpp.Init(codeRegistration, metadataRegistration); return(true); } } catch (Exception e) { WriteOutput(e.Message); WriteOutput("ERROR: An error occurred while processing."); return(false); } return(true); }
private static DotsRuntimeCSharpProgram SetupGame(AsmDefDescription game) { var gameProgram = GetOrMakeDotsRuntimeCSharpProgramFor(game); var withoutExt = new NPath(gameProgram.FileName).FileNameWithoutExtension; NPath exportManifest = new NPath(withoutExt + "/export.manifest"); Backend.Current.RegisterFileInfluencingGraph(exportManifest); if (exportManifest.FileExists()) { var dataFiles = exportManifest.MakeAbsolute().ReadAllLines(); foreach (var dataFile in dataFiles.Select(d => new NPath(d))) { gameProgram.SupportFiles.Add(new DeployableFile(dataFile, "Data/" + dataFile.FileName)); } } var configToSetupGame = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>(); if (!PerConfigBuildSettings.ContainsKey(game.Name)) { return(null); } foreach (var config in PerConfigBuildSettings[game.Name].Where(config => !CanSkipSetupOf(game.Name, config))) { gameProgram.ProjectFile.StartInfo.Add(c => c == config, StartInfoFor(config, EntryPointExecutableFor(gameProgram, config))); gameProgram.ProjectFile.BuildCommand.Add(c => c == config, new BeeBuildCommand(GameDeployBinaryFor(gameProgram, config).ToString(), false, false).ToExecuteArgs()); DotNetAssembly setupGame = gameProgram.SetupSpecificConfiguration(config).WithDeployables(new DotNetAssembly( Il2Cpp.Distribution.Path.Combine("build/profiles/Tiny/Facades/netstandard.dll"), Framework.FrameworkNone)); var postILProcessedGame = ILPostProcessorTool.SetupInvocation(setupGame, config, gameProgram.Defines.For(config).ToArray()); var postTypeRegGenGame = TypeRegistrationTool.SetupInvocation(postILProcessedGame, config); configToSetupGame[config] = postTypeRegGenGame; } ; var il2CppOutputProgram = new Il2Cpp.Il2CppOutputProgram(gameProgram.AsmDefDescription.Name.Replace(".", "-") + ".il2cpp"); var configToSetupGameBursted = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>(); BurstCompiler hostBurstCompiler = null; if (HostPlatform.IsWindows) { hostBurstCompiler = new BurstCompilerForWindows64(); hostBurstCompiler.Link = true; hostBurstCompiler.OnlyStaticMethods = true; } else if (HostPlatform.IsOSX) { hostBurstCompiler = new BurstCompilerForMac(); hostBurstCompiler.Link = true; hostBurstCompiler.OnlyStaticMethods = true; } else { Console.WriteLine("Tiny burst not yet supported on linux."); } var webBurstCompiler = new BurstCompilerForEmscripten(); foreach (var kvp in configToSetupGame) { var config = kvp.Key; var setupGame = kvp.Value; if (config.UseBurst) { var outputDir = $"artifacts/{game.Name}/{config.Identifier}_bursted"; var isWebGL = config.Platform is WebGLPlatform; var extension = config.NativeProgramConfiguration.ToolChain.DynamicLibraryFormat.Extension; //burst generates a .bundle on os x. if (config.Platform is MacOSXPlatform) { extension = "bundle"; } var burstlib = BurstCompiler.SetupBurstCompilationAndLinkForAssemblies( isWebGL ? webBurstCompiler : hostBurstCompiler, setupGame, new NPath(outputDir).Combine( $"lib_burst_generated{("."+extension) ?? ""}"), outputDir, out var burstedGame); if (isWebGL) { il2CppOutputProgram.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib); } else { burstedGame = burstedGame.WithDeployables(burstlib); } configToSetupGameBursted[config] = burstedGame; } else { configToSetupGameBursted[config] = setupGame; } } var configToSetupGameStripped = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>(); foreach (var kvp in configToSetupGameBursted) { var config = kvp.Key; var setupGame = kvp.Value; if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp) { setupGame = Il2Cpp.UnityLinker.SetupInvocation(setupGame, $"artifacts/{game.Name}/{config.Identifier}_stripped", config.NativeProgramConfiguration); il2CppOutputProgram.SetupConditionalSourcesAndLibrariesForConfig(config, setupGame); configToSetupGameStripped[kvp.Key] = setupGame; } else { configToSetupGameStripped[kvp.Key] = kvp.Value; } } foreach (var kvp in configToSetupGameStripped) { var config = kvp.Key; var setupGame = kvp.Value; NPath deployPath = GameDeployDirectoryFor(gameProgram, config); IDeployable deployedGame; NPath entryPointExecutable = null; if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp) { var builtNativeProgram = il2CppOutputProgram.SetupSpecificConfiguration( config.NativeProgramConfiguration, config.NativeProgramConfiguration.ExecutableFormat ) .WithDeployables(setupGame.RecursiveRuntimeDependenciesIncludingSelf.SelectMany(a => a.Deployables.Where(d => !(d is DotNetAssembly) && !(d is StaticLibrary))) .ToArray()); if (builtNativeProgram is IPackagedAppExtension) { (builtNativeProgram as IPackagedAppExtension).SetAppPackagingParameters(gameProgram.AsmDefDescription.Name, config.NativeProgramConfiguration.CodeGen, gameProgram.SupportFiles.For(config)); } deployedGame = builtNativeProgram.DeployTo(deployPath); entryPointExecutable = deployedGame.Path; if (config.EnableManagedDebugging) { Backend.Current.AddDependency(deployedGame.Path, Il2Cpp.CopyIL2CPPMetadataFile(deployPath, setupGame)); } } else { deployedGame = setupGame.DeployTo(deployPath); var dotNetAssembly = (DotNetAssembly)deployedGame; //Usually a dotnet runtime game does not have a static void main(), and instead references another "entrypoint asmdef" that provides it. //This is convenient, but what makes it weird is that you have to start YourEntryPoint.exe instead of YourGame.exe. Until we have a better //solution for this, we're going to copy YourEntryPoint.exe to YourGame.exe, so that it's easier to find, and so that when it runs and you look //at the process name you understand what it is. if (deployedGame.Path.HasExtension("dll")) { var to = deployPath.Combine(deployedGame.Path.ChangeExtension("exe").FileName); var from = dotNetAssembly.RecursiveRuntimeDependenciesIncludingSelf.SingleOrDefault(a => a.Path.HasExtension("exe"))?.Path; if (from == null) { throw new InvalidProgramException($"Program {dotNetAssembly.Path} is an executable-like thing, but doesn't reference anything with Main"); } Backend.Current.AddDependency(deployedGame.Path, CopyTool.Instance().Setup(to, from)); entryPointExecutable = to; } else { entryPointExecutable = deployedGame.Path; } } //Because we use multidag, and try to not run all the setupcode when we just want to create projectfiles, we have a bit of a challenge. //Projectfiles require exact start and build commands. So we need to have a cheap way to calculate those. However, it's important that they //exactly match the actual place where the buildprogram is going to place our files. If these don't match things break down. The checks //in this block, they compare the "quick way to determine where the binary will be placed, and what the start executable is", with the //actual return values returned from .DeployTo(), when we do run the actual buildcode. NPath deployedGamePath = GameDeployBinaryFor(gameProgram, config); if (deployedGame.Path != deployedGamePath) { throw new InvalidProgramException($"We expected deployPath to be {deployedGamePath}, but in reality it was {deployedGame.Path}"); } var expectedEntryPointExecutable = EntryPointExecutableFor(gameProgram, config); if (entryPointExecutable != expectedEntryPointExecutable) { throw new InvalidProgramException($"We expected entryPointExecutable to be {expectedEntryPointExecutable}, but in reality it was {entryPointExecutable}"); } Backend.Current.AddAliasDependency(config.Identifier, deployedGamePath); } return(gameProgram); }
private static DotsRuntimeCSharpProgram SetupGame(AsmDefDescription game) { var gameProgram = GetOrMakeDotsRuntimeCSharpProgramFor(game); var configToSetupGame = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>(); if (!PerConfigBuildSettings.ContainsKey(game.Name)) { return(null); } var configsToUse = PerConfigBuildSettings[game.Name].Where(config => !CanSkipSetupOf(game.Name, config)); foreach (var config in configsToUse) { var withoutExt = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier); NPath exportManifest = withoutExt.Combine("export.manifest"); Backend.Current.RegisterFileInfluencingGraph(exportManifest); if (exportManifest.FileExists()) { var dataFiles = exportManifest.MakeAbsolute().ReadAllLines(); foreach (var dataFile in dataFiles.Select(d => new NPath(d))) { gameProgram.SupportFiles.Add( c => c.Equals(config), new DeployableFile(dataFile, GetDeployPathFromExportPath(dataFile))); } } gameProgram.ProjectFile.StartInfo.Add( c => c == config, StartInfoFor(config, EntryPointExecutableFor(gameProgram, config))); gameProgram.ProjectFile.BuildCommand.Add( c => c == config, new BeeBuildCommand(GameDeployBinaryFor(gameProgram, config).ToString(), false, false).ToExecuteArgs()); } foreach (var config in configsToUse) { DotNetAssembly setupGame = gameProgram.SetupSpecificConfiguration(config); if (config.TargetFramework == TargetFramework.Tiny) { var tinyStandard = new DotNetAssembly(Il2Cpp.Distribution.Path.Combine("build/profiles/Tiny/Facades/netstandard.dll"), Framework.FrameworkNone); setupGame = setupGame.WithDeployables(tinyStandard); } var postILProcessedGame = ILPostProcessorTool.SetupInvocation( setupGame, config, gameProgram.Defines.For(config).ToArray()); var postTypeRegGenGame = TypeRegistrationTool.SetupInvocation(postILProcessedGame, config); configToSetupGame[config] = postTypeRegGenGame; } var il2CppOutputProgram = new Il2Cpp.Il2CppOutputProgram(gameProgram.AsmDefDescription.Name); var configToSetupGameBursted = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>(); foreach (var kvp in configToSetupGame) { var config = kvp.Key; var setupGame = kvp.Value; if (config.UseBurst) { BurstCompiler burstCompiler = null; if (config.Platform is WindowsPlatform) { burstCompiler = new BurstCompilerForWindows64(); burstCompiler.Link = false; } else if (config.Platform is MacOSXPlatform) { burstCompiler = new BurstCompilerForMac(); burstCompiler.Link = false; } else if (config.Platform is IosPlatform) { burstCompiler = new BurstCompilerForiOS(); burstCompiler.EnableStaticLinkage = true; burstCompiler.ObjectFileExtension = "a"; } else if (config.Platform is LinuxPlatform) { burstCompiler = new BurstCompilerForLinuxWaitingForBurstRelease(); } else if (config.Platform is AndroidPlatform) { burstCompiler = new BurstCompilerForAndroid(); burstCompiler.EnableStaticLinkage = false; burstCompiler.Link = false; burstCompiler.EnableDirectExternalLinking = true; if (config.NativeProgramConfiguration.ToolChain.Architecture is Arm64Architecture) { burstCompiler.TargetArchitecture = "ARMV8A_AARCH64"; } } else if (config.Platform is WebGLPlatform) { burstCompiler = new BurstCompilerForEmscripten(); burstCompiler.EnableStaticLinkage = true; burstCompiler.DisableVectors = false; } // Only generate marshaling info for platforms that require marshalling (e.g. Windows DotNet) // but also if collection checks are enabled (as that is why we need marshalling) burstCompiler.EnableJobMarshalling &= config.EnableUnityCollectionsChecks; burstCompiler.SafetyChecks = config.EnableUnityCollectionsChecks; burstCompiler.DisableWarnings = "BC1370"; // Suppress warning for burst function throwing an exception var outputDir = $"artifacts/{game.Name}/{config.Identifier}_bursted"; var burstLibName = "lib_burst_generated"; DotNetAssembly burstedGame = setupGame; var burstlib = BurstCompiler.SetupBurstCompilationForAssemblies( burstCompiler, setupGame, new NPath(outputDir).Combine("bclobj"), outputDir, burstLibName, out burstedGame); if ((config.Platform is IosPlatform || config.Platform is AndroidPlatform) && config.NativeProgramConfiguration.ToolChain.DynamicLibraryFormat.Extension == "a") // static lib based toolchain { il2CppOutputProgram.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib); il2CppOutputProgram.Defines.Add( c => c.Equals(config.NativeProgramConfiguration), $"FORCE_PINVOKE_{burstLibName}_INTERNAL"); } else if (config.Platform is WebGLPlatform) { il2CppOutputProgram.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib); } else { var burstDynamicLib = new NativeProgram(burstLibName); burstDynamicLib.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib); burstDynamicLib.Libraries.Add( c => c.Equals(config.NativeProgramConfiguration), gameProgram.TransitiveReferencesFor(config) .Where( p => p is DotsRuntimeCSharpProgram && ((DotsRuntimeCSharpProgram)p).NativeProgram != null) .Select( p => new NativeProgramAsLibrary(((DotsRuntimeCSharpProgram)p).NativeProgram) { BuildMode = NativeProgramLibraryBuildMode.Dynamic })); if (config.Platform is IosPlatform || config.Platform is AndroidPlatform) { NativeJobsPrebuiltLibrary.AddToNativeProgram(burstDynamicLib); } DotsRuntimeCSharpProgram.SetupDotsRuntimeNativeProgram(burstLibName, burstDynamicLib); var builtBurstLib = burstDynamicLib.SetupSpecificConfiguration( config.NativeProgramConfiguration, config.NativeProgramConfiguration.ToolChain.DynamicLibraryFormat); burstedGame = burstedGame.WithDeployables(builtBurstLib); } configToSetupGameBursted[config] = burstedGame; } else { configToSetupGameBursted[config] = setupGame; } } var configToSetupGameStripped = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>(); foreach (var kvp in configToSetupGameBursted) { var config = kvp.Key; var setupGame = kvp.Value; if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp) { setupGame = Il2Cpp.UnityLinker.SetupInvocation(setupGame, $"artifacts/{game.Name}/{config.Identifier}_stripped", config.NativeProgramConfiguration); il2CppOutputProgram.SetupConditionalSourcesAndLibrariesForConfig(config, setupGame); configToSetupGameStripped[kvp.Key] = setupGame; } else { configToSetupGameStripped[kvp.Key] = kvp.Value; } } foreach (var kvp in configToSetupGameStripped) { var config = kvp.Key; var setupGame = kvp.Value; NPath deployPath = GameDeployDirectoryFor(gameProgram, config); IDeployable deployedGame; NPath entryPointExecutable = null; if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp) { var tinyShellFileName = "tiny_shell.html"; NPath tinyShellPath = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier, "WebTemplate", tinyShellFileName); il2CppOutputProgram.DynamicLinkerSettingsForEmscripten().Add(c => c.WithShellFile(tinyShellPath)); var builtNativeProgram = il2CppOutputProgram.SetupSpecificConfiguration( config.NativeProgramConfiguration, config.NativeProgramConfiguration.ExecutableFormat ) .WithDeployables(setupGame.RecursiveRuntimeDependenciesIncludingSelf.SelectMany(a => a.Deployables.Where(d => !(d is DotNetAssembly) && !(d is StaticLibrary))) .ToArray()); if (builtNativeProgram is IPackagedAppExtension) { (builtNativeProgram as IPackagedAppExtension).SetAppPackagingParameters( gameProgram.AsmDefDescription.Name, config.DotsConfiguration); } if (config.PlatformBuildConfig is WebBuildConfig webBuildConfig) { if (webBuildConfig.SingleFile) { deployedGame = new DeployableFile(GameDeployBinaryFor(gameProgram, config)); CopyTool.Instance().Setup(deployedGame.Path, (builtNativeProgram as EmscriptenExecutable).Path); } else { deployedGame = builtNativeProgram.DeployTo(deployPath); } var webTemplateFolder = webBuildConfig.WebTemplateFolder; if (String.IsNullOrEmpty(webTemplateFolder)) { webTemplateFolder = LowLevelRoot.Combine("WebSupport", "WebTemplates", "Default").ToString(); } if (new NPath(webTemplateFolder).IsRelative) { webTemplateFolder = new NPath("../..").Combine(webTemplateFolder).MakeAbsolute().ToString(); } if (!new NPath(webTemplateFolder).Combine(tinyShellFileName).FileExists()) { throw new InvalidProgramException($"Web template folder \"{webTemplateFolder}\" doesn't contain \"{tinyShellFileName}\" file."); } foreach (var templateFilePath in new NPath(webTemplateFolder).Files(recurse:true)) { string fileRelativePath = templateFilePath.ToString().Substring(webTemplateFolder.Length + 1); if (fileRelativePath == tinyShellFileName) { NPath shellPackager = LowLevelRoot.Combine("WebSupport", "package_shell_file.js"); NPath tinyShellJS = LowLevelRoot.Combine("WebSupport", "tiny_shell.js"); var inputs = new List <NPath> { TinyEmscripten.NodeExe, shellPackager, templateFilePath, tinyShellJS }; var commandLineArguments = new List <string> { shellPackager.ToString(), "--outputHtml", tinyShellPath.ToString(), "--inputShellHtml", templateFilePath.ToString(), "--inputShellJs", tinyShellJS.ToString() }; NPath exportManifest = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier, "export.manifest"); if (webBuildConfig.SingleFile && exportManifest.FileExists()) { inputs.Add(exportManifest.MakeAbsolute().ReadAllLines().Select(d => new NPath(d))); NPath assetRootDirectory = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier); commandLineArguments.AddRange(new List <string> { "--assetRootDirectory", assetRootDirectory.ToString(), "--assetManifest", exportManifest.ToString() }); } Backend.Current.AddAction( actionName: "Package Shell File", targetFiles: new NPath[] { tinyShellPath }, inputs: inputs.ToArray(), executableStringFor: TinyEmscripten.NodeExe.InQuotes(), commandLineArguments: commandLineArguments.Select(d => d.InQuotes()).ToArray() ); Backend.Current.AddDependency(deployedGame.Path, tinyShellPath); } else if (!templateFilePath.HasExtension("meta")) { var targetPath = deployPath.Combine(fileRelativePath); CopyTool.Instance().Setup(targetPath, templateFilePath); Backend.Current.AddDependency(deployedGame.Path, targetPath); } } } else { deployedGame = builtNativeProgram.DeployTo(deployPath); } entryPointExecutable = deployedGame.Path; if (config.EnableManagedDebugging && !(builtNativeProgram is IPackagedAppExtension)) { Backend.Current.AddDependency(deployedGame.Path, Il2Cpp.CopyIL2CPPMetadataFile(deployPath, setupGame)); } // make sure http-server gets fetched from stevedore. this should probably go elsewhere, but this is // a convenient quick hack place. if (config.PlatformBuildConfig is WebBuildConfig) { var httpserver = new StevedoreArtifact("http-server"); httpserver.GenerateUnusualPath(); var httpserverpath = httpserver.GetUnusualPath().Combine("bin", "http-server"); Backend.Current.AddDependency(deployedGame.Path, httpserverpath); } } else { deployedGame = setupGame.DeployTo(deployPath); var dotNetAssembly = (DotNetAssembly)deployedGame; //Usually a dotnet runtime game does not have a static void main(), and instead references another "entrypoint asmdef" that provides it. //This is convenient, but what makes it weird is that you have to start YourEntryPoint.exe instead of YourGame.exe. Until we have a better //solution for this, we're going to copy YourEntryPoint.exe to YourGame.exe, so that it's easier to find, and so that when it runs and you look //at the process name you understand what it is. if (deployedGame.Path.HasExtension("dll")) { var to = deployPath.Combine(deployedGame.Path.ChangeExtension("exe").FileName); // Do an explicit check for the entrypoint.exe as a program may refer to other exes as assembly references var from = dotNetAssembly.RecursiveRuntimeDependenciesIncludingSelf.SingleOrDefault(a => a.Path.FileName == "Unity.Runtime.EntryPoint.exe")?.Path; if (from == null) { throw new InvalidProgramException($"Program {dotNetAssembly.Path} is an executable-like thing, but doesn't reference anything with Main"); } Backend.Current.AddDependency(deployedGame.Path, CopyTool.Instance().Setup(to, from)); entryPointExecutable = to; } else { entryPointExecutable = deployedGame.Path; } } //Because we use multidag, and try to not run all the setupcode when we just want to create projectfiles, we have a bit of a challenge. //Projectfiles require exact start and build commands. So we need to have a cheap way to calculate those. However, it's important that they //exactly match the actual place where the buildprogram is going to place our files. If these don't match things break down. The checks //in this block, they compare the "quick way to determine where the binary will be placed, and what the start executable is", with the //actual return values returned from .DeployTo(), when we do run the actual buildcode. NPath deployedGamePath = GameDeployBinaryFor(gameProgram, config); //Identifier with slash means that this is complementary target and we should skip steps which are main target specific. //See comment in DotsConfigs.cs DotsConfigs.MakeConfigs() method for details. if (config.Identifier.IndexOf('/') != -1) { continue; } if (deployedGame.Path != deployedGamePath) { throw new InvalidProgramException($"We expected deployPath to be {deployedGamePath}, but in reality it was {deployedGame.Path}"); } var expectedEntryPointExecutable = EntryPointExecutableFor(gameProgram, config); if (entryPointExecutable != expectedEntryPointExecutable) { throw new InvalidProgramException($"We expected entryPointExecutable to be {expectedEntryPointExecutable}, but in reality it was {entryPointExecutable}"); } Backend.Current.AddAliasDependency(config.Identifier, deployedGamePath); } return(gameProgram); }
public override void CustomizeSelf(AsmDefCSharpProgram program) { program.NativeProgram.Libraries.Add(c => c.Platform is WebGLPlatform && !Il2Cpp.ManagedDebuggingIsEnabled(c), Il2Cpp.LibIL2Cpp); program.NativeProgram.Libraries.Add(c => c.Platform is WebGLPlatform && Il2Cpp.ManagedDebuggingIsEnabled(c), Il2Cpp.BigLibIL2Cpp); }
public static DumpType[] Dump(string metadataPath, string il2cppPath, string stringVersion) { // From Program.cs config = new JavaScriptSerializer().Deserialize <Config>(CONFIG_JSON); var metadataBytes = File.ReadAllBytes(metadataPath); var il2cppBytes = File.ReadAllBytes(il2cppPath); var sanity = BitConverter.ToUInt32(metadataBytes, 0); if (sanity != 0xFAB11BAF) { throw new Exception("ERROR: Metadata file supplied is not valid metadata file."); } float fixedMetadataVersion; var metadataVersion = BitConverter.ToInt32(metadataBytes, 4); if (metadataVersion == 24) { var versionSplit = Array.ConvertAll(Regex.Replace(stringVersion, @"\D", ".").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries), int.Parse); var unityVersion = new Version(versionSplit[0], versionSplit[1]); if (unityVersion >= Unity20191) { fixedMetadataVersion = 24.2f; } else if (unityVersion >= Unity20183) { fixedMetadataVersion = 24.1f; } else { fixedMetadataVersion = metadataVersion; } } else { fixedMetadataVersion = metadataVersion; } Console.WriteLine("Initializing metadata..."); metadata = new Metadata(new MemoryStream(metadataBytes), fixedMetadataVersion); //判断il2cpp的magic var il2cppMagic = BitConverter.ToUInt32(il2cppBytes, 0); var isElf = false; var isPE = false; var is64bit = false; var isNSO = false; switch (il2cppMagic) { default: throw new Exception("ERROR: il2cpp file not supported."); case 0x304F534E: isNSO = true; is64bit = true; break; case 0x905A4D: //PE isPE = true; break; case 0x464c457f: //ELF isElf = true; if (il2cppBytes[4] == 2) //ELF64 { is64bit = true; } break; case 0xCAFEBABE: //FAT Mach-O case 0xBEBAFECA: // To 64bit case 0xFEEDFACF: // 64bit Mach-O is64bit = true; break; case 0xFEEDFACE: // 32bit Mach-O break; } var version = config.ForceIl2CppVersion ? config.ForceVersion : metadata.version; if (isNSO) { var nso = new NSO(new MemoryStream(il2cppBytes), version, metadata.maxMetadataUsages); il2cpp = nso.UnCompress(); } else if (isPE) { il2cpp = new PE(new MemoryStream(il2cppBytes), version, metadata.maxMetadataUsages); } else if (isElf) { if (is64bit) { il2cpp = new Elf64(new MemoryStream(il2cppBytes), version, metadata.maxMetadataUsages); } else { il2cpp = new Elf(new MemoryStream(il2cppBytes), version, metadata.maxMetadataUsages); } } else if (is64bit) { il2cpp = new Macho64(new MemoryStream(il2cppBytes), version, metadata.maxMetadataUsages); } else { il2cpp = new Macho(new MemoryStream(il2cppBytes), version, metadata.maxMetadataUsages); } Console.WriteLine("Searching..."); try { // Select Auto(Plus) bool flag = il2cpp.PlusSearch(metadata.methodDefs.Count(x => x.methodIndex >= 0), metadata.typeDefs.Length); if (!flag) { throw new Exception(); } } catch { throw new Exception("ERROR: Can't use this mode to process file, try another mode."); } Console.WriteLine("Dumping..."); //dump type var dumpTypes = new List <DumpType>(); 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 dumpType = new DumpType(); var typeDef = metadata.typeDefs[idx]; typeDefImageIndices.Add(typeDef, imageIndex); 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); } } //implementedInterfaces 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)); } } dumpType.Namespace = metadata.GetStringFromIndex(typeDef.namespaceIndex); var dumpTypeAttributes = new List <DumpAttribute>(); var typeAttributes = GetCustomAttributes(imageDef, typeDef.customAttributeIndex, typeDef.token); if (typeAttributes != null) { dumpTypeAttributes.AddRange(typeAttributes); } if (config.DumpAttribute && (typeDef.flags & TYPE_ATTRIBUTE_SERIALIZABLE) != 0) { dumpTypeAttributes.Add(new DumpAttribute { Name = "[Serializable]" }); } dumpType.Attributes = dumpTypeAttributes.ToArray(); var visibility = typeDef.flags & TYPE_ATTRIBUTE_VISIBILITY_MASK; switch (visibility) { case TYPE_ATTRIBUTE_PUBLIC: case TYPE_ATTRIBUTE_NESTED_PUBLIC: dumpType.Modifier += "public "; break; case TYPE_ATTRIBUTE_NOT_PUBLIC: case TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM: case TYPE_ATTRIBUTE_NESTED_ASSEMBLY: dumpType.Modifier += "internal "; break; case TYPE_ATTRIBUTE_NESTED_PRIVATE: dumpType.Modifier += "private "; break; case TYPE_ATTRIBUTE_NESTED_FAMILY: dumpType.Modifier += "protected "; break; case TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM: dumpType.Modifier += "protected internal "; break; } if ((typeDef.flags & TYPE_ATTRIBUTE_ABSTRACT) != 0 && (typeDef.flags & TYPE_ATTRIBUTE_SEALED) != 0) { dumpType.Modifier += "static "; } else if ((typeDef.flags & TYPE_ATTRIBUTE_INTERFACE) == 0 && (typeDef.flags & TYPE_ATTRIBUTE_ABSTRACT) != 0) { dumpType.Modifier += "abstract "; } else if (!isStruct && !isEnum && (typeDef.flags & TYPE_ATTRIBUTE_SEALED) != 0) { dumpType.Modifier += "sealed "; } dumpType.Modifier.TrimEnd(); if ((typeDef.flags & TYPE_ATTRIBUTE_INTERFACE) != 0) { dumpType.TypeStr = "interface"; } else if (isStruct) { dumpType.TypeStr = "struct"; } else if (isEnum) { dumpType.TypeStr = "enum"; } else { dumpType.TypeStr = "class"; } var typeName = GetTypeName(typeDef); dumpType.Name = $"{typeName}"; if (extends.Count > 0) { dumpType.Extends = extends.ToArray(); } //dump field var dumpFields = new List <DumpField>(); if (config.DumpField && typeDef.field_count > 0) { var fieldEnd = typeDef.fieldStart + typeDef.field_count; for (var i = typeDef.fieldStart; i < fieldEnd; ++i) { //dump_field(i, idx, i - typeDef.fieldStart); var dumpField = new DumpField(); var fieldDef = metadata.fieldDefs[i]; var fieldType = il2cpp.types[fieldDef.typeIndex]; var fieldDefaultValue = metadata.GetFieldDefaultValueFromIndex(i); var fieldAttributes = GetCustomAttributes(imageDef, fieldDef.customAttributeIndex, fieldDef.token); if (fieldAttributes != null) { dumpField.Attributes = fieldAttributes; } var access = fieldType.attrs & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK; switch (access) { case FIELD_ATTRIBUTE_PRIVATE: dumpField.Modifier += "private "; break; case FIELD_ATTRIBUTE_PUBLIC: dumpField.Modifier += "public "; break; case FIELD_ATTRIBUTE_FAMILY: dumpField.Modifier += "protected "; break; case FIELD_ATTRIBUTE_ASSEMBLY: case FIELD_ATTRIBUTE_FAM_AND_ASSEM: dumpField.Modifier += "internal "; break; case FIELD_ATTRIBUTE_FAM_OR_ASSEM: dumpField.Modifier += "protected internal "; break; } if ((fieldType.attrs & FIELD_ATTRIBUTE_LITERAL) != 0) { dumpField.Modifier += "const "; } else { if ((fieldType.attrs & FIELD_ATTRIBUTE_STATIC) != 0) { dumpField.Modifier += "static "; } if ((fieldType.attrs & FIELD_ATTRIBUTE_INIT_ONLY) != 0) { dumpField.Modifier += "readonly "; } } dumpField.TypeStr = GetTypeName(fieldType); dumpField.Name = metadata.GetStringFromIndex(fieldDef.nameIndex); if (fieldDefaultValue != null && fieldDefaultValue.dataIndex != -1) { var pointer = metadata.GetDefaultValueFromIndex(fieldDefaultValue.dataIndex); if (pointer > 0) { var fieldDefaultValueType = il2cpp.types[fieldDefaultValue.typeIndex]; metadata.Position = pointer; object val = null; switch (fieldDefaultValueType.type) { case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN: val = metadata.ReadBoolean(); break; case Il2CppTypeEnum.IL2CPP_TYPE_U1: val = metadata.ReadByte(); break; case Il2CppTypeEnum.IL2CPP_TYPE_I1: val = metadata.ReadSByte(); break; case Il2CppTypeEnum.IL2CPP_TYPE_CHAR: val = BitConverter.ToChar(metadata.ReadBytes(2), 0); break; case Il2CppTypeEnum.IL2CPP_TYPE_U2: val = metadata.ReadUInt16(); break; case Il2CppTypeEnum.IL2CPP_TYPE_I2: val = metadata.ReadInt16(); break; case Il2CppTypeEnum.IL2CPP_TYPE_U4: val = metadata.ReadUInt32(); break; case Il2CppTypeEnum.IL2CPP_TYPE_I4: val = metadata.ReadInt32(); break; case Il2CppTypeEnum.IL2CPP_TYPE_U8: val = metadata.ReadUInt64(); break; case Il2CppTypeEnum.IL2CPP_TYPE_I8: val = metadata.ReadInt64(); break; case Il2CppTypeEnum.IL2CPP_TYPE_R4: val = metadata.ReadSingle(); break; case Il2CppTypeEnum.IL2CPP_TYPE_R8: val = metadata.ReadDouble(); break; case Il2CppTypeEnum.IL2CPP_TYPE_STRING: var len = metadata.ReadInt32(); val = Encoding.UTF8.GetString(metadata.ReadBytes(len)); break; } if (val is string str) { dumpField.ValueStr = $"\"{ToEscapedString(str)}\""; } else if (val is char c) { var v = (int)c; dumpField.ValueStr = $"'\\x{v:x}'"; } else if (val != null) { dumpField.ValueStr = $"{val}"; } } } dumpFields.Add(dumpField); } } dumpType.Fields = dumpFields.ToArray(); //dump property var dumpProperties = new List <DumpProperty>(); if (config.DumpProperty && typeDef.property_count > 0) { var propertyEnd = typeDef.propertyStart + typeDef.property_count; for (var i = typeDef.propertyStart; i < propertyEnd; ++i) { var dumpProperty = new DumpProperty(); var propertyDef = metadata.propertyDefs[i]; var propertyAttributes = GetCustomAttributes(imageDef, propertyDef.customAttributeIndex, propertyDef.token); if (propertyAttributes != null) { dumpProperty.Attributes = propertyAttributes; } if (propertyDef.get >= 0) { var methodDef = metadata.methodDefs[typeDef.methodStart + propertyDef.get]; dumpProperty.Modifier = GetModifiers(methodDef); var propertyType = il2cpp.types[methodDef.returnType]; dumpProperty.TypeStr = GetTypeName(propertyType); dumpProperty.Name = metadata.GetStringFromIndex(propertyDef.nameIndex); } else if (propertyDef.set > 0) { var methodDef = metadata.methodDefs[typeDef.methodStart + propertyDef.set]; dumpProperty.Modifier = GetModifiers(methodDef); var parameterDef = metadata.parameterDefs[methodDef.parameterStart]; var propertyType = il2cpp.types[parameterDef.typeIndex]; dumpProperty.TypeStr = GetTypeName(propertyType); dumpProperty.Name = metadata.GetStringFromIndex(propertyDef.nameIndex); } dumpProperty.Access += "{ "; if (propertyDef.get >= 0) { dumpProperty.Access += "get; "; } if (propertyDef.set >= 0) { dumpProperty.Access += "set; "; } dumpProperty.Access += "}"; dumpProperties.Add(dumpProperty); } } dumpType.Properties = dumpProperties.ToArray(); //dump method var dumpMethods = new List <DumpMethod>(); if (config.DumpMethod && typeDef.method_count > 0) { var methodEnd = typeDef.methodStart + typeDef.method_count; for (var i = typeDef.methodStart; i < methodEnd; ++i) { var dumpMethod = new DumpMethod(); var methodDef = metadata.methodDefs[i]; var methodAttributes = GetCustomAttributes(imageDef, methodDef.customAttributeIndex, methodDef.token); if (methodAttributes != null) { dumpMethod.Attributes = methodAttributes; } dumpMethod.Modifier = GetModifiers(methodDef); var methodReturnType = il2cpp.types[methodDef.returnType]; var methodName = metadata.GetStringFromIndex(methodDef.nameIndex); dumpMethod.TypeStr = GetTypeName(methodReturnType); dumpMethod.Name = methodName; 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.attrs & PARAM_ATTRIBUTE_OPTIONAL) != 0) { parameterStr += "optional "; } if ((parameterType.attrs & PARAM_ATTRIBUTE_OUT) != 0) { parameterStr += "out "; } parameterStr += $"{parameterTypeName} {parameterName}"; dumpMethod.Parameters = new string[] { parameterStr }; } if (config.DumpMethodOffset) { var methodPointer = il2cpp.GetMethodPointer(methodDef.methodIndex, i, imageIndex, methodDef.token); if (methodPointer > 0) { dumpMethod.Address = il2cpp.MapVATR(methodPointer); } } dumpMethods.Add(dumpMethod); } } dumpType.Methods = dumpMethods.ToArray(); dumpTypes.Add(dumpType); } } catch { throw new Exception("ERROR: Some errors in dumping."); } } return(dumpTypes.ToArray()); }