using System.Collections.Generic; using System.IO; using System.Linq; using UnityEditor; namespace AssetStoreTools.Utility { internal static class FileUtility { private class RenameInfo { public string OriginalName; public string CurrentName; } public static string AbsolutePathToRelativePath(string path, bool allowSymlinks) { if (!File.Exists(path) && !Directory.Exists(path)) return path; string convertedPath = path.Replace("\\", "/"); var allPackages = PackageUtility.GetAllPackages(); foreach (var package in allPackages) { if (Path.GetFullPath(package.resolvedPath) != Path.GetFullPath(convertedPath) && !Path.GetFullPath(convertedPath).StartsWith(Path.GetFullPath(package.resolvedPath) + Path.DirectorySeparatorChar)) continue; convertedPath = Path.GetFullPath(convertedPath) .Replace(Path.GetFullPath(package.resolvedPath), package.assetPath) .Replace("\\", "/"); return convertedPath; } if (convertedPath.StartsWith(Constants.RootProjectPath)) { convertedPath = convertedPath.Substring(Constants.RootProjectPath.Length); } else { if (allowSymlinks && SymlinkUtil.FindSymlinkFolderRelative(convertedPath, out var symlinkPath)) convertedPath = symlinkPath; } return convertedPath; } public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive) { // Get information about the source directory var dir = new DirectoryInfo(sourceDir); // Check if the source directory exists if (!dir.Exists) throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}"); // Cache directories before we start copying DirectoryInfo[] dirs = dir.GetDirectories(); // Create the destination directory Directory.CreateDirectory(destinationDir); // Get the files in the source directory and copy to the destination directory foreach (FileInfo file in dir.GetFiles()) { string targetFilePath = Path.Combine(destinationDir, file.Name); file.CopyTo(targetFilePath); } // If recursive and copying subdirectories, recursively call this method if (recursive) { foreach (DirectoryInfo subDir in dirs) { string newDestinationDir = Path.Combine(destinationDir, subDir.Name); CopyDirectory(subDir.FullName, newDestinationDir, true); } } } public static bool ShouldHaveMeta(string assetPath) { if (string.IsNullOrEmpty(assetPath)) return false; // Meta files never have other metas if (assetPath.EndsWith(".meta", System.StringComparison.OrdinalIgnoreCase)) return false; // File System entries ending with '~' are hidden in the context of ADB if (assetPath.EndsWith("~")) return false; // File System entries whose names start with '.' are hidden in the context of ADB var assetName = assetPath.Replace("\\", "/").Split('/').Last(); if (assetName.StartsWith(".")) return false; return true; } public static bool IsMissingMetaFiles(IEnumerable sourcePaths) { foreach (var sourcePath in sourcePaths) { if (!Directory.Exists(sourcePath)) continue; var allDirectories = Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories); foreach (var dir in allDirectories) { var dirInfo = new DirectoryInfo(dir); if (!dirInfo.Name.EndsWith("~")) continue; var nestedContent = dirInfo.GetFileSystemInfos("*", SearchOption.AllDirectories); foreach (var nested in nestedContent) { if (!ShouldHaveMeta(nested.FullName)) continue; if (!File.Exists(nested.FullName + ".meta")) return true; } } } return false; } public static void GenerateMetaFiles(IEnumerable sourcePaths) { var renameInfos = new List(); foreach (var sourcePath in sourcePaths) { if (!Directory.Exists(sourcePath)) continue; var hiddenDirectoriesInPath = Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories).Where(x => x.EndsWith("~")); foreach (var hiddenDir in hiddenDirectoriesInPath) { var hiddenDirRelative = AbsolutePathToRelativePath(hiddenDir, ASToolsPreferences.Instance.EnableSymlinkSupport); if (!hiddenDirRelative.StartsWith("Assets/") && !hiddenDirRelative.StartsWith("Packages/")) { ASDebug.LogWarning($"Path {sourcePath} is not part of the Asset Database and will be skipped"); continue; } renameInfos.Add(new RenameInfo() { CurrentName = hiddenDirRelative, OriginalName = hiddenDirRelative }); } } if (renameInfos.Count == 0) return; try { EditorApplication.LockReloadAssemblies(); // Order paths from longest to shortest to avoid having to rename them multiple times renameInfos = renameInfos.OrderByDescending(x => x.OriginalName.Length).ToList(); try { AssetDatabase.StartAssetEditing(); foreach (var renameInfo in renameInfos) { renameInfo.CurrentName = renameInfo.OriginalName.TrimEnd('~'); Directory.Move(renameInfo.OriginalName, renameInfo.CurrentName); } } finally { AssetDatabase.StopAssetEditing(); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); AssetDatabase.ReleaseCachedFileHandles(); } // Restore the original path names in reverse order renameInfos = renameInfos.OrderBy(x => x.OriginalName.Length).ToList(); try { AssetDatabase.StartAssetEditing(); foreach (var renameInfo in renameInfos) { Directory.Move(renameInfo.CurrentName, renameInfo.OriginalName); if (File.Exists($"{renameInfo.CurrentName}.meta")) File.Delete($"{renameInfo.CurrentName}.meta"); } } finally { AssetDatabase.StopAssetEditing(); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); } } finally { EditorApplication.UnlockReloadAssemblies(); } } } }