using System; using System.Collections.Generic; using System.IO; using System.Reflection; using UnityEngine; using UnityObject = UnityEngine.Object; namespace Unity.VisualScripting { public sealed class AssemblyResourceProvider : IResourceProvider { public const char DirectorySeparatorChar = '.'; public Assembly assembly { get; } public string root { get; } private readonly string prefix; public AssemblyResourceProvider(Assembly assembly, string @namespace, string root) { Ensure.That(nameof(assembly)).IsNotNull(assembly); this.assembly = assembly; this.root = root; prefix = string.Empty; var hasNamespace = !string.IsNullOrEmpty(@namespace); var hasRoot = !string.IsNullOrEmpty(root); if (hasNamespace) { prefix += @namespace; } if (hasNamespace && hasRoot) { prefix += "."; } if (hasRoot) { prefix += root; } Analyze(); } #region Filesystem public IEnumerable GetAllFiles() { return assembly.GetManifestResourceNames(); } public IEnumerable GetFiles(string path) { Ensure.That(nameof(path)).IsNotNull(path); var normalizedDirectoryPath = NormalizeDirectoryPath(path); var directory = GetDirectory(normalizedDirectoryPath, true); foreach (var file in directory.files) { yield return $"{directory.path}{DirectorySeparatorChar}{file}"; } } public IEnumerable GetDirectories(string path) { Ensure.That(nameof(path)).IsNotNull(path); var normalizedDirectoryPath = NormalizeDirectoryPath(path); var directory = GetDirectory(normalizedDirectoryPath, true); foreach (var subDirectory in directory.subDirectories) { yield return subDirectory.Value.path; } } public bool FileExists(string path) { Ensure.That(nameof(path)).IsNotNull(path); path = PreNormalizePath(path); var normalizedDirectoryPath = NormalizeDirectoryPath(Path.GetDirectoryName(path)); var normalizedFileName = NormalizeFileName(Path.GetFileName(path)); var directory = GetDirectory(normalizedDirectoryPath, false); return directory != null && directory.files.Contains(normalizedFileName); } public bool DirectoryExists(string path) { Ensure.That(nameof(path)).IsNotNull(path); path = PreNormalizePath(path); var normalizedDirectoryPath = NormalizeDirectoryPath(path); return GetDirectory(normalizedDirectoryPath, false) != null; } private string PreNormalizePath(string path) { Ensure.That(nameof(path)).IsNotNull(path); var undottedPath = ""; var directory = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(directory)) { undottedPath += directory.Replace('.', Path.DirectorySeparatorChar); undottedPath += Path.DirectorySeparatorChar; } undottedPath += Path.GetFileNameWithoutExtension(path).Replace('.', Path.DirectorySeparatorChar); undottedPath += Path.GetExtension(path); return undottedPath; } private string NormalizeDirectoryPath(string directoryPath) { Ensure.That(nameof(directoryPath)).IsNotNull(directoryPath); if (string.IsNullOrEmpty(directoryPath)) { return prefix; } return prefix + "." + directoryPath.Replace(Path.DirectorySeparatorChar, DirectorySeparatorChar) .Replace(Path.AltDirectorySeparatorChar, DirectorySeparatorChar) .Replace(' ', '_'); } private string NormalizeFileName(string fileName) { Ensure.That(nameof(fileName)).IsNotNull(fileName); return fileName; } public string DebugPath(string path) { return path; } #endregion #region Loading public T LoadAsset(string path) where T : UnityObject { throw new NotSupportedException("Assets cannot be loaded from assembly resources."); } public Texture2D LoadTexture(string path, CreateTextureOptions options) { Ensure.That(nameof(path)).IsNotNull(path); Texture2D texture2D = Resources.Load(path); return texture2D; } #endregion #region Internals private void Analyze() { foreach (var path in assembly.GetManifestResourceNames()) { var directory = rootDirectory; var parts = path.Split(DirectorySeparatorChar); for (var i = 0; i < parts.Length; i++) { var isFile = i == parts.Length - 2; var isExtension = i == parts.Length - 1; var isDirectory = !isFile && !isExtension; var part = parts[i]; if (isDirectory) { Directory subDirectory; if (!directory.subDirectories.TryGetValue(part, out subDirectory)) { subDirectory = new Directory(directory, part); directory.subDirectories.Add(part, subDirectory); } directory = subDirectory; } else if (isFile) { var extension = parts[i + 1]; directory.files.Add(part + '.' + extension); } } } } private Directory GetDirectory(string normalizedDirectoryPath, bool throwOnFail) { Ensure.That(nameof(normalizedDirectoryPath)).IsNotNull(normalizedDirectoryPath); var parts = normalizedDirectoryPath.Split(DirectorySeparatorChar); var directory = rootDirectory; foreach (var part in parts) { Directory subDirectory; if (!directory.subDirectories.TryGetValue(part, out subDirectory)) { if (throwOnFail) { throw new FileNotFoundException("Assembly resource directory not found.", DebugPath(normalizedDirectoryPath)); } return null; } directory = subDirectory; } return directory; } private readonly Directory rootDirectory = new Directory(null, null); private class Directory { public Directory parent { get; } public string name { get; } public string path { get; } public readonly Dictionary subDirectories = new Dictionary(); public readonly List files = new List(); public Directory(Directory parent, string name) { this.parent = parent; this.name = name; if (string.IsNullOrEmpty(parent?.path)) { path = name; } else { path = $"{parent.path}{DirectorySeparatorChar}{name}"; } } } #endregion } }