From 31bbcf41bcb0dcd0b3e019bc56fa158d8ac37f70 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 21 Feb 2025 09:14:23 +0100 Subject: [PATCH] Fix handling of file extensions in project export. --- .../WholeProjectDecompiler.cs | 28 +++++++++++-------- .../DebugInfo/PortablePdbWriter.cs | 2 +- .../BamlResourceNodeFactory.cs | 2 +- ILSpy/Commands/GeneratePdbContextMenuEntry.cs | 2 +- ILSpy/Commands/SelectPdbContextMenuEntry.cs | 2 +- ILSpy/TextView/DecompilerTextView.cs | 2 +- ILSpy/TreeNodes/AssemblyTreeNode.cs | 2 +- 7 files changed, 22 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs index 283e2cb943..ec3e955b2c 100644 --- a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection.Metadata; @@ -135,7 +136,7 @@ protected WholeProjectDecompiler( public void DecompileProject(MetadataFile file, string targetDirectory, CancellationToken cancellationToken = default(CancellationToken)) { - string projectFileName = Path.Combine(targetDirectory, CleanUpFileName(file.Name) + ".csproj"); + string projectFileName = Path.Combine(targetDirectory, CleanUpFileName(file.Name, ".csproj")); using (var writer = new StreamWriter(projectFileName)) { DecompileProject(file, targetDirectory, writer, cancellationToken); @@ -238,7 +239,7 @@ IEnumerable WriteCodeFilesInProject(MetadataFile module, IList< string GetFileFileNameForHandle(TypeDefinitionHandle h) { var type = metadata.GetTypeDefinition(h); - string file = CleanUpFileName(metadata.GetString(type.Name) + ".cs"); + string file = CleanUpFileName(metadata.GetString(type.Name), ".cs"); string ns = metadata.GetString(type.Namespace); if (string.IsNullOrEmpty(ns)) { @@ -339,8 +340,7 @@ protected virtual IEnumerable WriteResourceFilesInProject(Metad { foreach (var (name, value) in resourcesFile) { - string fileName = SanitizeFileName(name) - .Replace('/', Path.DirectorySeparatorChar); + string fileName = SanitizeFileName(name); string dirName = Path.GetDirectoryName(fileName); if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName)) { @@ -609,9 +609,14 @@ static string CleanUpApplicationManifest(byte[] appManifest) /// /// Cleans up a node name for use as a file name. /// - public static string CleanUpFileName(string text) + public static string CleanUpFileName(string text, string extension) { - return CleanUpName(text, separateAtDots: false, treatAsFileName: false); + Debug.Assert(!string.IsNullOrEmpty(extension)); + if (!extension.StartsWith(".")) + extension = "." + extension; + text = text + extension; + + return CleanUpName(text, separateAtDots: false, treatAsFileName: !string.IsNullOrEmpty(extension), treatAsPath: false); } /// @@ -620,7 +625,7 @@ public static string CleanUpFileName(string text) /// public static string SanitizeFileName(string fileName) { - return CleanUpName(fileName, separateAtDots: false, treatAsFileName: true); + return CleanUpName(fileName, separateAtDots: false, treatAsFileName: true, treatAsPath: true); } /// @@ -629,7 +634,7 @@ public static string SanitizeFileName(string fileName) /// If is active, we check for file a extension and try to preserve it, /// if it's valid. /// - static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName) + static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName, bool treatAsPath) { // Remove anything that could be confused with a rooted path. int pos = text.IndexOf(':'); @@ -692,7 +697,7 @@ static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName if (separateAtDots) currentSegmentLength = 0; } - else if (treatAsFileName && (c is '/' or '\\') && currentSegmentLength > 1) + else if (treatAsPath && (c is '/' or '\\') && currentSegmentLength > 1) { // if we treat this as a file name, we've started a new segment b.Append(Path.DirectorySeparatorChar); @@ -732,13 +737,12 @@ static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName /// public static string CleanUpDirectoryName(string text) { - return CleanUpName(text, separateAtDots: false, treatAsFileName: false); + return CleanUpName(text, separateAtDots: false, treatAsFileName: false, treatAsPath: false); } public static string CleanUpPath(string text) { - return CleanUpName(text, separateAtDots: true, treatAsFileName: false) - .Replace('.', Path.DirectorySeparatorChar); + return CleanUpName(text, separateAtDots: true, treatAsFileName: true, treatAsPath: true); } static bool IsReservedFileSystemName(string name) diff --git a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs index 3779ace159..792c3e727a 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -92,7 +92,7 @@ string BuildFileNameFromTypeName(TypeDefinitionHandle handle) string ns = settings.UseNestedDirectoriesForNamespaces ? WholeProjectDecompiler.CleanUpPath(typeName.Namespace) : WholeProjectDecompiler.CleanUpDirectoryName(typeName.Namespace); - return Path.Combine(ns, WholeProjectDecompiler.CleanUpFileName(typeName.Name) + ".cs"); + return Path.Combine(ns, WholeProjectDecompiler.CleanUpFileName(typeName.Name, ".cs")); } var sourceFiles = reader.GetTopLevelTypeDefinitions().Where(t => IncludeTypeWhenGeneratingPdb(file, t, settings)).GroupBy(BuildFileNameFromTypeName).ToList(); diff --git a/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs index 29c54c1003..681a92d5ab 100644 --- a/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs +++ b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs @@ -62,7 +62,7 @@ public string WriteResourceToFile(LoadedAssembly assembly, string fileName, Stre var typeDefinition = result.TypeName.HasValue ? typeSystem.MainModule.GetTypeDefinition(result.TypeName.Value.TopLevelTypeName) : null; if (typeDefinition != null) { - fileName = WholeProjectDecompiler.CleanUpPath(typeDefinition.ReflectionName) + ".xaml"; + fileName = WholeProjectDecompiler.CleanUpPath(typeDefinition.ReflectionName + ".xaml"); var partialTypeInfo = new PartialTypeInfo(typeDefinition); foreach (var member in result.GeneratedMembers) { diff --git a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs index 7a03748950..33e10b24d4 100644 --- a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs +++ b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs @@ -71,7 +71,7 @@ internal static void GeneratePdbForAssembly(LoadedAssembly assembly, LanguageSer return; } SaveFileDialog dlg = new SaveFileDialog(); - dlg.FileName = WholeProjectDecompiler.CleanUpFileName(assembly.ShortName) + ".pdb"; + dlg.FileName = WholeProjectDecompiler.CleanUpFileName(assembly.ShortName, ".pdb"); dlg.Filter = Resources.PortablePDBPdbAllFiles; dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName); if (dlg.ShowDialog() != true) diff --git a/ILSpy/Commands/SelectPdbContextMenuEntry.cs b/ILSpy/Commands/SelectPdbContextMenuEntry.cs index 9c63a8b8c1..5d3d953515 100644 --- a/ILSpy/Commands/SelectPdbContextMenuEntry.cs +++ b/ILSpy/Commands/SelectPdbContextMenuEntry.cs @@ -38,7 +38,7 @@ public async void Execute(TextViewContext context) if (assembly == null) return; OpenFileDialog dlg = new OpenFileDialog(); - dlg.FileName = WholeProjectDecompiler.CleanUpFileName(assembly.ShortName) + ".pdb"; + dlg.FileName = WholeProjectDecompiler.CleanUpFileName(assembly.ShortName, ".pdb"); dlg.Filter = Resources.PortablePDBPdbAllFiles; dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName); if (dlg.ShowDialog() != true) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 3b9ea493ce..dc0d5edaec 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -1091,7 +1091,7 @@ public void SaveToDisk(ILSpy.Language language, IEnumerable treeN SaveFileDialog dlg = new SaveFileDialog(); dlg.DefaultExt = language.FileExtension; dlg.Filter = language.Name + "|*" + language.FileExtension + Properties.Resources.AllFiles; - dlg.FileName = WholeProjectDecompiler.CleanUpFileName(treeNodes.First().ToString()) + language.FileExtension; + dlg.FileName = WholeProjectDecompiler.CleanUpFileName(treeNodes.First().ToString(), language.FileExtension); if (dlg.ShowDialog() == true) { SaveToDisk(new DecompilationContext(language, treeNodes.ToArray(), options), dlg.FileName); diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index a34882e4d6..e2efa8d1aa 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -475,7 +475,7 @@ public override bool Save(TabPageModel tabPage) if (string.IsNullOrEmpty(language.ProjectFileExtension)) return false; SaveFileDialog dlg = new SaveFileDialog(); - dlg.FileName = WholeProjectDecompiler.CleanUpFileName(LoadedAssembly.ShortName) + language.ProjectFileExtension; + dlg.FileName = WholeProjectDecompiler.CleanUpFileName(LoadedAssembly.ShortName, language.ProjectFileExtension); dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension + "|" + language.Name + " single file|*" + language.FileExtension + "|All files|*.*"; if (dlg.ShowDialog() == true) {