diff --git a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
index 25eadffae..ed60d54f5 100644
--- a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
+++ b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
@@ -269,7 +269,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{
foreach (var (name, value) in resourcesFile)
{
- string fileName = CleanUpFileName(name)
+ string fileName = SanitizeFileName(name)
.Replace('/', Path.DirectorySeparatorChar);
string dirName = Path.GetDirectoryName(fileName);
if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName))
@@ -571,14 +571,25 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
///
public static string CleanUpFileName(string text)
{
- return CleanUpFileName(text, separateAtDots: false);
+ return CleanUpName(text, separateAtDots: false, treatAsFileName: false);
+ }
+
+ ///
+ /// Removes invalid characters from file names and reduces their length,
+ /// but keeps file extensions intact.
+ ///
+ public static string SanitizeFileName(string fileName)
+ {
+ return CleanUpName(fileName, separateAtDots: false, treatAsFileName: true);
}
///
/// Cleans up a node name for use as a file system name. If is active,
- /// dots are seen as segment separators. Each segment is limited to 255 characters.
+ /// dots are seen as segment separators. Each segment is limited to maxSegmentLength characters.
+ /// (see ) If is active,
+ /// we check for file a extension and try to preserve it, if it's valid.
///
- static string CleanUpFileName(string text, bool separateAtDots)
+ static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName)
{
int pos = text.IndexOf(':');
if (pos > 0)
@@ -587,23 +598,49 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (pos > 0)
text = text.Substring(0, pos);
text = text.Trim();
- // Whitelist allowed characters, replace everything else:
- StringBuilder b = new StringBuilder(text.Length);
+ string extension = null;
int currentSegmentLength = 0;
var (supportsLongPaths, maxPathLength, maxSegmentLength) = longPathSupport.Value;
+ if (treatAsFileName)
+ {
+ // Check if input is a file name, i.e., has a valid extension
+ // If yes, preserve extension and append it at the end.
+ // But only, if the extension length does not exceed maxSegmentLength,
+ // if that's the case we just give up and treat the extension no different
+ // from the file name.
+ int lastDot = text.LastIndexOf('.');
+ if (lastDot >= 0 && text.Length - lastDot < maxSegmentLength)
+ {
+ string originalText = text;
+ extension = text.Substring(lastDot);
+ text = text.Remove(lastDot);
+ foreach (var c in extension)
+ {
+ if (!(char.IsLetterOrDigit(c) || c == '-' || c == '_' || c == '.'))
+ {
+ // extension contains an invalid character, therefore cannot be a valid extension.
+ extension = null;
+ text = originalText;
+ break;
+ }
+ }
+ }
+ }
+ // Whitelist allowed characters, replace everything else:
+ StringBuilder b = new StringBuilder(text.Length + (extension?.Length ?? 0));
foreach (var c in text)
{
currentSegmentLength++;
if (char.IsLetterOrDigit(c) || c == '-' || c == '_')
{
- // if the current segment exceeds 255 characters,
+ // if the current segment exceeds maxSegmentLength characters,
// skip until the end of the segment.
if (currentSegmentLength <= maxSegmentLength)
b.Append(c);
}
else if (c == '.' && b.Length > 0 && b[b.Length - 1] != '.')
{
- // if the current segment exceeds 255 characters,
+ // if the current segment exceeds maxSegmentLength characters,
// skip until the end of the segment.
if (separateAtDots || currentSegmentLength <= maxSegmentLength)
b.Append('.'); // allow dot, but never two in a row
@@ -614,7 +651,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
}
else
{
- // if the current segment exceeds 255 characters,
+ // if the current segment exceeds maxSegmentLength characters,
// skip until the end of the segment.
if (currentSegmentLength <= maxSegmentLength)
b.Append('-');
@@ -625,6 +662,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (b.Length == 0)
b.Append('-');
string name = b.ToString();
+ if (extension != null)
+ name += extension;
if (IsReservedFileSystemName(name))
return name + "_";
else if (name == ".")
@@ -638,7 +677,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
///
public static string CleanUpDirectoryName(string text)
{
- return CleanUpFileName(text, separateAtDots: true).Replace('.', Path.DirectorySeparatorChar);
+ return CleanUpName(text, separateAtDots: true, treatAsFileName: false)
+ .Replace('.', Path.DirectorySeparatorChar);
}
static bool IsReservedFileSystemName(string name)
diff --git a/ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs b/ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs
index e41d055bd..79974c89b 100644
--- a/ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs
+++ b/ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs
@@ -23,6 +23,7 @@ using System.Linq;
using System.Threading.Tasks;
using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
@@ -46,7 +47,7 @@ namespace ICSharpCode.ILSpy
return;
var assembly = selectedNodes[0].PackageEntry;
SaveFileDialog dlg = new SaveFileDialog();
- dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(assembly.Name));
+ dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(assembly.Name));
dlg.Filter = ".NET assemblies|*.dll;*.exe;*.winmd" + Resources.AllFiles;
dlg.InitialDirectory = Path.GetDirectoryName(bundleNode.LoadedAssembly.FileName);
if (dlg.ShowDialog() != true)
@@ -70,7 +71,7 @@ namespace ICSharpCode.ILSpy
{
foreach (var node in selectedNodes)
{
- var fileName = Path.GetFileName(DecompilerTextView.CleanUpName(node.PackageEntry.Name));
+ var fileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(node.PackageEntry.Name));
SaveEntry(output, node.PackageEntry, Path.Combine(outputFolderOrFileName, fileName));
}
}
diff --git a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs
index 97101ece0..2577206e6 100644
--- a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs
+++ b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs
@@ -25,6 +25,7 @@ using System.Windows;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
+using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
@@ -63,7 +64,7 @@ namespace ICSharpCode.ILSpy
return;
}
SaveFileDialog dlg = new SaveFileDialog();
- dlg.FileName = DecompilerTextView.CleanUpName(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 be9597b75..113122953 100644
--- a/ILSpy/Commands/SelectPdbContextMenuEntry.cs
+++ b/ILSpy/Commands/SelectPdbContextMenuEntry.cs
@@ -19,8 +19,8 @@
using System.IO;
using System.Linq;
+using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.ILSpy.Properties;
-using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using Microsoft.Win32;
@@ -35,7 +35,7 @@ namespace ICSharpCode.ILSpy
if (assembly == null)
return;
OpenFileDialog dlg = new OpenFileDialog();
- dlg.FileName = DecompilerTextView.CleanUpName(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 ffc9ef303..2c70a15bf 100644
--- a/ILSpy/TextView/DecompilerTextView.cs
+++ b/ILSpy/TextView/DecompilerTextView.cs
@@ -1034,7 +1034,7 @@ namespace ICSharpCode.ILSpy.TextView
SaveFileDialog dlg = new SaveFileDialog();
dlg.DefaultExt = language.FileExtension;
dlg.Filter = language.Name + "|*" + language.FileExtension + Properties.Resources.AllFiles;
- dlg.FileName = CleanUpName(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);
@@ -1149,14 +1149,6 @@ namespace ICSharpCode.ILSpy.TextView
thread.Start();
return tcs.Task;
}
-
- ///
- /// Cleans up a node name for use as a file name.
- ///
- internal static string CleanUpName(string text)
- {
- return WholeProjectDecompiler.CleanUpFileName(text);
- }
#endregion
internal ReferenceSegment? GetReferenceSegmentAtMousePosition()
diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs
index da10af18c..a4e9f1c0d 100644
--- a/ILSpy/TreeNodes/AssemblyTreeNode.cs
+++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs
@@ -26,10 +26,10 @@ using System.Windows.Controls;
using System.Windows.Documents;
using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Properties;
-using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.TreeView;
@@ -384,7 +384,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (string.IsNullOrEmpty(language.ProjectFileExtension))
return false;
SaveFileDialog dlg = new SaveFileDialog();
- dlg.FileName = DecompilerTextView.CleanUpName(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)
{
diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
index 659f72639..52271e845 100644
--- a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
+++ b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
@@ -20,8 +20,8 @@ using System;
using System.IO;
using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata;
-using ICSharpCode.ILSpy.TextView;
using Microsoft.Win32;
@@ -80,7 +80,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override bool Save(ViewModels.TabPageModel tabPage)
{
SaveFileDialog dlg = new SaveFileDialog();
- dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key));
+ dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(key));
if (dlg.ShowDialog() == true)
{
using var data = OpenStream();
diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs
index 37630289f..ce862a523 100644
--- a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs
+++ b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs
@@ -24,6 +24,7 @@ using System.Text;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
@@ -91,7 +92,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (type == FileType.Xml)
ext = ".xml";
else
- ext = Path.GetExtension(DecompilerTextView.CleanUpName(Resource.Name));
+ ext = Path.GetExtension(WholeProjectDecompiler.SanitizeFileName(Resource.Name));
tabPage.ShowTextView(textView => textView.ShowNode(output, this, HighlightingManager.Instance.GetDefinitionByExtension(ext)));
tabPage.SupportsLanguageSwitching = false;
return true;
@@ -106,7 +107,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (s == null)
return false;
SaveFileDialog dlg = new SaveFileDialog();
- dlg.FileName = DecompilerTextView.CleanUpName(Resource.Name);
+ dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(Resource.Name));
if (dlg.ShowDialog() == true)
{
s.Position = 0;
diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs
index de6145d68..bdef592c7 100644
--- a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs
+++ b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs
@@ -24,6 +24,7 @@ using System.IO;
using System.Linq;
using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.Controls;
@@ -129,7 +130,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (s == null)
return false;
SaveFileDialog dlg = new SaveFileDialog();
- dlg.FileName = DecompilerTextView.CleanUpName(Resource.Name);
+ dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(Resource.Name));
dlg.Filter = Resources.ResourcesFileFilter;
if (dlg.ShowDialog() == true)
{