From 5861edc332aedbdbf77fef13c17bd182d58435c8 Mon Sep 17 00:00:00 2001 From: Ryan Thomas Date: Thu, 9 Feb 2017 20:03:03 -0600 Subject: [PATCH 1/3] Fix KDiff3 & Nexus Mod Manager Compaitibility Issues Regarding Symbolic Links Added junction/simlink pre-processing for file paths when calling KDiff3, as KDiff3 does not follow symlinks/junctions when passed as arguments from the command line. I could not find any documentation about this particular error regarding KDiff3, so I suspect it may be an issue with Windows 10, but have no evidence to back it up. Furthermore, its my personal preference to parse such things on our end anyway. --- WitcherScriptMerger/Extensions.cs | 8 +++ WitcherScriptMerger/SimLink.cs | 61 +++++++++++++++++++ WitcherScriptMerger/Tools/KDiff3.cs | 8 ++- .../WitcherScriptMerger.csproj | 1 + 4 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 WitcherScriptMerger/SimLink.cs diff --git a/WitcherScriptMerger/Extensions.cs b/WitcherScriptMerger/Extensions.cs index 8d3585d..96ce96f 100644 --- a/WitcherScriptMerger/Extensions.cs +++ b/WitcherScriptMerger/Extensions.cs @@ -8,6 +8,8 @@ namespace WitcherScriptMerger { + using System.IO; + static class Extensions { #region Strings @@ -54,6 +56,12 @@ public static string GetPluralS(this int num) return num == 1 ? "" : "s"; } + public static string ResolveTargetFileFullName(this string fileFullName) + { + var fileInfo = new FileInfo(fileFullName); + return fileInfo.Exists ? SimLink.GetSymbolicLinkTarget(fileInfo) : fileInfo.FullName; + } + #endregion #region Tree & Context Menu diff --git a/WitcherScriptMerger/SimLink.cs b/WitcherScriptMerger/SimLink.cs new file mode 100644 index 0000000..6672f9b --- /dev/null +++ b/WitcherScriptMerger/SimLink.cs @@ -0,0 +1,61 @@ +using System; +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Win32.SafeHandles; + +namespace WitcherScriptMerger +{ + /// + /// Provides access to NTFS junction points in .Net. + /// + public static class SimLink + { + private const int CREATION_DISPOSITION_OPEN_EXISTING = 3; + + private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; + + // http://msdn.microsoft.com/en-us/library/aa364962%28VS.85%29.aspx + [DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode, + SetLastError = true)] + public static extern int GetFinalPathNameByHandle(IntPtr handle, + [In, Out] StringBuilder path, + int bufLen, + int flags); + + // http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx + [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern SafeFileHandle CreateFile(string lpFileName, + int dwDesiredAccess, + int dwShareMode, + IntPtr SecurityAttributes, + int dwCreationDisposition, + int dwFlagsAndAttributes, + IntPtr hTemplateFile); + + public static string GetSymbolicLinkTarget(FileInfo symlink) + { + var fileHandle = CreateFile(symlink.FullName, 0, 2, IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero); + if (fileHandle.IsInvalid) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + var path = new StringBuilder(512); + var size = GetFinalPathNameByHandle(fileHandle.DangerousGetHandle(), path, path.Capacity, 0); + if (size < 0) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + // The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\" + // More information about "\\?\" here -> http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx + if (path[0] == '\\' && path[1] == '\\' && path[2] == '?' && path[3] == '\\') + { + return path.ToString().Substring(4); + } + return path.ToString(); + } + } +} \ No newline at end of file diff --git a/WitcherScriptMerger/Tools/KDiff3.cs b/WitcherScriptMerger/Tools/KDiff3.cs index 35fbfcf..17d5b0c 100644 --- a/WitcherScriptMerger/Tools/KDiff3.cs +++ b/WitcherScriptMerger/Tools/KDiff3.cs @@ -32,8 +32,12 @@ public static int Run( ? "\"" + vanillaFile.FullName + "\" " : ""); + // resolve any simlinked files + var source1FullName = source1.TextFile.FullName.ResolveTargetFileFullName(); + var source2FullName = source2.TextFile.FullName.ResolveTargetFileFullName(); + args += - $"\"{source1.TextFile.FullName}\" \"{source2.TextFile.FullName}\" " + + $"\"{source1FullName}\" \"{source2FullName}\" " + $"-o \"{outputPath}\" " + "--cs \"WhiteSpace3FileMergeDefault=2\" " + "--cs \"CreateBakFiles=0\" " + @@ -51,7 +55,7 @@ public static int Run( if (!Program.Settings.Get("ReviewEachMerge") && hasVanillaVersion) { - if (source1.TextFile.FullName.EqualsIgnoreCase(outputPath) + if (source1FullName.EqualsIgnoreCase(source2FullName) && source2.Hash != null && source2.Hash.IsOutdated) { Program.MainForm.ShowMessage( diff --git a/WitcherScriptMerger/WitcherScriptMerger.csproj b/WitcherScriptMerger/WitcherScriptMerger.csproj index d124350..62d5ed1 100644 --- a/WitcherScriptMerger/WitcherScriptMerger.csproj +++ b/WitcherScriptMerger/WitcherScriptMerger.csproj @@ -69,6 +69,7 @@ OptionsForm.cs + From aab45a236140cb842ab820c1da77ff0ef811700e Mon Sep 17 00:00:00 2001 From: Ryan Thomas Date: Thu, 9 Feb 2017 20:11:50 -0600 Subject: [PATCH 2/3] Simplify implementation. --- WitcherScriptMerger/Extensions.cs | 7 ++++--- WitcherScriptMerger/Tools/KDiff3.cs | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/WitcherScriptMerger/Extensions.cs b/WitcherScriptMerger/Extensions.cs index 96ce96f..0d84725 100644 --- a/WitcherScriptMerger/Extensions.cs +++ b/WitcherScriptMerger/Extensions.cs @@ -56,12 +56,13 @@ public static string GetPluralS(this int num) return num == 1 ? "" : "s"; } - public static string ResolveTargetFileFullName(this string fileFullName) + #endregion + + #region FileInfo + public static string ResolveTargetFileFullName(this FileInfo fileInfo) { - var fileInfo = new FileInfo(fileFullName); return fileInfo.Exists ? SimLink.GetSymbolicLinkTarget(fileInfo) : fileInfo.FullName; } - #endregion #region Tree & Context Menu diff --git a/WitcherScriptMerger/Tools/KDiff3.cs b/WitcherScriptMerger/Tools/KDiff3.cs index 17d5b0c..b6e1e2e 100644 --- a/WitcherScriptMerger/Tools/KDiff3.cs +++ b/WitcherScriptMerger/Tools/KDiff3.cs @@ -33,8 +33,8 @@ public static int Run( : ""); // resolve any simlinked files - var source1FullName = source1.TextFile.FullName.ResolveTargetFileFullName(); - var source2FullName = source2.TextFile.FullName.ResolveTargetFileFullName(); + var source1FullName = source1.TextFile.ResolveTargetFileFullName(); + var source2FullName = source2.TextFile.ResolveTargetFileFullName(); args += $"\"{source1FullName}\" \"{source2FullName}\" " + From da88aec5cd0f18748fe6ed05745244e82633746e Mon Sep 17 00:00:00 2001 From: Ryan Thomas Date: Thu, 9 Feb 2017 20:13:32 -0600 Subject: [PATCH 3/3] Reverse bug introduced in first commit. whoops :) --- WitcherScriptMerger/Tools/KDiff3.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WitcherScriptMerger/Tools/KDiff3.cs b/WitcherScriptMerger/Tools/KDiff3.cs index b6e1e2e..090fe67 100644 --- a/WitcherScriptMerger/Tools/KDiff3.cs +++ b/WitcherScriptMerger/Tools/KDiff3.cs @@ -55,7 +55,7 @@ public static int Run( if (!Program.Settings.Get("ReviewEachMerge") && hasVanillaVersion) { - if (source1FullName.EqualsIgnoreCase(source2FullName) + if (source1FullName.EqualsIgnoreCase(outputPath) && source2.Hash != null && source2.Hash.IsOutdated) { Program.MainForm.ShowMessage(