/// <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
        }
Exemple #4
0
        /// <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);
        }