// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.IO; namespace Gauntlet { public class PathUtils { /// /// Converts a path to the long path format if it's not already. /// /// The path to convert. /// The converted path. public static string MakeLongPath(string InPath) { string LongPathPrefix = @"\\?\"; if (!InPath.StartsWith(LongPathPrefix)) { InPath = LongPathPrefix + InPath; } return InPath; } /// /// Similar to Path.Combine but creates a long path instead. /// /// The parts of the path to combine. /// The full long path. public static string CombineLongPath(params string[] Paths) { return MakeLongPath(Path.Combine(Paths)); } /// /// Find a path that includes specific directories in the given order, but could also include unknown intermediate directories. /// /// The absolute path to the root directory where we will begin our search /// Directories names that will be searched for and combined into a path /// If successful, returns the absolute found relevant path. Returns string.Empty if an issue occurs or the path cannot be found. public static string FindRelevantPath(string BasePath, params string[] PathNodes) { string[] TotalPathNodes = new string[PathNodes.Length + 1]; TotalPathNodes[0] = BasePath; for (int i = 0; i < PathNodes.Length; i++) { TotalPathNodes[i + 1] = PathNodes[i]; } return FindRelevantPath(TotalPathNodes); } /// /// Find a path that includes specific directories in the given order, but could also include unknown intermediate directories. /// /// Directories names that will be searched for and combined into a path /// If successful, returns the absolute found relevant path. Returns string.Empty if an issue occurs or the path cannot be found. public static string FindRelevantPath(params string[] PathNodes) { if (PathNodes.Length < 2) { Gauntlet.Log.Warning("This function requires two or more directory names (path nodes) to search for a path."); return string.Empty; } if (!Directory.Exists(PathNodes[0])) { Gauntlet.Log.Warning("The first parameter in this function must be an existing directory path."); return string.Empty; } List FoundPathNodes = new List() { PathNodes[0] }; string FoundPath = PathNodes[0]; for (int NodeIndex = 1; NodeIndex < PathNodes.Length; NodeIndex++) { // Break at the final path node if it is a file name if (NodeIndex == PathNodes.Length - 1 && Path.HasExtension(PathNodes[NodeIndex])) { FoundPathNodes.Add(PathNodes[NodeIndex]); FoundPath = Path.Combine(FoundPathNodes.ToArray()); break; } string FoundSubdirectory = FindRelevantSubdirectory(FoundPath, PathNodes[NodeIndex]); if (string.IsNullOrEmpty(FoundSubdirectory)) { Gauntlet.Log.Info("Could not find complete relevant path. One or more path nodes could not be found among: " + string.Join(" \\ ", PathNodes)); Gauntlet.Log.Info("Partially found relevant path: " + FoundPath); return string.Empty; } FoundSubdirectory = Path.GetRelativePath(FoundPath, FoundSubdirectory); FoundPathNodes.Add(FoundSubdirectory); FoundPath = Path.Combine(FoundPathNodes.ToArray()); } return FoundPath; } private static string FindRelevantSubdirectory(string ParentDirectoryPath, string SubdirectoryName, bool bSearchWideFirst = true) { try { string RelevantSubdirectory = MatchChildDirectory(ParentDirectoryPath, SubdirectoryName); if (string.IsNullOrEmpty(RelevantSubdirectory)) { string[] Subdirectories = Directory.GetDirectories(ParentDirectoryPath); // Most of the time, our target directory is only one extra level deep, so search that before deeply recursing if (bSearchWideFirst) { Gauntlet.Log.Info("Did not find child directory named \"{0}\" within {1}. Searching next level down.", SubdirectoryName, ParentDirectoryPath); foreach (string Subdirectory in Subdirectories) { if (string.IsNullOrEmpty(RelevantSubdirectory)) { RelevantSubdirectory = MatchChildDirectory(Subdirectory, SubdirectoryName); } else { string AdditionalRelevantSubdirectory = MatchChildDirectory(Subdirectory, SubdirectoryName); if (!string.IsNullOrEmpty(AdditionalRelevantSubdirectory)) { Gauntlet.Log.Info("Found additional match for \"{0}\" within {1}! Ignoring this match ({2}) in favor of {3}", SubdirectoryName, ParentDirectoryPath, AdditionalRelevantSubdirectory, RelevantSubdirectory); } } } if (!string.IsNullOrEmpty(RelevantSubdirectory)) { return RelevantSubdirectory; } Gauntlet.Log.Info("Did not find child directory named \"{0}\" one level below {1}. Searching recursively through all subdirectories.", SubdirectoryName, ParentDirectoryPath); } foreach (string Subdirectory in Subdirectories) { string FoundDirectory = FindRelevantSubdirectory(Subdirectory, SubdirectoryName, false); if (!string.IsNullOrEmpty(FoundDirectory)) { // Return the first match rather than searching the entire parent directory before analyzing for the best match, which could take a long time return FoundDirectory; } } } return RelevantSubdirectory; } catch (Exception Ex) { Gauntlet.Log.Error(Ex.ToString()); return string.Empty; } } private static string MatchChildDirectory(string ParentDirectoryPath, string ChildDirectoryName) { try { string[] Subdirectories = Directory.GetDirectories(ParentDirectoryPath); string[] MatchingDirectories = Subdirectories.Where(directory => new DirectoryInfo(directory).Name.Equals(ChildDirectoryName, StringComparison.InvariantCultureIgnoreCase)).ToArray(); if (MatchingDirectories.Any()) { if (MatchingDirectories.Count() > 1) { // If case-insensitive matched more than one result, match case-sensitive MatchingDirectories = MatchingDirectories.Where(directory => new DirectoryInfo(directory).Name.Equals(ChildDirectoryName, StringComparison.InvariantCulture)).ToArray(); if (MatchingDirectories.Count() != 1) { Gauntlet.Log.Warning("Target directory name \"{0}\" is ambiguously capitalized for a match within directory {1}", ChildDirectoryName, ParentDirectoryPath); return string.Empty; } } return MatchingDirectories.First(); } } catch (Exception Ex) { Gauntlet.Log.Error(Ex.ToString()); return string.Empty; } return string.Empty; } } }