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.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Il2CppInspector.Reflection;
|
using Il2CppInspector.Reflection;
|
||||||
|
using Assembly = Il2CppInspector.Reflection.Assembly;
|
||||||
using CustomAttributeData = Il2CppInspector.Reflection.CustomAttributeData;
|
using CustomAttributeData = Il2CppInspector.Reflection.CustomAttributeData;
|
||||||
using MethodInfo = Il2CppInspector.Reflection.MethodInfo;
|
using MethodInfo = Il2CppInspector.Reflection.MethodInfo;
|
||||||
using TypeInfo = Il2CppInspector.Reflection.TypeInfo;
|
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();
|
usedAssemblyAttributes.Clear();
|
||||||
Parallel.ForEach(model.Assemblies, asm => {
|
Parallel.ForEach(model.Assemblies, asm => {
|
||||||
// Sort namespaces into alphabetical order, then sort types within the namespaces by the specified sort function
|
// 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();
|
usedAssemblyAttributes.Clear();
|
||||||
Parallel.ForEach(model.Assemblies.SelectMany(x => x.DefinedTypes), type => {
|
var usedAssemblies = new HashSet<Assembly>();
|
||||||
writeFile($"{outPath}\\{type.Assembly.ShortName.Replace(".dll", "")}\\" + (type.Namespace + (type.Namespace.Length > 0 ? "." : "") + Regex.Replace(type.Name, "`[0-9]", ""))
|
|
||||||
.Replace('.', '\\') + ".cs", new[] {type});
|
// 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 nsRefs = new HashSet<string>();
|
||||||
var code = new StringBuilder();
|
var code = new StringBuilder();
|
||||||
@@ -98,6 +115,7 @@ namespace Il2CppInspector
|
|||||||
var assemblies = types.Select(t => t.Assembly).Distinct();
|
var assemblies = types.Select(t => t.Assembly).Distinct();
|
||||||
|
|
||||||
// Add assembly attribute namespaces to reference list
|
// Add assembly attribute namespaces to reference list
|
||||||
|
if (outputAssemblyAttributes)
|
||||||
nsRefs.UnionWith(assemblies.SelectMany(a => a.CustomAttributes).Select(a => a.AttributeType.Namespace));
|
nsRefs.UnionWith(assemblies.SelectMany(a => a.CustomAttributes).Select(a => a.AttributeType.Namespace));
|
||||||
|
|
||||||
// Generate each type
|
// Generate each type
|
||||||
@@ -147,7 +165,7 @@ namespace Il2CppInspector
|
|||||||
|
|
||||||
// Stop if nothing to output
|
// Stop if nothing to output
|
||||||
if (!usedTypes.Any())
|
if (!usedTypes.Any())
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
// Close namespace
|
// Close namespace
|
||||||
if (useNamespaceSyntax && !string.IsNullOrEmpty(nsContext))
|
if (useNamespaceSyntax && !string.IsNullOrEmpty(nsContext))
|
||||||
@@ -187,7 +205,7 @@ namespace Il2CppInspector
|
|||||||
writer.Write("\n");
|
writer.Write("\n");
|
||||||
|
|
||||||
// Output assembly information and attributes
|
// Output assembly information and attributes
|
||||||
writer.Write(generateAssemblyInfo(assemblies, nsRefs) + "\n\n");
|
writer.Write(generateAssemblyInfo(assemblies, nsRefs, outputAssemblyAttributes) + "\n\n");
|
||||||
|
|
||||||
// Output type definitions
|
// Output type definitions
|
||||||
writer.Write(code);
|
writer.Write(code);
|
||||||
@@ -203,20 +221,23 @@ namespace Il2CppInspector
|
|||||||
System.Threading.Thread.Sleep(100);
|
System.Threading.Thread.Sleep(100);
|
||||||
}
|
}
|
||||||
} while (!fileWritten);
|
} 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();
|
var text = new StringBuilder();
|
||||||
|
|
||||||
foreach (var asm in assemblies) {
|
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");
|
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
|
// Assembly-level attributes
|
||||||
|
if (outputAssemblyAttributes)
|
||||||
lock (usedAssemblyAttributesLock) {
|
lock (usedAssemblyAttributesLock) {
|
||||||
text.Append(asm.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute)
|
text.Append(asm.CustomAttributes.Where(a => a.AttributeType.FullName != ExtAttribute)
|
||||||
.Except(usedAssemblyAttributes ?? new HashSet<CustomAttributeData>())
|
.Except(usedAssemblyAttributes ?? new HashSet<CustomAttributeData>())
|
||||||
.OrderBy(a => a.AttributeType.Name)
|
.OrderBy(a => a.AttributeType.Name)
|
||||||
.ToString(new Scope { Current = null, Namespaces = namespaces }, attributePrefix: "assembly: ", emitPointer: !SuppressMetadata, mustCompile: MustCompile));
|
.ToString(new Scope { Current = null, Namespaces = namespaces ?? new List<string>() }, attributePrefix: "assembly: ", emitPointer: !SuppressMetadata, mustCompile: MustCompile));
|
||||||
if (asm.CustomAttributes.Any())
|
if (asm.CustomAttributes.Any())
|
||||||
text.Append("\n");
|
text.Append("\n");
|
||||||
|
|
||||||
|
|||||||
@@ -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.")]
|
[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; }
|
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
|
// Adapted from: https://stackoverflow.com/questions/16376191/measuring-code-execution-time
|
||||||
@@ -143,10 +146,10 @@ namespace Il2CppInspector
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ("assembly", "index"):
|
case ("assembly", "index"):
|
||||||
writer.WriteFilesByAssembly(csOut, t => t.Index);
|
writer.WriteFilesByAssembly(csOut, t => t.Index, options.SeparateAssemblyAttributesFiles);
|
||||||
break;
|
break;
|
||||||
case ("assembly", "name"):
|
case ("assembly", "name"):
|
||||||
writer.WriteFilesByAssembly(csOut, t => t.Name);
|
writer.WriteFilesByAssembly(csOut, t => t.Name, options.SeparateAssemblyAttributesFiles);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ("class", _):
|
case ("class", _):
|
||||||
@@ -154,7 +157,7 @@ namespace Il2CppInspector
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ("tree", _):
|
case ("tree", _):
|
||||||
writer.WriteFilesByClassTree(csOut);
|
writer.WriteFilesByClassTree(csOut, options.SeparateAssemblyAttributesFiles);
|
||||||
break;
|
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
|
-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
|
-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.
|
-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:
|
Defaults if not specified:
|
||||||
|
|||||||
Reference in New Issue
Block a user