|
|
|
|
@ -151,8 +151,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -151,8 +151,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
TargetDirectory = targetDirectory; |
|
|
|
|
directories.Clear(); |
|
|
|
|
var resources = WriteResourceFilesInProject(moduleDefinition).ToList(); |
|
|
|
|
var files = WriteCodeFilesInProject(moduleDefinition, resources.SelectMany(r => r.partialTypes ?? Enumerable.Empty<PartialTypeInfo>()).ToList(), cancellationToken).ToList(); |
|
|
|
|
files.AddRange(resources.Select(r => (r.itemType, r.fileName))); |
|
|
|
|
var files = WriteCodeFilesInProject(moduleDefinition, resources.SelectMany(r => r.PartialTypes ?? Enumerable.Empty<PartialTypeInfo>()).ToList(), cancellationToken).ToList(); |
|
|
|
|
files.AddRange(resources); |
|
|
|
|
files.AddRange(WriteMiscellaneousFilesInProject(moduleDefinition)); |
|
|
|
|
if (StrongNameKeyFile != null) |
|
|
|
|
{ |
|
|
|
|
@ -190,7 +190,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -190,7 +190,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
return decompiler; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IEnumerable<(string itemType, string fileName)> WriteAssemblyInfo(DecompilerTypeSystem ts, CancellationToken cancellationToken) |
|
|
|
|
IEnumerable<ProjectItemInfo> WriteAssemblyInfo(DecompilerTypeSystem ts, CancellationToken cancellationToken) |
|
|
|
|
{ |
|
|
|
|
var decompiler = CreateDecompiler(ts); |
|
|
|
|
decompiler.CancellationToken = cancellationToken; |
|
|
|
|
@ -205,10 +205,10 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -205,10 +205,10 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
{ |
|
|
|
|
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions)); |
|
|
|
|
} |
|
|
|
|
return new[] { ("Compile", assemblyInfo) }; |
|
|
|
|
return new[] { new ProjectItemInfo("Compile", assemblyInfo) }; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IEnumerable<(string itemType, string fileName)> WriteCodeFilesInProject(Metadata.PEFile module, IList<PartialTypeInfo> partialTypes, CancellationToken cancellationToken) |
|
|
|
|
IEnumerable<ProjectItemInfo> WriteCodeFilesInProject(Metadata.PEFile module, IList<PartialTypeInfo> partialTypes, CancellationToken cancellationToken) |
|
|
|
|
{ |
|
|
|
|
var metadata = module.Metadata; |
|
|
|
|
var files = module.Metadata.GetTopLevelTypeDefinitions().Where(td => IncludeTypeWhenDecompilingProject(module, td)) |
|
|
|
|
@ -229,12 +229,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -229,12 +229,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
progress.TotalUnits = files.Count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return files.Select(f => ("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken)); |
|
|
|
|
return files.Select(f => new ProjectItemInfo("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken)); |
|
|
|
|
|
|
|
|
|
string GetFileFileNameForHandle(TypeDefinitionHandle h) |
|
|
|
|
{ |
|
|
|
|
var type = metadata.GetTypeDefinition(h); |
|
|
|
|
string file = CleanUpFileName(metadata.GetString(type.Name)) + ".cs"; |
|
|
|
|
string file = SanitizeFileName(metadata.GetString(type.Name) + ".cs"); |
|
|
|
|
string ns = metadata.GetString(type.Namespace); |
|
|
|
|
if (string.IsNullOrEmpty(ns)) |
|
|
|
|
{ |
|
|
|
|
@ -259,45 +259,43 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -259,45 +259,43 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
CancellationToken = cancellationToken |
|
|
|
|
}, |
|
|
|
|
delegate (IGrouping<string, TypeDefinitionHandle> file) { |
|
|
|
|
using (StreamWriter w = new StreamWriter(Path.Combine(TargetDirectory, file.Key))) |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
using StreamWriter w = new StreamWriter(Path.Combine(TargetDirectory, file.Key)); |
|
|
|
|
CSharpDecompiler decompiler = CreateDecompiler(ts); |
|
|
|
|
|
|
|
|
|
foreach (var partialType in partialTypes) |
|
|
|
|
{ |
|
|
|
|
CSharpDecompiler decompiler = CreateDecompiler(ts); |
|
|
|
|
decompiler.AddPartialTypeDefinition(partialType); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
decompiler.CancellationToken = cancellationToken; |
|
|
|
|
var declaredTypes = file.ToArray(); |
|
|
|
|
var syntaxTree = decompiler.DecompileTypes(declaredTypes); |
|
|
|
|
|
|
|
|
|
foreach (var partialType in partialTypes) |
|
|
|
|
foreach (var node in syntaxTree.Descendants) |
|
|
|
|
{ |
|
|
|
|
var td = (node.GetResolveResult() as TypeResolveResult)?.Type.GetDefinition(); |
|
|
|
|
if (td?.ParentModule != ts.MainModule) |
|
|
|
|
continue; |
|
|
|
|
while (td?.DeclaringTypeDefinition != null) |
|
|
|
|
{ |
|
|
|
|
decompiler.AddPartialTypeDefinition(partialType); |
|
|
|
|
td = td.DeclaringTypeDefinition; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
decompiler.CancellationToken = cancellationToken; |
|
|
|
|
var declaredTypes = file.ToArray(); |
|
|
|
|
var syntaxTree = decompiler.DecompileTypes(declaredTypes); |
|
|
|
|
|
|
|
|
|
foreach (var node in syntaxTree.Descendants) |
|
|
|
|
if (td != null && td.MetadataToken is { IsNil: false } token && !processedTypes.Contains((TypeDefinitionHandle)token)) |
|
|
|
|
{ |
|
|
|
|
var td = (node.GetResolveResult() as TypeResolveResult)?.Type.GetDefinition(); |
|
|
|
|
if (td?.ParentModule != ts.MainModule) |
|
|
|
|
continue; |
|
|
|
|
while (td?.DeclaringTypeDefinition != null) |
|
|
|
|
{ |
|
|
|
|
td = td.DeclaringTypeDefinition; |
|
|
|
|
} |
|
|
|
|
if (td != null && td.MetadataToken is { IsNil: false } token && !processedTypes.Contains((TypeDefinitionHandle)token)) |
|
|
|
|
lock (workList) |
|
|
|
|
{ |
|
|
|
|
lock (workList) |
|
|
|
|
{ |
|
|
|
|
workList.Add((TypeDefinitionHandle)token); |
|
|
|
|
} |
|
|
|
|
workList.Add((TypeDefinitionHandle)token); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions)); |
|
|
|
|
} |
|
|
|
|
catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) |
|
|
|
|
{ |
|
|
|
|
throw new DecompilerException(module, $"Error decompiling for '{file.Key}'", innerException); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions)); |
|
|
|
|
} |
|
|
|
|
catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) |
|
|
|
|
{ |
|
|
|
|
throw new DecompilerException(module, $"Error decompiling for '{file.Key}'", innerException); |
|
|
|
|
} |
|
|
|
|
progress.Status = file.Key; |
|
|
|
|
Interlocked.Increment(ref progress.UnitsCompleted); |
|
|
|
|
@ -308,7 +306,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -308,7 +306,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region WriteResourceFilesInProject
|
|
|
|
|
protected virtual IEnumerable<(string itemType, string fileName, List<PartialTypeInfo> partialTypes)> WriteResourceFilesInProject(Metadata.PEFile module) |
|
|
|
|
protected virtual IEnumerable<ProjectItemInfo> WriteResourceFilesInProject(Metadata.PEFile module) |
|
|
|
|
{ |
|
|
|
|
foreach (var r in module.Resources.Where(r => r.ResourceType == ResourceType.Embedded)) |
|
|
|
|
{ |
|
|
|
|
@ -318,7 +316,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -318,7 +316,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
if (r.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) |
|
|
|
|
{ |
|
|
|
|
bool decodedIntoIndividualFiles; |
|
|
|
|
var individualResources = new List<(string itemType, string fileName, List<PartialTypeInfo> partialTypes)>(); |
|
|
|
|
var individualResources = new List<ProjectItemInfo>(); |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
var resourcesFile = new ResourcesFile(stream); |
|
|
|
|
@ -378,12 +376,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -378,12 +376,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
stream.Position = 0; |
|
|
|
|
stream.CopyTo(fs); |
|
|
|
|
} |
|
|
|
|
yield return ("EmbeddedResource", fileName, null); |
|
|
|
|
yield return new ProjectItemInfo("EmbeddedResource", fileName).With("LogicalName", r.Name); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected virtual IEnumerable<(string itemType, string fileName, List<PartialTypeInfo> partialTypes)> WriteResourceToFile(string fileName, string resourceName, Stream entryStream) |
|
|
|
|
protected virtual IEnumerable<ProjectItemInfo> WriteResourceToFile(string fileName, string resourceName, Stream entryStream) |
|
|
|
|
{ |
|
|
|
|
if (fileName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) |
|
|
|
|
{ |
|
|
|
|
@ -398,7 +396,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -398,7 +396,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
writer.AddResource(entry.Key, entry.Value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return new[] { ("EmbeddedResource", resx, (List<PartialTypeInfo>)null) }; |
|
|
|
|
return new[] { new ProjectItemInfo("EmbeddedResource", resx).With("LogicalName", resourceName) }; |
|
|
|
|
} |
|
|
|
|
catch (BadImageFormatException) |
|
|
|
|
{ |
|
|
|
|
@ -413,7 +411,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -413,7 +411,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
{ |
|
|
|
|
entryStream.CopyTo(fs); |
|
|
|
|
} |
|
|
|
|
return new[] { ("EmbeddedResource", fileName, (List<PartialTypeInfo>)null) }; |
|
|
|
|
return new[] { new ProjectItemInfo("EmbeddedResource", fileName).With("LogicalName", resourceName) }; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
string GetFileNameForResource(string fullName) |
|
|
|
|
@ -426,7 +424,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -426,7 +424,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
// the directory part Namespace1\Namespace2\... reuses as many existing directories as
|
|
|
|
|
// possible, and only the remaining name parts are used as prefix for the filename.
|
|
|
|
|
// This is not affected by the UseNestedDirectoriesForNamespaces setting.
|
|
|
|
|
string[] splitName = fullName.Split(Path.DirectorySeparatorChar); |
|
|
|
|
string[] splitName = fullName.Split('\\', '/'); |
|
|
|
|
string fileName = string.Join(".", splitName); |
|
|
|
|
string separator = Path.DirectorySeparatorChar.ToString(); |
|
|
|
|
for (int i = splitName.Length - 1; i > 0; i--) |
|
|
|
|
@ -444,7 +442,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -444,7 +442,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region WriteMiscellaneousFilesInProject
|
|
|
|
|
protected virtual IEnumerable<(string itemType, string fileName)> WriteMiscellaneousFilesInProject(PEFile module) |
|
|
|
|
protected virtual IEnumerable<ProjectItemInfo> WriteMiscellaneousFilesInProject(PEFile module) |
|
|
|
|
{ |
|
|
|
|
var resources = module.Reader.ReadWin32Resources(); |
|
|
|
|
if (resources == null) |
|
|
|
|
@ -454,21 +452,21 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -454,21 +452,21 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
if (appIcon != null) |
|
|
|
|
{ |
|
|
|
|
File.WriteAllBytes(Path.Combine(TargetDirectory, "app.ico"), appIcon); |
|
|
|
|
yield return ("ApplicationIcon", "app.ico"); |
|
|
|
|
yield return new ProjectItemInfo("ApplicationIcon", "app.ico"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
byte[] appManifest = CreateApplicationManifest(resources); |
|
|
|
|
if (appManifest != null && !IsDefaultApplicationManifest(appManifest)) |
|
|
|
|
{ |
|
|
|
|
File.WriteAllBytes(Path.Combine(TargetDirectory, "app.manifest"), appManifest); |
|
|
|
|
yield return ("ApplicationManifest", "app.manifest"); |
|
|
|
|
yield return new ProjectItemInfo("ApplicationManifest", "app.manifest"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var appConfig = module.FileName + ".config"; |
|
|
|
|
if (File.Exists(appConfig)) |
|
|
|
|
{ |
|
|
|
|
File.Copy(appConfig, Path.Combine(TargetDirectory, "app.config"), overwrite: true); |
|
|
|
|
yield return ("ApplicationConfig", Path.GetFileName(appConfig)); |
|
|
|
|
yield return new ProjectItemInfo("ApplicationConfig", Path.GetFileName(appConfig)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -618,10 +616,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -618,10 +616,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
/// </summary>
|
|
|
|
|
static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName) |
|
|
|
|
{ |
|
|
|
|
// Remove anything that could be confused with a rooted path.
|
|
|
|
|
int pos = text.IndexOf(':'); |
|
|
|
|
if (pos > 0) |
|
|
|
|
text = text.Substring(0, pos); |
|
|
|
|
pos = text.IndexOf('`'); |
|
|
|
|
if (pos > 0) |
|
|
|
|
text = text.Substring(0, pos); |
|
|
|
|
text = text.Trim(); |
|
|
|
|
@ -652,6 +648,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -652,6 +648,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Remove generics
|
|
|
|
|
pos = text.IndexOf('`'); |
|
|
|
|
if (pos > 0) |
|
|
|
|
{ |
|
|
|
|
text = text.Substring(0, pos).Trim(); |
|
|
|
|
} |
|
|
|
|
// Whitelist allowed characters, replace everything else:
|
|
|
|
|
StringBuilder b = new StringBuilder(text.Length + (extension?.Length ?? 0)); |
|
|
|
|
foreach (var c in text) |
|
|
|
|
@ -693,7 +695,15 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -693,7 +695,15 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
b.Append('-'); |
|
|
|
|
string name = b.ToString(); |
|
|
|
|
if (extension != null) |
|
|
|
|
{ |
|
|
|
|
// make sure that adding the extension to the filename
|
|
|
|
|
// does not exceed maxSegmentLength.
|
|
|
|
|
// trim the name, if necessary.
|
|
|
|
|
if (name.Length + extension.Length > maxSegmentLength) |
|
|
|
|
name = name.Remove(name.Length - extension.Length); |
|
|
|
|
name += extension; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (IsReservedFileSystemName(name)) |
|
|
|
|
return name + "_"; |
|
|
|
|
else if (name == ".") |
|
|
|
|
@ -753,4 +763,25 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
@@ -753,4 +763,25 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
|
|
|
|
|
return TargetServices.DetectTargetFramework(module).Moniker != null; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public record struct ProjectItemInfo(string ItemType, string FileName) |
|
|
|
|
{ |
|
|
|
|
public List<PartialTypeInfo> PartialTypes { get; set; } = null; |
|
|
|
|
|
|
|
|
|
public Dictionary<string, string> AdditionalProperties { get; set; } = null; |
|
|
|
|
|
|
|
|
|
public ProjectItemInfo With(string name, string value) |
|
|
|
|
{ |
|
|
|
|
AdditionalProperties ??= new Dictionary<string, string>(); |
|
|
|
|
AdditionalProperties.Add(name, value); |
|
|
|
|
return this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public ProjectItemInfo With(IEnumerable<KeyValuePair<string, string>> pairs) |
|
|
|
|
{ |
|
|
|
|
AdditionalProperties ??= new Dictionary<string, string>(); |
|
|
|
|
AdditionalProperties.AddRange(pairs); |
|
|
|
|
return this; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|