Browse Source

Merge pull request #2567 from icsharpcode/filename-sanitizer

Improve file name sanitizer
pull/2700/head
Siegfried Pammer 4 years ago committed by GitHub
parent
commit
0f3e8b79be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml
  2. 2
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml
  3. 2
      ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml
  4. 78
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
  5. 5
      ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs
  6. 21
      ICSharpCode.Decompiler/DecompilerSettings.cs
  7. 5
      ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs
  8. 3
      ILSpy/Commands/GeneratePdbContextMenuEntry.cs
  9. 4
      ILSpy/Commands/SelectPdbContextMenuEntry.cs
  10. 4
      ILSpy/Options/MiscSettingsPanel.xaml
  11. 4
      ILSpy/Options/MiscSettingsPanel.xaml.cs
  12. 26
      ILSpy/Properties/Resources.Designer.cs
  13. 14
      ILSpy/Properties/Resources.resx
  14. 10
      ILSpy/TextView/DecompilerTextView.cs
  15. 4
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  16. 4
      ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
  17. 5
      ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs
  18. 3
      ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs

2
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ForLoopTests.xml

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<symbols> <symbols>
<files> <files>
<file id="1" name="ICSharpCode\Decompiler\Tests\TestCases\PdbGen\ForLoopTests.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System; <file id="1" name="ICSharpCode.Decompiler.Tests.TestCases.PdbGen\ForLoopTests.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen; namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;

2
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/HelloWorld.xml

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<symbols> <symbols>
<files> <files>
<file id="1" name="ICSharpCode\Decompiler\Tests\TestCases\PdbGen\HelloWorld.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System; <file id="1" name="ICSharpCode.Decompiler.Tests.TestCases.PdbGen\HelloWorld.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen; namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;

2
ICSharpCode.Decompiler.Tests/TestCases/PdbGen/LambdaCapturing.xml

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<symbols> <symbols>
<files> <files>
<file id="1" name="ICSharpCode\Decompiler\Tests\TestCases\PdbGen\LambdaCapturing.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System; <file id="1" name="ICSharpCode.Decompiler.Tests.TestCases.PdbGen\LambdaCapturing.cs" language="C#" checksumAlgorithm="SHA256"><![CDATA[using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen; namespace ICSharpCode.Decompiler.Tests.TestCases.PdbGen;

78
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

@ -214,7 +214,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
} }
else else
{ {
string dir = CleanUpDirectoryName(ns); string dir = Settings.UseNestedDirectoriesForNamespaces ? CleanUpPath(ns) : CleanUpDirectoryName(ns);
if (directories.Add(dir)) if (directories.Add(dir))
Directory.CreateDirectory(Path.Combine(TargetDirectory, dir)); Directory.CreateDirectory(Path.Combine(TargetDirectory, dir));
return Path.Combine(dir, file); return Path.Combine(dir, file);
@ -269,7 +269,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{ {
foreach (var (name, value) in resourcesFile) foreach (var (name, value) in resourcesFile)
{ {
string fileName = CleanUpFileName(name) string fileName = SanitizeFileName(name)
.Replace('/', Path.DirectorySeparatorChar); .Replace('/', Path.DirectorySeparatorChar);
string dirName = Path.GetDirectoryName(fileName); string dirName = Path.GetDirectoryName(fileName);
if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName)) if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName))
@ -279,7 +279,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
Stream entryStream = (Stream)value; Stream entryStream = (Stream)value;
entryStream.Position = 0; entryStream.Position = 0;
individualResources.AddRange( individualResources.AddRange(
WriteResourceToFile(fileName, (string)name, entryStream)); WriteResourceToFile(fileName, name, entryStream));
} }
decodedIntoIndividualFiles = true; decodedIntoIndividualFiles = true;
} }
@ -368,6 +368,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
// That is, a full name of the form "Namespace1.Namespace2{...}.NamespaceN.ResourceName" is split such that // That is, a full name of the form "Namespace1.Namespace2{...}.NamespaceN.ResourceName" is split such that
// the directory part Namespace1\Namespace2\... reuses as many existing directories as // 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. // 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(Path.DirectorySeparatorChar);
string fileName = string.Join(".", splitName); string fileName = string.Join(".", splitName);
string separator = Path.DirectorySeparatorChar.ToString(); string separator = Path.DirectorySeparatorChar.ToString();
@ -546,7 +547,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{ {
case PlatformID.MacOSX: case PlatformID.MacOSX:
case PlatformID.Unix: case PlatformID.Unix:
return (true, int.MaxValue, int.MaxValue); return (true, int.MaxValue, 255);
case PlatformID.Win32NT: case PlatformID.Win32NT:
const string key = @"SYSTEM\CurrentControlSet\Control\FileSystem"; const string key = @"SYSTEM\CurrentControlSet\Control\FileSystem";
var fileSystem = Registry.LocalMachine.OpenSubKey(key); var fileSystem = Registry.LocalMachine.OpenSubKey(key);
@ -571,14 +572,25 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
/// </summary> /// </summary>
public static string CleanUpFileName(string text) public static string CleanUpFileName(string text)
{ {
return CleanUpFileName(text, separateAtDots: false); return CleanUpName(text, separateAtDots: false, treatAsFileName: false);
}
/// <summary>
/// Removes invalid characters from file names and reduces their length,
/// but keeps file extensions and path structure intact.
/// </summary>
public static string SanitizeFileName(string fileName)
{
return CleanUpName(fileName, separateAtDots: false, treatAsFileName: true);
} }
/// <summary> /// <summary>
/// Cleans up a node name for use as a file system name. If <paramref name="separateAtDots"/> is active, /// Cleans up a node name for use as a file system name. If <paramref name="separateAtDots"/> 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 <see cref="GetLongPathSupport"/>) If <paramref name="treatAsFileName"/> is active,
/// we check for file a extension and try to preserve it, if it's valid.
/// </summary> /// </summary>
static string CleanUpFileName(string text, bool separateAtDots) static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName)
{ {
int pos = text.IndexOf(':'); int pos = text.IndexOf(':');
if (pos > 0) if (pos > 0)
@ -587,23 +599,49 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (pos > 0) if (pos > 0)
text = text.Substring(0, pos); text = text.Substring(0, pos);
text = text.Trim(); text = text.Trim();
// Whitelist allowed characters, replace everything else: string extension = null;
StringBuilder b = new StringBuilder(text.Length);
int currentSegmentLength = 0; int currentSegmentLength = 0;
var (supportsLongPaths, maxPathLength, maxSegmentLength) = longPathSupport.Value; 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) foreach (var c in text)
{ {
currentSegmentLength++; currentSegmentLength++;
if (char.IsLetterOrDigit(c) || c == '-' || c == '_') 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. // skip until the end of the segment.
if (currentSegmentLength <= maxSegmentLength) if (currentSegmentLength <= maxSegmentLength)
b.Append(c); b.Append(c);
} }
else if (c == '.' && b.Length > 0 && b[b.Length - 1] != '.') 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. // skip until the end of the segment.
if (separateAtDots || currentSegmentLength <= maxSegmentLength) if (separateAtDots || currentSegmentLength <= maxSegmentLength)
b.Append('.'); // allow dot, but never two in a row b.Append('.'); // allow dot, but never two in a row
@ -612,9 +650,15 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (separateAtDots) if (separateAtDots)
currentSegmentLength = 0; currentSegmentLength = 0;
} }
else if (treatAsFileName && (c == '/' || c == '\\') && currentSegmentLength > 0)
{
// if we treat this as a file name, we've started a new segment
b.Append(c);
currentSegmentLength = 0;
}
else else
{ {
// if the current segment exceeds 255 characters, // if the current segment exceeds maxSegmentLength characters,
// skip until the end of the segment. // skip until the end of the segment.
if (currentSegmentLength <= maxSegmentLength) if (currentSegmentLength <= maxSegmentLength)
b.Append('-'); b.Append('-');
@ -625,6 +669,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (b.Length == 0) if (b.Length == 0)
b.Append('-'); b.Append('-');
string name = b.ToString(); string name = b.ToString();
if (extension != null)
name += extension;
if (IsReservedFileSystemName(name)) if (IsReservedFileSystemName(name))
return name + "_"; return name + "_";
else if (name == ".") else if (name == ".")
@ -638,7 +684,13 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
/// </summary> /// </summary>
public static string CleanUpDirectoryName(string text) public static string CleanUpDirectoryName(string text)
{ {
return CleanUpFileName(text, separateAtDots: true).Replace('.', Path.DirectorySeparatorChar); return CleanUpName(text, separateAtDots: false, treatAsFileName: false);
}
public static string CleanUpPath(string text)
{
return CleanUpName(text, separateAtDots: true, treatAsFileName: false)
.Replace('.', Path.DirectorySeparatorChar);
} }
static bool IsReservedFileSystemName(string name) static bool IsReservedFileSystemName(string name)

5
ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs

@ -66,7 +66,10 @@ namespace ICSharpCode.Decompiler.DebugInfo
string BuildFileNameFromTypeName(TypeDefinitionHandle handle) string BuildFileNameFromTypeName(TypeDefinitionHandle handle)
{ {
var typeName = handle.GetFullTypeName(reader).TopLevelTypeName; var typeName = handle.GetFullTypeName(reader).TopLevelTypeName;
return Path.Combine(WholeProjectDecompiler.CleanUpDirectoryName(typeName.Namespace), WholeProjectDecompiler.CleanUpFileName(typeName.Name) + ".cs"); string ns = settings.UseNestedDirectoriesForNamespaces
? WholeProjectDecompiler.CleanUpPath(typeName.Namespace)
: WholeProjectDecompiler.CleanUpDirectoryName(typeName.Namespace);
return Path.Combine(ns, WholeProjectDecompiler.CleanUpFileName(typeName.Name) + ".cs");
} }
foreach (var sourceFile in reader.GetTopLevelTypeDefinitions().GroupBy(BuildFileNameFromTypeName)) foreach (var sourceFile in reader.GetTopLevelTypeDefinitions().GroupBy(BuildFileNameFromTypeName))

21
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -1777,7 +1777,7 @@ namespace ICSharpCode.Decompiler
/// Gets or sets a value indicating whether the new SDK style format /// Gets or sets a value indicating whether the new SDK style format
/// shall be used for the generated project files. /// shall be used for the generated project files.
/// </summary> /// </summary>
[Category("DecompilerSettings.Other")] [Category("DecompilerSettings.ProjectExport")]
[Description("DecompilerSettings.UseSdkStyleProjectFormat")] [Description("DecompilerSettings.UseSdkStyleProjectFormat")]
public bool UseSdkStyleProjectFormat { public bool UseSdkStyleProjectFormat {
get { return useSdkStyleProjectFormat; } get { return useSdkStyleProjectFormat; }
@ -1790,6 +1790,25 @@ namespace ICSharpCode.Decompiler
} }
} }
bool useNestedDirectoriesForNamespaces;
/// <summary>
/// Gets/sets whether namespaces and namespace-like identifiers should be split at '.'
/// and each part should produce a new level of nesting in the output directory structure.
/// </summary>
[Category("DecompilerSettings.ProjectExport")]
[Description("DecompilerSettings.UseNestedDirectoriesForNamespaces")]
public bool UseNestedDirectoriesForNamespaces {
get { return useNestedDirectoriesForNamespaces; }
set {
if (useNestedDirectoriesForNamespaces != value)
{
useNestedDirectoriesForNamespaces = value;
OnPropertyChanged();
}
}
}
bool aggressiveScalarReplacementOfAggregates = false; bool aggressiveScalarReplacementOfAggregates = false;
[Category("DecompilerSettings.Other")] [Category("DecompilerSettings.Other")]

5
ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs

@ -23,6 +23,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
@ -46,7 +47,7 @@ namespace ICSharpCode.ILSpy
return; return;
var assembly = selectedNodes[0].PackageEntry; var assembly = selectedNodes[0].PackageEntry;
SaveFileDialog dlg = new SaveFileDialog(); 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.Filter = ".NET assemblies|*.dll;*.exe;*.winmd" + Resources.AllFiles;
dlg.InitialDirectory = Path.GetDirectoryName(bundleNode.LoadedAssembly.FileName); dlg.InitialDirectory = Path.GetDirectoryName(bundleNode.LoadedAssembly.FileName);
if (dlg.ShowDialog() != true) if (dlg.ShowDialog() != true)
@ -70,7 +71,7 @@ namespace ICSharpCode.ILSpy
{ {
foreach (var node in selectedNodes) 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)); SaveEntry(output, node.PackageEntry, Path.Combine(outputFolderOrFileName, fileName));
} }
} }

3
ILSpy/Commands/GeneratePdbContextMenuEntry.cs

@ -25,6 +25,7 @@ using System.Windows;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TextView;
@ -63,7 +64,7 @@ namespace ICSharpCode.ILSpy
return; return;
} }
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(assembly.ShortName) + ".pdb"; dlg.FileName = WholeProjectDecompiler.CleanUpFileName(assembly.ShortName) + ".pdb";
dlg.Filter = Resources.PortablePDBPdbAllFiles; dlg.Filter = Resources.PortablePDBPdbAllFiles;
dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName); dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName);
if (dlg.ShowDialog() != true) if (dlg.ShowDialog() != true)

4
ILSpy/Commands/SelectPdbContextMenuEntry.cs

@ -19,8 +19,8 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
using Microsoft.Win32; using Microsoft.Win32;
@ -35,7 +35,7 @@ namespace ICSharpCode.ILSpy
if (assembly == null) if (assembly == null)
return; return;
OpenFileDialog dlg = new OpenFileDialog(); OpenFileDialog dlg = new OpenFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(assembly.ShortName) + ".pdb"; dlg.FileName = WholeProjectDecompiler.CleanUpFileName(assembly.ShortName) + ".pdb";
dlg.Filter = Resources.PortablePDBPdbAllFiles; dlg.Filter = Resources.PortablePDBPdbAllFiles;
dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName); dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName);
if (dlg.ShowDialog() != true) if (dlg.ShowDialog() != true)

4
ILSpy/Options/MiscSettingsPanel.xaml

@ -6,9 +6,13 @@
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties" xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"> d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Orientation="Vertical">
<GroupBox Header="{x:Static properties:Resources.Misc}">
<StackPanel Margin="10"> <StackPanel Margin="10">
<CheckBox IsChecked="{Binding AllowMultipleInstances}" Content="{x:Static properties:Resources.AllowMultipleInstances}" /> <CheckBox IsChecked="{Binding AllowMultipleInstances}" Content="{x:Static properties:Resources.AllowMultipleInstances}" />
<CheckBox IsChecked="{Binding LoadPreviousAssemblies}" Content="{x:Static properties:Resources.LoadAssembliesThatWereLoadedInTheLastInstance}"/> <CheckBox IsChecked="{Binding LoadPreviousAssemblies}" Content="{x:Static properties:Resources.LoadAssembliesThatWereLoadedInTheLastInstance}"/>
<Button Command="{Binding AddRemoveShellIntegrationCommand}" Content="{Binding AddRemoveShellIntegrationText}" Margin="3" /> <Button Command="{Binding AddRemoveShellIntegrationCommand}" Content="{Binding AddRemoveShellIntegrationText}" Margin="3" />
</StackPanel> </StackPanel>
</GroupBox>
</StackPanel>
</UserControl> </UserControl>

4
ILSpy/Options/MiscSettingsPanel.xaml.cs

@ -49,7 +49,7 @@ namespace ICSharpCode.ILSpy.Options
{ {
XElement e = settings["MiscSettings"]; XElement e = settings["MiscSettings"];
var s = new MiscSettings(); var s = new MiscSettings();
s.AllowMultipleInstances = (bool?)e.Attribute("AllowMultipleInstances") ?? false; s.AllowMultipleInstances = (bool?)e.Attribute(nameof(s.AllowMultipleInstances)) ?? false;
s.LoadPreviousAssemblies = (bool?)e.Attribute(nameof(s.LoadPreviousAssemblies)) ?? true; s.LoadPreviousAssemblies = (bool?)e.Attribute(nameof(s.LoadPreviousAssemblies)) ?? true;
return s; return s;
@ -60,7 +60,7 @@ namespace ICSharpCode.ILSpy.Options
var s = (MiscSettings)this.DataContext; var s = (MiscSettings)this.DataContext;
var section = new XElement("MiscSettings"); var section = new XElement("MiscSettings");
section.SetAttributeValue("AllowMultipleInstances", s.AllowMultipleInstances); section.SetAttributeValue(nameof(s.AllowMultipleInstances), s.AllowMultipleInstances);
section.SetAttributeValue(nameof(s.LoadPreviousAssemblies), s.LoadPreviousAssemblies); section.SetAttributeValue(nameof(s.LoadPreviousAssemblies), s.LoadPreviousAssemblies);
XElement existingElement = root.Element("MiscSettings"); XElement existingElement = root.Element("MiscSettings");

26
ILSpy/Properties/Resources.Designer.cs generated

@ -1127,6 +1127,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Project export.
/// </summary>
public static string DecompilerSettings_ProjectExport {
get {
return ResourceManager.GetString("DecompilerSettings.ProjectExport", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Ranges. /// Looks up a localized string similar to Ranges.
/// </summary> /// </summary>
@ -1316,6 +1325,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Use nested directories for namespaces.
/// </summary>
public static string DecompilerSettings_UseNestedDirectoriesForNamespaces {
get {
return ResourceManager.GetString("DecompilerSettings.UseNestedDirectoriesForNamespaces", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Use non-trailing named arguments. /// Looks up a localized string similar to Use non-trailing named arguments.
/// </summary> /// </summary>
@ -2002,7 +2020,7 @@ namespace ICSharpCode.ILSpy.Properties {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to You can change this by toggling the setting at Options &gt; Decompiler &gt; Other &gt; Use new SDK style format for generated project files (*.csproj).. /// Looks up a localized string similar to You can change this by toggling the setting at Options &gt; Decompiler &gt; Project export &gt; Use new SDK style format for generated project files (*.csproj)..
/// </summary> /// </summary>
public static string ProjectExportFormatChangeSettingHint { public static string ProjectExportFormatChangeSettingHint {
get { get {
@ -2029,9 +2047,11 @@ namespace ICSharpCode.ILSpy.Properties {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Failed to decompile the assemblies {0} because the namespace directory structure is nested too deep. /// Looks up a localized string similar to Failed to decompile the assemblies {0} because the namespace names are too long or the directory structure is nested too deep.
///
///If you are using Windows 10.0.14393 (Windows 10 version 1607) or later, you can enable &quot;Long path support&quot; by creating a REG_DWORD registry key named &quot;LongPathsEnabled&quot; with value 0x1 at &quot;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem&quot; (see https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation for more information).
/// ///
///If you are using Windows 10.0.14393 (Windows 10 version 1607) or later, you can enable &quot;Long path support&quot; by creating a REG_DWORD registry key named &quot;LongPathsEnabled&quot; with value 0x1 at &quot;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem&quot; (see https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation for more information).. ///If this does not [rest of string was truncated]&quot;;.
/// </summary> /// </summary>
public static string ProjectExportPathTooLong { public static string ProjectExportPathTooLong {
get { get {

14
ILSpy/Properties/Resources.resx

@ -399,6 +399,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.PatternMatching" xml:space="preserve"> <data name="DecompilerSettings.PatternMatching" xml:space="preserve">
<value>Use pattern matching expressions</value> <value>Use pattern matching expressions</value>
</data> </data>
<data name="DecompilerSettings.ProjectExport" xml:space="preserve">
<value>Project export</value>
</data>
<data name="DecompilerSettings.Ranges" xml:space="preserve"> <data name="DecompilerSettings.Ranges" xml:space="preserve">
<value>Ranges</value> <value>Ranges</value>
</data> </data>
@ -462,6 +465,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.UseNamedArguments" xml:space="preserve"> <data name="DecompilerSettings.UseNamedArguments" xml:space="preserve">
<value>Use named arguments</value> <value>Use named arguments</value>
</data> </data>
<data name="DecompilerSettings.UseNestedDirectoriesForNamespaces" xml:space="preserve">
<value>Use nested directories for namespaces</value>
</data>
<data name="DecompilerSettings.UseNonTrailingNamedArguments" xml:space="preserve"> <data name="DecompilerSettings.UseNonTrailingNamedArguments" xml:space="preserve">
<value>Use non-trailing named arguments</value> <value>Use non-trailing named arguments</value>
</data> </data>
@ -689,7 +695,7 @@ Please disable all filters that might hide the item (i.e. activate "View &gt; Sh
<value>Portable PDB|*.pdb|All files|*.*</value> <value>Portable PDB|*.pdb|All files|*.*</value>
</data> </data>
<data name="ProjectExportFormatChangeSettingHint" xml:space="preserve"> <data name="ProjectExportFormatChangeSettingHint" xml:space="preserve">
<value>You can change this by toggling the setting at Options &gt; Decompiler &gt; Other &gt; Use new SDK style format for generated project files (*.csproj).</value> <value>You can change this by toggling the setting at Options &gt; Decompiler &gt; Project export &gt; Use new SDK style format for generated project files (*.csproj).</value>
</data> </data>
<data name="ProjectExportFormatNonSDKHint" xml:space="preserve"> <data name="ProjectExportFormatNonSDKHint" xml:space="preserve">
<value>A Non-SDK project was generated. Learn more at https://docs.microsoft.com/en-us/nuget/resources/check-project-format.</value> <value>A Non-SDK project was generated. Learn more at https://docs.microsoft.com/en-us/nuget/resources/check-project-format.</value>
@ -698,9 +704,11 @@ Please disable all filters that might hide the item (i.e. activate "View &gt; Sh
<value>A SDK-style project was generated. Learn more at https://docs.microsoft.com/en-us/nuget/resources/check-project-format.</value> <value>A SDK-style project was generated. Learn more at https://docs.microsoft.com/en-us/nuget/resources/check-project-format.</value>
</data> </data>
<data name="ProjectExportPathTooLong" xml:space="preserve"> <data name="ProjectExportPathTooLong" xml:space="preserve">
<value>Failed to decompile the assemblies {0} because the namespace directory structure is nested too deep. <value>Failed to decompile the assemblies {0} because the namespace names are too long or the directory structure is nested too deep.
If you are using Windows 10.0.14393 (Windows 10 version 1607) or later, you can enable "Long path support" by creating a REG_DWORD registry key named "LongPathsEnabled" with value 0x1 at "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" (see https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation for more information).
If you are using Windows 10.0.14393 (Windows 10 version 1607) or later, you can enable "Long path support" by creating a REG_DWORD registry key named "LongPathsEnabled" with value 0x1 at "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" (see https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation for more information).</value> If this does not solve the problem and your system supports long paths, you can try to use a nested path structure. You can change this by toggling the setting at Options &gt; Decompiler &gt; Project export &gt; Use nested directories for namespaces. This helps because even on "long-path-aware" platforms, the length of a directory name is limited, on Windows/NTFS this is 255 characters.</value>
</data> </data>
<data name="PropertyManuallyMissingReferencesListLoadedAssemblies" xml:space="preserve"> <data name="PropertyManuallyMissingReferencesListLoadedAssemblies" xml:space="preserve">
<value>for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies.</value> <value>for ex. property getter/setter access. To get optimal decompilation results, please manually add the missing references to the list of loaded assemblies.</value>

10
ILSpy/TextView/DecompilerTextView.cs

@ -1034,7 +1034,7 @@ namespace ICSharpCode.ILSpy.TextView
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.DefaultExt = language.FileExtension; dlg.DefaultExt = language.FileExtension;
dlg.Filter = language.Name + "|*" + language.FileExtension + Properties.Resources.AllFiles; 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) if (dlg.ShowDialog() == true)
{ {
SaveToDisk(new DecompilationContext(language, treeNodes.ToArray(), options), dlg.FileName); SaveToDisk(new DecompilationContext(language, treeNodes.ToArray(), options), dlg.FileName);
@ -1149,14 +1149,6 @@ namespace ICSharpCode.ILSpy.TextView
thread.Start(); thread.Start();
return tcs.Task; return tcs.Task;
} }
/// <summary>
/// Cleans up a node name for use as a file name.
/// </summary>
internal static string CleanUpName(string text)
{
return WholeProjectDecompiler.CleanUpFileName(text);
}
#endregion #endregion
internal ReferenceSegment? GetReferenceSegmentAtMousePosition() internal ReferenceSegment? GetReferenceSegmentAtMousePosition()

4
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -26,10 +26,10 @@ using System.Windows.Controls;
using System.Windows.Documents; using System.Windows.Documents;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.ViewModels; using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.TreeView; using ICSharpCode.TreeView;
@ -384,7 +384,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (string.IsNullOrEmpty(language.ProjectFileExtension)) if (string.IsNullOrEmpty(language.ProjectFileExtension))
return false; return false;
SaveFileDialog dlg = new SaveFileDialog(); 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|*.*"; dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension + "|" + language.Name + " single file|*" + language.FileExtension + "|All files|*.*";
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)
{ {

4
ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs

@ -20,8 +20,8 @@ using System;
using System.IO; using System.IO;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.TextView;
using Microsoft.Win32; using Microsoft.Win32;
@ -80,7 +80,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override bool Save(ViewModels.TabPageModel tabPage) public override bool Save(ViewModels.TabPageModel tabPage)
{ {
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key)); dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(key));
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)
{ {
using var data = OpenStream(); using var data = OpenStream();

5
ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs

@ -24,6 +24,7 @@ using System.Text;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.Properties; using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TextView;
@ -91,7 +92,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (type == FileType.Xml) if (type == FileType.Xml)
ext = ".xml"; ext = ".xml";
else 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.ShowTextView(textView => textView.ShowNode(output, this, HighlightingManager.Instance.GetDefinitionByExtension(ext)));
tabPage.SupportsLanguageSwitching = false; tabPage.SupportsLanguageSwitching = false;
return true; return true;
@ -106,7 +107,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (s == null) if (s == null)
return false; return false;
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(Resource.Name); dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(Resource.Name));
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)
{ {
s.Position = 0; s.Position = 0;

3
ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs

@ -24,6 +24,7 @@ using System.IO;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.Controls; using ICSharpCode.ILSpy.Controls;
@ -129,7 +130,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (s == null) if (s == null)
return false; return false;
SaveFileDialog dlg = new SaveFileDialog(); SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(Resource.Name); dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(Resource.Name));
dlg.Filter = Resources.ResourcesFileFilter; dlg.Filter = Resources.ResourcesFileFilter;
if (dlg.ShowDialog() == true) if (dlg.ShowDialog() == true)
{ {

Loading…
Cancel
Save