• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SamboyCoding / Cpp2IL / 17216182984

25 Aug 2025 05:35PM UTC coverage: 30.38% (-4.0%) from 34.352%
17216182984

Pull #481

github

web-flow
Merge b82763a24 into d5260685f
Pull Request #481: Decompiler

1804 of 7561 branches covered (23.86%)

Branch coverage included in aggregate %.

100 of 1839 new or added lines in 29 files covered. (5.44%)

41 existing lines in 6 files now uncovered.

4093 of 11850 relevant lines covered (34.54%)

165575.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.13
/Cpp2IL.Core/OutputFormats/AsmResolverDllOutputFormat.cs
1
// #define VERBOSE_LOGGING
2

3
using System;
4
using System.Collections.Generic;
5
using System.IO;
6
using System.Linq;
7
using System.Threading.Tasks;
8
using AsmResolver.DotNet;
9
using AsmResolver.DotNet.Builder;
10
using AsmResolver.PE.Builder;
11
using AsmResolver.PE.DotNet.Metadata.Tables;
12
using Cpp2IL.Core.Api;
13
using Cpp2IL.Core.Logging;
14
using Cpp2IL.Core.Model.Contexts;
15
using Cpp2IL.Core.Utils;
16
using Cpp2IL.Core.Utils.AsmResolver;
17
using LibCpp2IL.Metadata;
18

19
namespace Cpp2IL.Core.OutputFormats;
20

21
public abstract class AsmResolverDllOutputFormat : Cpp2IlOutputFormat
22
{
23
    private AssemblyDefinition? MostRecentCorLib { get; set; }
225✔
24
    protected int TotalMethodCount;
25
    protected int SuccessfulMethodCount;
26

27
    public sealed override void DoOutput(ApplicationAnalysisContext context, string outputRoot)
28
    {
29
        var ret = BuildAssemblies(context);
×
30

31
        var start = DateTime.Now;
×
32
        Logger.Verbose("Generating PE images...", "DllOutput");
×
33

34
        if (!Directory.Exists(outputRoot))
×
35
            Directory.CreateDirectory(outputRoot);
×
36

37
        //Convert assembly definitions to PE files
38
        var peImagesToWrite = ret
×
39
            .AsParallel()
×
40
            .Select(a => (image: a.ManifestModule!.ToPEImage(new ManagedPEImageBuilder()), name: a.ManifestModule.Name!))
×
41
            .ToList();
×
42

43
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
×
44

45
        start = DateTime.Now;
×
46
        Logger.Verbose("Building and writing managed PE files to disk...", "DllOutput");
×
47

48
        //Save them
49
        var fileBuilder = new ManagedPEFileBuilder();
×
50
        foreach (var (image, name) in peImagesToWrite)
×
51
        {
52
            var dllPath = Path.Combine(outputRoot, name);
×
53
            fileBuilder.CreateFile(image).Write(dllPath);
×
54
        }
55

56
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
×
57

NEW
58
        if (TotalMethodCount != 0)
×
59
        {
NEW
60
            var percent = Math.Round((SuccessfulMethodCount / (float)TotalMethodCount) * 100);
×
NEW
61
            Logger.InfoNewline($"{percent}% of methods successfully decompiled ({SuccessfulMethodCount} / {TotalMethodCount})", "DllOutput");
×
62
        }
63
    }
×
64

65
    public virtual List<AssemblyDefinition> BuildAssemblies(ApplicationAnalysisContext context)
66
    {
67
#if VERBOSE_LOGGING
68
        var asmCount = context.Assemblies.Count;
69
        var typeCount = context.AllTypes.Count();
70
        var methodCount = context.AllTypes.SelectMany(t => t.Methods).Count();
71
        var fieldCount = context.AllTypes.SelectMany(t => t.Fields).Count();
72
        var propertyCount = context.AllTypes.SelectMany(t => t.Properties).Count();
73
        var eventCount = context.AllTypes.SelectMany(t => t.Events).Count();
74
#endif
75

76
        //Build the stub assemblies
77
        var start = DateTime.Now;
5✔
78
#if VERBOSE_LOGGING
79
        Logger.Verbose($"Building stub assemblies ({asmCount} assemblies, {typeCount} types)...", "DllOutput");
80
#else
81
        Logger.Verbose($"Building stub assemblies...", "DllOutput");
5✔
82
#endif
83
        List<AssemblyDefinition> ret = BuildStubAssemblies(context);
5✔
84
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
5✔
85

86
        start = DateTime.Now;
5✔
87
        Logger.Verbose("Configuring inheritance and generics...", "DllOutput");
5✔
88

89
        Parallel.ForEach(context.Assemblies, AsmResolverAssemblyPopulator.ConfigureHierarchy);
5✔
90

91
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
5✔
92

93
        //Populate them
94
        start = DateTime.Now;
5✔
95

96
#if VERBOSE_LOGGING
97
        Logger.Verbose($"Adding {fieldCount} fields, {methodCount} methods, {propertyCount} properties, and {eventCount} events (in parallel)...", "DllOutput");
98
#else
99
        Logger.Verbose($"Adding fields, methods, properties, and events (in parallel)...", "DllOutput");
5✔
100
#endif
101

102
        MiscUtils.ExecuteParallel(context.Assemblies, AsmResolverAssemblyPopulator.CopyDataFromIl2CppToManaged);
5✔
103
        MiscUtils.ExecuteParallel(context.Assemblies, AsmResolverAssemblyPopulator.AddExplicitInterfaceImplementations);
5✔
104

105
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
5✔
106

107
        //Populate custom attributes
108
        start = DateTime.Now;
5✔
109
        Logger.Verbose("Adding custom attributes to all of the above...", "DllOutput");
5✔
110
        MiscUtils.ExecuteParallel(context.Assemblies, AsmResolverAssemblyPopulator.PopulateCustomAttributes);
5✔
111

112
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
5✔
113

114
        //Fill method bodies - this should always be done last
115
        start = DateTime.Now;
5✔
116
        Logger.Verbose($"Filling method bodies (in parallel)...", "DllOutput");
5✔
117
        MiscUtils.ExecuteParallel(context.Assemblies, FillMethodBodies);
5✔
118

119
        Logger.VerboseNewline($"{(DateTime.Now - start).TotalMilliseconds:F1}ms", "DllOutput");
5✔
120

121
        TypeDefinitionsAsmResolver.Reset();
5✔
122

123
        return ret;
5✔
124
    }
125

126
    protected abstract void FillMethodBody(MethodDefinition methodDefinition, MethodAnalysisContext methodContext);
127

128
    protected virtual void FillMethodBodies(AssemblyAnalysisContext context)
129
    {
130
        foreach (var typeContext in context.Types)
29,680✔
131
        {
132
            if (AsmResolverAssemblyPopulator.IsTypeContextModule(typeContext))
14,630✔
133
                continue;
134

135
#if !DEBUG
136
            try
137
#endif
138
            {
139
                foreach (var methodCtx in typeContext.Methods)
202,900✔
140
                {
141
                    var managedMethod = methodCtx.GetExtraData<MethodDefinition>("AsmResolverMethod") ?? throw new($"AsmResolver method not found in method analysis context for {typeContext.FullName}.{methodCtx.Name}");
87,030!
142

143
                    FillMethodBody(managedMethod, methodCtx);
87,030✔
144
                }
145
            }
14,420✔
146
#if !DEBUG
147
            catch (System.Exception e)
×
148
            {
149
                var managedType = typeContext.GetExtraData<TypeDefinition>("AsmResolverType") ?? throw new($"AsmResolver type not found in type analysis context for {typeContext.FullName}");
×
150
                throw new($"Failed to process type {managedType.FullName} (module {managedType.Module?.Name}, declaring type {managedType.DeclaringType?.FullName}) in {context.Name}", e);
×
151
            }
152
#endif
153
        }
154
    }
210✔
155

156
    private List<AssemblyDefinition> BuildStubAssemblies(ApplicationAnalysisContext context)
157
    {
158
        var assemblyResolver = new Il2CppAssemblyResolver();
5✔
159
        var metadataResolver = new DefaultMetadataResolver(assemblyResolver);
5✔
160

161
        var corlib = context.Assemblies.First(a => a.Name == "mscorlib");
10✔
162
        MostRecentCorLib = BuildStubAssembly(corlib, null, metadataResolver);
5✔
163
        assemblyResolver.DummyAssemblies.Add(MostRecentCorLib.Name!, MostRecentCorLib);
5✔
164

165
        var ret = context.Assemblies
5✔
166
            // .AsParallel()
5✔
167
            .Where(a => a.Name != "mscorlib")
210✔
168
            .Select(a => BuildStubAssembly(a, MostRecentCorLib, metadataResolver))
205✔
169
            .ToList();
5✔
170

171
        ret.ForEach(a => assemblyResolver.DummyAssemblies.Add(a.Name!, a));
210✔
172

173
        ret.Add(MostRecentCorLib);
5✔
174
        return ret;
5✔
175
    }
176

177
    private sealed class CorLibModuleDefinition : ModuleDefinition
178
    {
179
        public CorLibModuleDefinition(string? name, AssemblyDefinition assembly) : base(name, new(assembly))
5✔
180
        {
181
            // https://github.com/Washi1337/AsmResolver/issues/620
182
            CorLibTypeFactory = new(this);
5✔
183
            AssemblyReferences.Clear();
5✔
184
        }
5✔
185
    }
186

187
    private static AssemblyDefinition BuildStubAssembly(AssemblyAnalysisContext assemblyContext, AssemblyDefinition? corLib, IMetadataResolver metadataResolver)
188
    {
189
        //Get the name of the assembly (= the name of the DLL without the file extension)
190
        //Build an AsmResolver assembly from this definition
191
        var ourAssembly = new AssemblyDefinition(assemblyContext.Name, assemblyContext.Version)
210✔
192
        {
210✔
193
            HashAlgorithm = (AssemblyHashAlgorithm)assemblyContext.HashAlgorithm,
210✔
194
            Attributes = (AssemblyAttributes)assemblyContext.Flags,
210✔
195
            Culture = assemblyContext.Culture,
210✔
196
            PublicKey = assemblyContext.PublicKey,
210✔
197
        };
210✔
198

199
        //Setting the corlib module allows element types in references to that assembly to be set correctly without us having to manually set them.
200
        var moduleName = assemblyContext.CleanAssemblyName + ".dll";
210✔
201

202
        var managedModule = corLib is not null //Use either ourself as corlib, if we are corlib, otherwise the provided one
210✔
203
            ? new ModuleDefinition(moduleName, new(corLib))
210✔
204
            {
210✔
205
                MetadataResolver = metadataResolver
210✔
206
            }
210✔
207
            : new CorLibModuleDefinition(moduleName, ourAssembly)
210✔
208
            {
210✔
209
                MetadataResolver = metadataResolver
210✔
210
            };
210✔
211
        ourAssembly.Modules.Add(managedModule);
210✔
212

213
        foreach (var il2CppTypeDefinition in assemblyContext.TopLevelTypes)
23,340✔
214
        {
215
            if (il2CppTypeDefinition.Name != "<Module>")
11,460✔
216
                //We skip module because I've never come across an il2cpp assembly with any top-level functions, and it's simpler to skip it as AsmResolver adds one by default.
217
                managedModule.TopLevelTypes.Add(BuildStubType(il2CppTypeDefinition));
11,250✔
218
        }
219

220
        if (corLib == null)
210✔
221
        {
222
            //We *are* the corlib, so cache defs now
223
            TypeDefinitionsAsmResolver.CacheNeededTypeDefinitions();
5✔
224
        }
225

226
        //We can get issues with consumers of the API if the base type is not set correctly for value types or enums, so we set it here (as early as possible) if we can
227
        foreach (var assemblyContextType in assemblyContext.Types)
29,680✔
228
        {
229
            if (assemblyContextType.Definition is not { } def || assemblyContextType.GetExtraData<TypeDefinition>("AsmResolverType") is not { } asmResolverType)
14,630✔
230
                continue;
231

232
            if (def.IsValueType)
14,420✔
233
                asmResolverType.BaseType = managedModule.DefaultImporter.ImportType(TypeDefinitionsAsmResolver.ValueType);
4,475✔
234
            else if (def.IsEnumType)
9,945!
235
                asmResolverType.BaseType = managedModule.DefaultImporter.ImportType(TypeDefinitionsAsmResolver.Enum);
×
236
        }
237

238
        //Store the managed assembly in the context so we can use it later.
239
        assemblyContext.PutExtraData("AsmResolverAssembly", ourAssembly);
210✔
240

241
        return ourAssembly;
210✔
242
    }
243

244
    private static TypeDefinition BuildStubType(TypeAnalysisContext typeContext)
245
    {
246
        var typeDef = typeContext.Definition;
14,420✔
247

248
        //Initialize an empty type definition
249
        var ret = new TypeDefinition(typeContext.Namespace, typeContext.Name, (TypeAttributes)typeContext.Attributes);
14,420✔
250

251
        //Set up its layout
252
        if (typeDef != null)
14,420✔
253
            ConfigureTypeSize(typeDef, ret);
14,420✔
254

255
        //Create nested types
256
        foreach (var cppNestedType in typeContext.NestedTypes)
35,180✔
257
            ret.NestedTypes.Add(BuildStubType(cppNestedType));
3,170✔
258

259
        //Associate this asm resolve td with the type context
260
        typeContext.PutExtraData("AsmResolverType", ret);
14,420✔
261

262
        //Add to the lookup-by-id table used by the resolver
263
        if (typeDef != null)
14,420✔
264
            AsmResolverUtils.TypeDefsByIndex[typeDef.TypeIndex] = ret;
14,420✔
265

266
        return ret;
14,420✔
267
    }
268

269
    private static void ConfigureTypeSize(Il2CppTypeDefinition il2CppDefinition, TypeDefinition asmResolverDefinition)
270
    {
271
        if (!il2CppDefinition.IsValueType || il2CppDefinition.IsEnumType)
14,420✔
272
            return; // Only structs can have their layout changed
11,865✔
273

274
        ushort packingSize = 0;
2,555✔
275
        var classSize = 0U;
2,555✔
276
        if (!il2CppDefinition.PackingSizeIsDefault)
2,555✔
277
            packingSize = (ushort)il2CppDefinition.PackingSize;
915✔
278

279
        if (!il2CppDefinition.ClassSizeIsDefault)
2,555✔
280
        {
281
            if (il2CppDefinition.Size > 1 << 30)
915!
282
                throw new Exception($"Got invalid size for type {il2CppDefinition}: {il2CppDefinition.RawSizes}");
×
283

284
            if (il2CppDefinition.Size != -1)
915!
285
                classSize = (uint)il2CppDefinition.Size;
915✔
286
            else
287
                classSize = 0; //Not sure what this value actually implies but it seems to work
×
288
        }
289

290
        if (packingSize != 0 || classSize != 0)
2,555✔
291
            asmResolverDefinition.ClassLayout = new(packingSize, classSize);
915✔
292
    }
2,555✔
293
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc