// 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;
}
}
}