Editor/Core/ProjectManifest.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace TeamCreate;
public static class ProjectManifest
{
private static readonly string[] IncludeRoots = new string[5] { "Assets", "Code", "Libraries", "ProjectSettings", "Editor" };
private static readonly string[] ExcludedDirectories = new string[7] { "obj", "bin", ".sbox", "Properties", "TestResults", ".collaboration", ".git" };
private static readonly string[] ExcludedExtensions = new string[6] { ".user", ".csproj", ".dll", ".pdb", ".cache", ".tmp" };
public static IEnumerable<string> EnumerateInputs(string projectRoot)
{
bool incomplete;
return EnumerateInputs(projectRoot, null, out incomplete);
}
private static IEnumerable<string> EnumerateInputs(string projectRoot, Func<string, string[]> listDirectories, out bool incomplete)
{
List<string> list = new List<string>();
incomplete = false;
if (string.IsNullOrWhiteSpace(projectRoot) || !Directory.Exists(projectRoot))
{
return list;
}
list.AddRange(Walk(projectRoot, listDirectories, out incomplete));
list.Sort(StringComparer.Ordinal);
return list;
}
public static string Compute(string projectRoot)
{
bool incomplete;
return Compute(projectRoot, null, out incomplete);
}
internal static string Compute(string projectRoot, Func<string, string[]> listDirectories, out bool incomplete)
{
if (string.IsNullOrWhiteSpace(projectRoot) || !Directory.Exists(projectRoot))
{
incomplete = true;
return "";
}
IEnumerable<string> enumerable = EnumerateInputs(projectRoot, listDirectories, out incomplete);
if (incomplete)
{
return "";
}
string fullPath = Path.GetFullPath(projectRoot);
List<string> list = new List<string>();
foreach (string item in enumerable)
{
byte[] array = ReadBytesOrNull(Path.Combine(fullPath, item.Replace('/', Path.DirectorySeparatorChar)));
if (array == null)
{
incomplete = true;
return "";
}
list.Add(item + "\n" + Sha256Hex(MetaFileRules.Canonical(item, array)));
}
return HashLines(list);
}
public static string HashLines(IEnumerable<string> lines)
{
StringBuilder stringBuilder = new StringBuilder();
if (lines != null)
{
foreach (string line in lines)
{
stringBuilder.Append(line).Append('\n');
}
}
return TruncateHex(SHA256.HashData(Encoding.UTF8.GetBytes(stringBuilder.ToString())));
}
private static List<string> Walk(string projectRoot, Func<string, string[]> listDirectories, out bool incomplete)
{
List<string> list = new List<string>();
string fullPath = Path.GetFullPath(projectRoot);
incomplete = false;
HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
hashSet.Add(PhysicalPath(fullPath));
Stack<(string, string)> stack = new Stack<(string, string)>();
stack.Push((fullPath, ""));
while (stack.Count > 0)
{
var (text, text2) = stack.Pop();
string[] files;
string[] array;
try
{
files = Directory.GetFiles(text);
array = ((listDirectories == null) ? Directory.GetDirectories(text) : listDirectories(text));
}
catch (Exception ex) when ((ex is IOException || ex is UnauthorizedAccessException) ? true : false)
{
incomplete = true;
continue;
}
string[] array2 = files;
foreach (string path in array2)
{
string fileName = Path.GetFileName(path);
if (!IsExcludedFile(fileName) && (text2.Length != 0 || fileName.EndsWith(".sbproj", StringComparison.OrdinalIgnoreCase)))
{
list.Add((text2.Length == 0) ? fileName : (text2 + "/" + fileName));
}
}
List<(string, string)> list2 = new List<(string, string)>();
string[] array3 = array;
foreach (string text3 in array3)
{
string fileName2 = Path.GetFileName(text3);
if (!IsExcludedDirectory(fileName2) && (text2.Length != 0 || IncludeRoots.Contains(fileName2, StringComparer.OrdinalIgnoreCase)))
{
string item = PhysicalPath(text3);
if (hashSet.Add(item))
{
list2.Add((text3, (text2.Length == 0) ? fileName2 : (text2 + "/" + fileName2)));
}
}
}
foreach (var item2 in list2.OrderBy(((string Full, string Relative) x) => x.Relative, StringComparer.Ordinal))
{
stack.Push(item2);
}
}
return list;
}
private static bool IsExcludedFile(string name)
{
string[] excludedExtensions = ExcludedExtensions;
foreach (string value in excludedExtensions)
{
if (name.EndsWith(value, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private static bool IsExcludedDirectory(string name)
{
string[] excludedDirectories = ExcludedDirectories;
foreach (string b in excludedDirectories)
{
if (string.Equals(name, b, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private static string PhysicalPath(string directory)
{
try
{
if ((File.GetAttributes(directory) & FileAttributes.ReparsePoint) != 0)
{
FileSystemInfo fileSystemInfo = Directory.ResolveLinkTarget(directory, returnFinalTarget: true);
if (fileSystemInfo != null)
{
return Path.GetFullPath(fileSystemInfo.FullName);
}
}
}
catch (Exception ex) when ((ex is IOException || ex is UnauthorizedAccessException) ? true : false)
{
}
return Path.GetFullPath(directory);
}
private static byte[] ReadBytesOrNull(string path)
{
try
{
return File.ReadAllBytes(path);
}
catch (Exception ex) when ((ex is IOException || ex is UnauthorizedAccessException) ? true : false)
{
return null;
}
}
private static string Sha256Hex(byte[] bytes)
{
return Convert.ToHexString(SHA256.HashData(bytes)).ToLowerInvariant();
}
private static string TruncateHex(byte[] hash)
{
return Convert.ToHexString(hash).ToLowerInvariant().Substring(0, 8);
}
}