/// <summary> /// Function to locate the associated texture codec and file for a sprite. /// </summary> /// <param name="fileSystem">The file system to evaluate.</param> /// <param name="localDir">The local directory for the sprite file.</param> /// <param name="renderer">The renderer used for resource look up.</param> /// <param name="textureName">The name of the texture.</param> /// <param name="codecs">The list of available image codecs to use when determining texture type.</param> /// <returns>A tuple containing the codec, the texture file, and a flag to indicate that the texture was previously loaded into memory.</returns> private static (IGorgonImageCodec codec, IGorgonVirtualFile file, bool alreadyLoaded) LocateTextureCodecAndFile( IGorgonFileSystem fileSystem, IGorgonVirtualDirectory localDir, Gorgon2D renderer, string textureName, IEnumerable <IGorgonImageCodec> codecs) { // First, attempt to locate the resource by its name. If it's already loaded, we should not load it again. GorgonTexture2D texture = renderer.Graphics .LocateResourcesByName <GorgonTexture2D>(textureName) .FirstOrDefault(); if (texture != null) { return(null, null, true); } IGorgonImageCodec codec; // We couldn't find the texture in our loaded resources, so try to locate it on the file system. // First, check the local directory. IEnumerable <IGorgonVirtualFile> files = fileSystem.FindFiles(localDir.FullPath, $"{textureName}.*", false); foreach (IGorgonVirtualFile file in files) { codec = FindTextureCodec(file, codecs); if (codec != null) { return(codec, file, false); } } // Check to see if the name has path information for the texture in the name. // The GorgonEditor from v2 does this. if (!textureName.Contains("/")) { // It is not. We cannot load the texture. return(null, null, false); } IGorgonVirtualFile textureFile = fileSystem.GetFile(textureName); if (textureFile == null) { return(null, null, false); } // Try to find a codec for the image file. codec = FindTextureCodec(textureFile, codecs); return(codec == null ? (null, null, false) : (codec, textureFile, false)); }
/// <summary> /// Function to copy the contents of a file system to the writable area. /// </summary> /// <param name="sourceFileSystem">The <see cref="IGorgonFileSystem"/> to copy.</param> /// <param name="copyProgress">A method callback used to track the progress of the copy operation.</param> /// <param name="allowOverwrite">[Optional] <b>true</b> to allow overwriting of files that already exist in the file system with the same path, <b>false</b> to throw an exception when a file with the same path is encountered.</param> /// <returns>A <see cref="ValueTuple{T1,T2}"/> containing the number of directories (<c>item1</c>) and the number of files (<c>item2</c>) copied, or <b>null</b> if the operation was cancelled.</returns> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="sourceFileSystem"/> parameter is <b>null</b>.</exception> /// <exception cref="IOException">Thrown when the a file exists in <see cref="IGorgonFileSystemWriter{T}.FileSystem"/>, and the <paramref name="allowOverwrite"/> parameter is set to <b>false</b>.</exception> /// <remarks> /// <para> /// This copies all the file and directory information from one file system, into the <see cref="IGorgonFileSystemWriter{T}.FileSystem"/> linked to this writer. /// </para> /// <para> /// When the <paramref name="allowOverwrite"/> is set to <b>false</b>, and a <see cref="IGorgonVirtualFile"/> already exists with the same path as another <see cref="IGorgonVirtualFile"/> in the /// <paramref name="sourceFileSystem"/>, then an exception will be raised. /// </para> /// </remarks> public (int DirectoryCount, int FileCount)? CopyFrom(IGorgonFileSystem sourceFileSystem, Func <GorgonWriterCopyProgress, bool> copyProgress = null, bool allowOverwrite = true) { if (sourceFileSystem == null) { throw new ArgumentNullException(nameof(sourceFileSystem)); } IGorgonVirtualFile[] files = sourceFileSystem.FindFiles("/", "*").ToArray(); IGorgonVirtualDirectory[] directories = sourceFileSystem.FindDirectories("/", "*").ToArray(); return((files.Length == 0) && (directories.Length == 0) ? ((int DirectoryCount, int FileCount)?)(0, 0) : CopyInternal(copyProgress, allowOverwrite, files, directories, CancellationToken.None)); }
/// <summary> /// Function to asynchronously copy the contents of a file system to the writable area. /// </summary> /// <param name="sourceFileSystem">The <see cref="IGorgonFileSystem"/> to copy.</param> /// <param name="cancelToken">The <see cref="CancellationToken"/> used to cancel an in progress copy.</param> /// <param name="copyProgress">A method callback used to track the progress of the copy operation.</param> /// <param name="allowOverwrite">[Optional] <b>true</b> to allow overwriting of files that already exist in the file system with the same path, <b>false</b> to throw an exception when a file with the same path is encountered.</param> /// <returns>A <see cref="ValueTuple{T1,T2}"/> containing the number of directories (<c>item1</c>) and the number of files (<c>item2</c>) copied, or <b>null</b> if the operation was cancelled.</returns> /// <remarks> /// <para> /// This copies all the file and directory information from one file system, into the <see cref="IGorgonFileSystemWriter{T}.FileSystem"/> linked to this writer. /// </para> /// <para> /// When the <paramref name="allowOverwrite"/> is set to <b>false</b>, and a <see cref="IGorgonVirtualFile"/> already exists with the same path as another <see cref="IGorgonVirtualFile"/> in the /// <paramref name="sourceFileSystem"/>, then an exception will be raised. /// </para> /// <para> /// This version of the copy method allows for an asynchronous copy of a set of a files and directories from another <see cref="IGorgonFileSystem"/>. This method should be used when there is a large /// amount of data to transfer between the file systems. /// </para> /// <para> /// Unlike the <see cref="IGorgonFileSystemWriter{T}.CopyFrom"/> method, this method will report the progress of the copy through the <paramref name="copyProgress"/> callback. This callback is a method that takes a /// <see cref="GorgonWriterCopyProgress"/> value as a parameter that will report the current state, and will return a <see cref="bool"/> to indicate whether to continue the copy or not (<b>true</b> to /// continue, <b>false</b> to stop). /// </para> /// <para> /// <note type="warning"> /// <para> /// The <paramref name="copyProgress"/> method does not switch back to the UI context. Ensure that you invoke any operations that update a UI on the appropriate thread (e.g <c>BeginInvoke</c> on a /// WinForms UI element or <c>Dispatcher</c> on a WPF element). /// </para> /// </note> /// </para> /// <para> /// This method also allows for cancellation of the copy operation by passing a <see cref="CancellationToken"/> to the <paramref name="cancelToken"/> parameter. /// </para> /// </remarks> public async Task <(int DirectoryCount, int FileCount)?> CopyFromAsync(IGorgonFileSystem sourceFileSystem, CancellationToken cancelToken, Func <GorgonWriterCopyProgress, bool> copyProgress = null, bool allowOverwrite = true) { if (sourceFileSystem == null) { throw new ArgumentNullException(nameof(sourceFileSystem)); } // ReSharper disable MethodSupportsCancellation (IGorgonVirtualFile[] Files, IGorgonVirtualDirectory[] Directories) = await Task.Run(() => { IGorgonVirtualFile[] files = sourceFileSystem.FindFiles("/", "*").ToArray(); IGorgonVirtualDirectory[] directories = sourceFileSystem.FindDirectories("/", "*").ToArray(); return(Files : files, Directories : directories); }).ConfigureAwait(false); return((Files.Length == 0) && (Directories.Length == 0) ? ((int DirectoryCount, int FileCount)?)(0, 0) : await Task.Run(() => CopyInternal(copyProgress, allowOverwrite, Files, Directories, cancelToken), cancelToken).ConfigureAwait(false)); // ReSharper restore MethodSupportsCancellation }
/// <summary> /// Function to locate specific types of editor content files contained within the file system. /// </summary> /// <param name="fileSystem">The file system containing the content items.</param> /// <param name="path">Path to the directory containing the files to evaluate.</param> /// <param name="contentType">The type of content to locate.</param> /// <param name="searchMask">[Optional] A mask for filtering the search by file name.</param> /// <param name="recursive">[Optional] <b>true</b> to recursively search, <b>false</b> to only search the specified path.</param> /// <returns>A list of <see cref="IGorgonVirtualFile"/> items for each content item.</returns> /// <remarks> /// <para> /// Applications can use this to locate specific types of content files within a file system. If <b>null</b> is passed to the <paramref name="contentType"/>, then all content files with /// no content type associated will be returned. /// </para> /// </remarks> public static IReadOnlyList <IGorgonVirtualFile> GetContentItems(this IGorgonFileSystem fileSystem, string path, string contentType, string searchMask = "*", bool recursive = false) { var result = new List <IGorgonVirtualFile>(); if (fileSystem == null) { throw new ArgumentNullException(nameof(fileSystem)); } if (path == null) { throw new ArgumentNullException(nameof(path)); } if (string.IsNullOrWhiteSpace(path)) { throw new ArgumentEmptyException(nameof(path)); } IProjectMetadata metaData = fileSystem.GetMetadata(); if (metaData == null) { return(result); } // We passed in a file name, extract it for a seach pattern. if (string.IsNullOrWhiteSpace(searchMask)) { searchMask = "*"; } path = path.FormatDirectory('/'); IGorgonVirtualDirectory directory = fileSystem.GetDirectory(path); if (directory == null) { throw new DirectoryNotFoundException(); } IEnumerable <IGorgonVirtualFile> files = fileSystem.FindFiles(directory.FullPath, searchMask, recursive); // Handle the unassociated files. if (string.IsNullOrWhiteSpace(contentType)) { foreach (IGorgonVirtualFile file in files) { if (!metaData.ProjectItems.TryGetValue(file.FullPath, out ProjectItemMetadata metaDataItem)) { continue; } IReadOnlyDictionary <string, string> attributes = metaDataItem.Attributes; if ((attributes != null) && (attributes.Count > 0) && (attributes.TryGetValue(CommonEditorConstants.ContentTypeAttr, out string type))) { continue; } result.Add(file); } return(result); } // Filter the list based on the content type we ask for. foreach (IGorgonVirtualFile file in files) { if (!metaData.ProjectItems.TryGetValue(file.FullPath, out ProjectItemMetadata metaDataItem)) { continue; } IReadOnlyDictionary <string, string> attributes = metaDataItem.Attributes; if ((attributes == null) || (attributes.Count == 0) || (!attributes.TryGetValue(CommonEditorConstants.ContentTypeAttr, out string type)) || (!string.Equals(contentType, type, StringComparison.OrdinalIgnoreCase))) { continue; } result.Add(file); } return(result); }
/// <summary> /// Function to copy data from a file system to the file system linked to this writer. /// </summary> /// <param name="sourceFileSystem">The file system to copy from.</param> /// <param name="progress">The callback for copy progress.</param> /// <param name="allowOverwrite">Flag to indicate whether to allow overwriting files or not.</param> /// <param name="token">The cancellation token for asynchronous copy.</param> /// <returns>A tuple containing the count of the directories and files copied.</returns> private (int DirectoryCount, int FileCount)? CopyInternal(IGorgonFileSystem sourceFileSystem, Func <GorgonWriterCopyProgress, bool> progress, bool allowOverwrite, CancellationToken token) { int directoryCount = 0; int fileCount = 0; // Enumerate files and directories from the source. IGorgonVirtualFile[] files = sourceFileSystem.FindFiles("/", "*").ToArray(); IGorgonVirtualDirectory[] directories = sourceFileSystem.FindDirectories("/", "*").ToArray(); if ((files.Length == 0) && (directories.Length == 0)) { return(0, 0); } // Create all the directories. foreach (IGorgonVirtualDirectory directory in directories) { if (token.IsCancellationRequested) { return(null); } CreateDirectory(directory.FullPath); ++directoryCount; } foreach (IGorgonVirtualFile file in files) { IGorgonVirtualFile destFile = FileSystem.GetFile(file.FullPath); if (token.IsCancellationRequested) { return(null); } // Do not allow overwrite if the user requested it. if ((destFile != null) && (!allowOverwrite)) { throw new IOException(string.Format(Resources.GORFS_ERR_FILE_EXISTS, destFile.FullPath)); } // Copy the file. using (Stream sourceStream = file.OpenStream()) { using (Stream destStream = OpenStream(file.FullPath, FileMode.Create)) { sourceStream.CopyTo(destStream, 80000); } } ++fileCount; if (progress == null) { continue; } if (!progress(new GorgonWriterCopyProgress(file, fileCount, files.Length, directories.Length))) { return(null); } } return(directoryCount, fileCount); }