CLI/Output: Add --separate-attributes option for assembly-level attributes
This commit is contained in:
@@ -10,6 +10,7 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Il2CppInspector.Reflection;
|
||||
using Assembly = Il2CppInspector.Reflection.Assembly;
|
||||
using CustomAttributeData = Il2CppInspector.Reflection.CustomAttributeData;
|
||||
using MethodInfo = Il2CppInspector.Reflection.MethodInfo;
|
||||
using TypeInfo = Il2CppInspector.Reflection.TypeInfo;
|
||||
@@ -55,11 +56,14 @@ namespace Il2CppInspector
|
||||
});
|
||||
}
|
||||
|
||||
public void WriteFilesByAssembly<TKey>(string outPath, Func<TypeInfo, TKey> orderBy) {
|
||||
public void WriteFilesByAssembly<TKey>(string outPath, Func<TypeInfo, TKey> orderBy, bool separateAttributes) {
|
||||
usedAssemblyAttributes.Clear();
|
||||
Parallel.ForEach(model.Assemblies, asm => {
|
||||
// Sort namespaces into alphabetical order, then sort types within the namespaces by the specified sort function
|
||||
writeFile($"{outPath}\\{asm.ShortName.Replace(".dll", "")}.cs", asm.DefinedTypes.OrderBy(t => t.Namespace).ThenBy(orderBy));
|
||||
if (writeFile($"{outPath}\\{asm.ShortName.Replace(".dll", "")}.cs", asm.DefinedTypes.OrderBy(t => t.Namespace).ThenBy(orderBy), outputAssemblyAttributes: !separateAttributes)
|
||||
&& separateAttributes) {
|
||||
File.WriteAllText($"{outPath}\\AssemblyInfo_{asm.ShortName.Replace(".dll", "")}.cs", generateAssemblyInfo(new [] {asm}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,15 +75,28 @@ namespace Il2CppInspector
|
||||
});
|
||||
}
|
||||
|
||||
public void WriteFilesByClassTree(string outPath) {
|
||||
public void WriteFilesByClassTree(string outPath, bool separateAttributes) {
|
||||
usedAssemblyAttributes.Clear();
|
||||
Parallel.ForEach(model.Assemblies.SelectMany(x => x.DefinedTypes), type => {
|
||||
writeFile($"{outPath}\\{type.Assembly.ShortName.Replace(".dll", "")}\\" + (type.Namespace + (type.Namespace.Length > 0 ? "." : "") + Regex.Replace(type.Name, "`[0-9]", ""))
|
||||
.Replace('.', '\\') + ".cs", new[] {type});
|
||||
});
|
||||
var usedAssemblies = new HashSet<Assembly>();
|
||||
|
||||
// Each thread tracks its own list of used assemblies and they are merged as each thread completes
|
||||
Parallel.ForEach(model.Assemblies.SelectMany(x => x.DefinedTypes),
|
||||
() => new HashSet<Assembly>(),
|
||||
(type, _, used) => {
|
||||
if (writeFile($"{outPath}\\{type.Assembly.ShortName.Replace(".dll", "")}\\" + (type.Namespace + (type.Namespace.Length > 0 ? "." : "") + Regex.Replace(type.Name, "`[0-9]", ""))
|
||||
.Replace('.', '\\') + ".cs", new[] {type}, outputAssemblyAttributes: !separateAttributes))
|
||||
used.Add(type.Assembly);
|
||||
return used;
|
||||
},
|
||||
usedPartition => usedAssemblies.UnionWith(usedPartition)
|
||||
);
|
||||
|
||||
if (separateAttributes && usedAssemblies.Any())
|
||||
foreach (var asm in usedAssemblies)
|
||||
File.WriteAllText($"{outPath}\\{asm.ShortName.Replace(".dll", "")}\\AssemblyInfo.cs", generateAssemblyInfo(new [] {asm}));
|
||||
}
|
||||
|
||||
private void writeFile(string outFile, IEnumerable<TypeInfo> types, bool useNamespaceSyntax = true) {
|
||||
private bool writeFile(string outFile, IEnumerable<TypeInfo> types, bool useNamespaceSyntax = true, bool outputAssemblyAttributes = true) {
|
||||
|
||||
var nsRefs = new HashSet<string>();
|
||||
var code = new StringBuilder();
|
||||
@@ -98,7 +115,8 @@ namespace Il2CppInspector
|
||||
var assemblies = types.Select(t => t.Assembly).Distinct();
|
||||
|
||||
// Add assembly attribute namespaces to reference list
|
||||
nsRefs.UnionWith(assemblies.SelectMany(a => a.CustomAttributes).Select(a => a.AttributeType.Namespace));
|
||||
if (outputAssemblyAttributes)
|
||||
nsRefs.UnionWith(assemblies.SelectMany(a => a.CustomAttributes).Select(a => a.AttributeType.Namespace));
|
||||
|
||||
// Generate each type
|
||||
foreach (var type in types) {
|
||||
@@ -147,7 +165,7 @@ namespace Il2CppInspector
|
||||
|
||||
// Stop if nothing to output
|
||||
if (!usedTypes.Any())
|
||||
return;
|
||||
return false;
|
||||
|
||||
// Close namespace
|
||||
if (useNamespaceSyntax && !string.IsNullOrEmpty(nsContext))
|
||||
@@ -187,7 +205,7 @@ namespace Il2CppInspector
|
||||
writer.Write("\n");
|
||||
|
||||
// Output assembly information and attributes
|
||||
writer.Write(generateAssemblyInfo(assemblies, nsRefs) + "\n\n");
|
||||
writer.Write(generateAssemblyInfo(assemblies, nsRefs, outputAssemblyAttributes) + "\n\n");
|
||||
|
||||
// Output type definitions
|
||||
writer.Write(code);
|
||||
@@ -203,25 +221,28 @@ namespace Il2CppInspector
|
||||
System.Threading.Thread.Sleep(100);
|
||||
}
|
||||
} while (!fileWritten);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string generateAssemblyInfo(IEnumerable<Reflection.Assembly> assemblies, IEnumerable<string> namespaces) {
|
||||
private string generateAssemblyInfo(IEnumerable<Reflection.Assembly> assemblies, IEnumerable<string> namespaces = null, bool outputAssemblyAttributes = true) {
|
||||
var text = new StringBuilder();
|
||||
|
||||
foreach (var asm in assemblies) {
|
||||
text.Append($"// Image {asm.Index}: {asm.ShortName} - Assembly: {asm.FullName} - Types {asm.ImageDefinition.typeStart}-{asm.ImageDefinition.typeStart + asm.ImageDefinition.typeCount - 1}\n");
|
||||
|
||||
// Assembly-level attributes
|
||||
lock (usedAssemblyAttributesLock) {
|
||||
text.Append(asm.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute)
|
||||
.Except(usedAssemblyAttributes ?? new HashSet<CustomAttributeData>())
|
||||
.OrderBy(a => a.AttributeType.Name)
|
||||
.ToString(new Scope { Current = null, Namespaces = namespaces }, attributePrefix: "assembly: ", emitPointer: !SuppressMetadata, mustCompile: MustCompile));
|
||||
if (asm.CustomAttributes.Any())
|
||||
text.Append("\n");
|
||||
if (outputAssemblyAttributes)
|
||||
lock (usedAssemblyAttributesLock) {
|
||||
text.Append(asm.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute)
|
||||
.Except(usedAssemblyAttributes ?? new HashSet<CustomAttributeData>())
|
||||
.OrderBy(a => a.AttributeType.Name)
|
||||
.ToString(new Scope { Current = null, Namespaces = namespaces ?? new List<string>() }, attributePrefix: "assembly: ", emitPointer: !SuppressMetadata, mustCompile: MustCompile));
|
||||
if (asm.CustomAttributes.Any())
|
||||
text.Append("\n");
|
||||
|
||||
usedAssemblyAttributes.UnionWith(asm.CustomAttributes);
|
||||
}
|
||||
usedAssemblyAttributes.UnionWith(asm.CustomAttributes);
|
||||
}
|
||||
}
|
||||
return text.ToString().TrimEnd();
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ namespace Il2CppInspector
|
||||
|
||||
[Option('k', "must-compile", Required = false, HelpText = "Compilation tidying: try really hard to make code that compiles. Suppress generation of code for items with CompilerGenerated attribute. Comment out attributes without parameterless constructors or all-optional constructor arguments. Don't emit add/remove/raise on events. Specify AttributeTargets.All on classes with AttributeUsage attribute. Force auto-properties to have get accessors. Force regular properties to have bodies.")]
|
||||
public bool MustCompile { get; set; }
|
||||
|
||||
[Option("separate-attributes", Required = false, HelpText = "Place assembly-level attributes in their own AssemblyInfo.cs files. Only used when layout is per-assembly or tree")]
|
||||
public bool SeparateAssemblyAttributesFiles { get; set; }
|
||||
}
|
||||
|
||||
// Adapted from: https://stackoverflow.com/questions/16376191/measuring-code-execution-time
|
||||
@@ -143,10 +146,10 @@ namespace Il2CppInspector
|
||||
break;
|
||||
|
||||
case ("assembly", "index"):
|
||||
writer.WriteFilesByAssembly(csOut, t => t.Index);
|
||||
writer.WriteFilesByAssembly(csOut, t => t.Index, options.SeparateAssemblyAttributesFiles);
|
||||
break;
|
||||
case ("assembly", "name"):
|
||||
writer.WriteFilesByAssembly(csOut, t => t.Name);
|
||||
writer.WriteFilesByAssembly(csOut, t => t.Name, options.SeparateAssemblyAttributesFiles);
|
||||
break;
|
||||
|
||||
case ("class", _):
|
||||
@@ -154,7 +157,7 @@ namespace Il2CppInspector
|
||||
break;
|
||||
|
||||
case ("tree", _):
|
||||
writer.WriteFilesByClassTree(csOut);
|
||||
writer.WriteFilesByClassTree(csOut, options.SeparateAssemblyAttributesFiles);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ File format and architecture are automatically detected.
|
||||
-f, --flatten Flatten the namespace hierarchy into a single folder rather than using per-namespace subfolders. Only used when layout is per-namespace or per-class. Ignored for tree layout
|
||||
-n, --suppress-metadata Diff tidying: suppress method pointers, field offsets and type indices from C# output. Useful for comparing two versions of a binary for changes with a diff tool
|
||||
-k, --must-compile Compilation tidying: try really hard to make code that compiles. Suppress generation of code for items with CompilerGenerated attribute. Comment out attributes without parameterless constructors or all-optional constructor arguments. Don't emit add/remove/raise on events. Specify AttributeTargets.All on classes with AttributeUsage attribute. Force auto-properties to have get accessors. Force regular properties to have bodies.
|
||||
--separate-attributes Place assembly-level attributes in their own AssemblyInfo.cs files. Only used when layout is per-assembly or tree
|
||||
```
|
||||
|
||||
Defaults if not specified:
|
||||
|
||||
Reference in New Issue
Block a user