/// <summary>
        /// Function to compress an image using block compression.
        /// </summary>
        /// <param name="imageFile">The file to compress.</param>
        /// <param name="format">The block compression format to use.</param>
        /// <param name="mipCount">The number of mip levels.</param>
        /// <returns>The virtual file with the compressed data.</returns>
        public IGorgonVirtualFile Compress(IGorgonVirtualFile imageFile, BufferFormat format, int mipCount)
        {
            string             directory      = Path.GetDirectoryName(imageFile.PhysicalFile.FullPath);
            Process            texConvProcess = null;
            IGorgonVirtualFile encodedFile    = null;

            var info = new ProcessStartInfo
            {
                Arguments        = $"-f {format.ToString().ToUpper()} -y -m {mipCount} -fl 12.0 -ft DDS -o \"{directory}\" -nologo \"{imageFile.PhysicalFile.FullPath}\"",
                ErrorDialog      = true,
                FileName         = _texConv.FullName,
                WorkingDirectory = directory,
                UseShellExecute  = false,
#if DEBUG
                CreateNoWindow = false,
#else
                CreateNoWindow = true,
#endif
                RedirectStandardError  = true,
                RedirectStandardOutput = true
            };

            try
            {
                texConvProcess = Process.Start(info);

                if (texConvProcess == null)
                {
                    return(null);
                }

                texConvProcess.WaitForExit();

                string errorData = texConvProcess.StandardError.ReadToEnd();

                if (errorData.StartsWith("Invalid value", StringComparison.OrdinalIgnoreCase))
                {
                    errorData = errorData.Substring(0, errorData.IndexOf("\n", StringComparison.OrdinalIgnoreCase));
                    throw new GorgonException(GorgonResult.CannotWrite, Resources.GORIMG_ERR_CANNOT_COMPRESS, new IOException(errorData));
                }

                errorData = texConvProcess.StandardOutput.ReadToEnd();

                if (errorData.Contains("FAILED"))
                {
                    throw new GorgonException(GorgonResult.CannotWrite, Resources.GORIMG_ERR_CANNOT_COMPRESS, new IOException(errorData));
                }

                _writer.FileSystem.Refresh();

                encodedFile = _writer.FileSystem.GetFile(imageFile.FullPath);
                return(encodedFile);
            }
            finally
            {
                texConvProcess?.Dispose();
            }
        }
Exemple #2
0
        /// <summary>Function to edit the image data in an external application.</summary>
        /// <param name="workingFile">The file containing the image data to edit.</param>
        /// <returns>
        ///   <b>true</b> if the image data was changed, <b>false</b> if not.</returns>
        /// <exception cref="IOException">Thrown if no associated executable was found for the file type.</exception>
        public bool EditImage(IGorgonVirtualFile workingFile)
        {
            string exePath = GetExecutable(workingFile);

            if ((string.IsNullOrWhiteSpace(exePath)) || (!File.Exists(exePath)))
            {
                throw new IOException(string.Format(Resources.GORIMG_ERR_NO_EXTERNAL_EDITOR, workingFile.Name));
            }

            GorgonApplication.MainForm.Visible = false;

            var startInfo = new ProcessStartInfo(exePath)
            {
                ErrorDialog             = true,
                ErrorDialogParentHandle = GorgonApplication.MainForm.Handle,
                RedirectStandardError   = true,
                RedirectStandardOutput  = true,
                WindowStyle             = ProcessWindowStyle.Maximized,
                UseShellExecute         = false,
                WorkingDirectory        = Path.GetDirectoryName(workingFile.PhysicalFile.FullPath).FormatDirectory(Path.DirectorySeparatorChar),
                Arguments = $"\"{workingFile.PhysicalFile.FullPath}\""
            };

            Process externalProcess = null;

            var values = new WatchValues()
            {
                FilePath   = workingFile.PhysicalFile.FullPath,
                HasChanges = false
            };

            FileSystemWatcher watcher = CreateWatcher(values);

            try
            {
                _log.Print($"Launching {startInfo.FileName} to edit {workingFile.FullPath}", LoggingLevel.Verbose);

                externalProcess = Process.Start(startInfo);
                externalProcess.WaitForExit();

                if (!values.HasChanges)
                {
                    _log.Print($"{startInfo.FileName} closed. No changes detected on {workingFile.FullPath}.", LoggingLevel.Verbose);
                    return(false);
                }

                _log.Print($"{startInfo.FileName} closed. Changes detected on {workingFile.FullPath}.", LoggingLevel.Verbose);
            }
            finally
            {
                watcher?.Dispose();
                externalProcess?.Dispose();
                GorgonApplication.MainForm.Visible = true;
            }

            return(true);
        }
        /// <summary>
        /// Function to open a stream to a file on the physical file system from the <see cref="IGorgonVirtualFile"/> passed in.
        /// </summary>
        /// <param name="file">The <see cref="IGorgonVirtualFile"/> that will be used to locate the file that will be opened on the physical file system.</param>
        /// <returns>A <see cref="Stream"/> to the file, or <b>null</b> if the file does not exist.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="file"/> parameter is <b>null</b>.</exception>
        /// <remarks>
        /// <para>
        /// This will take the <see cref="IGorgonVirtualFile"/> and open its corresponding physical file location as a stream for reading. The stream that is returned will be opened, and as such, it is the
        /// responsibility of the user to close the stream when finished.
        /// </para>
        /// </remarks>
        public Stream OpenFileStream(IGorgonVirtualFile file)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }

            return(OnOpenFileStream(file));
        }
Exemple #4
0
        /// <summary>
        /// Function to determine if this directory, or optionally, any of the sub directories contains the specified file.
        /// </summary>
        /// <param name="file">The <see cref="IGorgonVirtualFile"/> to search for.</param>
        /// <returns><b>true</b> if found, <b>false</b> if not.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="file"/> parameter is <b>null</b>.</exception>
        /// <remarks>
        /// Use this to determine if a <see cref="IGorgonVirtualFile"/> exists under this directory or any of its sub directories. This search includes all sub directories for this and child directories.
        /// To determine if a file exists in the immediate directory, use the <see cref="IGorgonNamedObjectReadOnlyDictionary{T}.Contains"/> method.
        /// </remarks>
        public bool ContainsFile(IGorgonVirtualFile file)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }

            return(ContainsFile(file.Name));
        }
        /// <summary>
        /// Function to decompress a block compressed file into a standard pixel format.
        /// </summary>
        /// <param name="imageFile">The file to decompress</param>
        /// <param name="metadata">The metadata for the file.</param>
        /// <returns>The decompressed image.</returns>
        public IGorgonImage Decompress(ref IGorgonVirtualFile imageFile, IGorgonImageInfo metadata)
        {
            string             directory      = Path.GetDirectoryName(imageFile.PhysicalFile.FullPath);
            IGorgonImage       result         = null;
            Process            texConvProcess = null;
            Stream             inStream       = null;
            IGorgonVirtualFile decodedFile    = null;

            var info = new ProcessStartInfo
            {
                Arguments        = $"-f {_d3dFormats[metadata.Format]} -y -m {metadata.MipCount} -fl 12.0 -ft DDS -o \"{directory}\" -nologo -px decoded {imageFile.PhysicalFile.FullPath}\"",
                ErrorDialog      = true,
                FileName         = _texConv.FullName,
                WorkingDirectory = directory,
                UseShellExecute  = false,
#if DEBUG
                CreateNoWindow = false,
#else
                CreateNoWindow = true,
#endif
                RedirectStandardError  = true,
                RedirectStandardOutput = true
            };

            try
            {
                texConvProcess = Process.Start(info);

                if (texConvProcess == null)
                {
                    return(null);
                }

                texConvProcess.WaitForExit();

                _writer.FileSystem.Refresh();
                imageFile = _writer.FileSystem.GetFile(imageFile.FullPath);

                decodedFile = _writer.FileSystem.GetFile(imageFile.Directory.FullPath + "decoded" + imageFile.Name);
                inStream    = decodedFile.OpenStream();
                result      = _codec.LoadFromStream(inStream);

                return(result);
            }
            finally
            {
                texConvProcess?.Dispose();
                inStream?.Dispose();

                if (decodedFile != null)
                {
                    _writer.DeleteFile(decodedFile.FullPath);
                }
            }
        }
        /// <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));
        }
Exemple #7
0
        /// <summary>
        /// Function to load the associated sprite texture based on the project metadata.
        /// </summary>
        /// <param name="graphics">The graphics interface to use when looking up or creating the texture.</param>
        /// <param name="fileSystem">The file system containing the file to load.</param>
        /// <param name="metadata">The metadata for the project.</param>
        /// <param name="imagePath">The path to the image.</param>
        /// <param name="usage">The intended usage for the texture.</param>
        /// <param name="codecs">The list of available codecs.</param>
        /// <returns>A new texture containing the sprite texture data.</returns>
        private static GorgonTexture2D GetTexture(GorgonGraphics graphics, IGorgonFileSystem fileSystem, IProjectMetadata metadata, string imagePath, ResourceUsage usage, IReadOnlyDictionary <string, IGorgonImageCodec> codecs)
        {
            bool shouldConvertToPremultiplied = false;

            // First, check to see if this texture isn't already loaded into memory.
            GorgonTexture2D texture = graphics.LocateResourcesByName <GorgonTexture2D>(imagePath).FirstOrDefault();

            if (texture != null)
            {
                return(texture);
            }

            if (!metadata.ProjectItems.TryGetValue(imagePath, out ProjectItemMetadata textureMetadata))
            {
                return(null);
            }

            if (textureMetadata.Attributes.TryGetValue("PremultipliedAlpha", out string isPremultiplied))
            {
#pragma warning disable CA1806 // Do not ignore method results
                bool.TryParse(isPremultiplied, out shouldConvertToPremultiplied);
#pragma warning restore CA1806 // Do not ignore method results
            }

            if ((!textureMetadata.Attributes.TryGetValue(CommonEditorConstants.ContentTypeAttr, out string contentType)) ||
                (!string.Equals(contentType, CommonEditorContentTypes.ImageType, StringComparison.OrdinalIgnoreCase)) ||
                (!textureMetadata.Attributes.TryGetValue("ImageCodec", out string imageCodecTypeName)) ||
                (!codecs.TryGetValue(imageCodecTypeName, out IGorgonImageCodec codec)))
            {
                return(null);
            }

            IGorgonVirtualFile file = fileSystem.GetFile(imagePath);

            if (file == null)
            {
                return(null);
            }

            using (Stream fileStream = file.OpenStream())
            {
                texture = GorgonTexture2D.FromStream(graphics, fileStream, codec, file.Size, new GorgonTexture2DLoadOptions
                {
                    ConvertToPremultipliedAlpha = shouldConvertToPremultiplied,
                    IsTextureCube = false,
                    Name          = file.FullPath,
                    Binding       = TextureBinding.ShaderResource,
                    Usage         = (((usage & ResourceUsage.None) == ResourceUsage.None) || ((usage & ResourceUsage.Staging) == ResourceUsage.Staging)) ? ResourceUsage.Default : usage
                });
            }

            return(texture);
        }
Exemple #8
0
        /// <summary>
        /// Function to locate the application that can edit the specified file.
        /// </summary>
        /// <param name="workingFile">The file to edit.</param>
        /// <returns>The path to the executable.</returns>
        private string GetExecutable(IGorgonVirtualFile workingFile)
        {
            _log.Print($"Retrieving associated executable for files of type {workingFile.Extension}.", LoggingLevel.Verbose);

            string exePath = Win32API.GetAssociatedExecutable(workingFile.PhysicalFile.FullPath);

            if (string.IsNullOrWhiteSpace(exePath))
            {
                _log.Print($"No executable found for files of type {workingFile.Extension}.", LoggingLevel.Verbose);
                return(null);
            }

            _log.Print($"Found executable path {exePath}.", LoggingLevel.Verbose);

            return(exePath);
        }
Exemple #9
0
        /// <summary>
        /// Function to locate the texture file associated with the sprite being imported.
        /// </summary>
        /// <returns>The texture being imported.</returns>
        private GorgonTexture2DView GetTexture()
        {
            // We need to copy the sprite data into a memory stream since the underlying stream may not be seekable (BZip2 lies and says it is seekable, but it really isn't).
            using (Stream fileStream = SourceFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                string textureName = _codec.GetAssociatedTextureName(fileStream);
                GorgonTexture2DView textureForSprite = null;

                // Let's try and load the texture into memory.
                // ReSharper disable once InvertIf
                if (!string.IsNullOrWhiteSpace(textureName))
                {
                    IGorgonVirtualFile textureFile = LocateTextureFile(textureName);

                    // We couldn't load the file, so, let's try again without a file extension since we strip those.
                    int extensionDot = textureName.LastIndexOf('.');
                    if ((textureFile == null) && (extensionDot > 1))
                    {
                        textureName = textureName.Substring(0, extensionDot);
                        textureFile = LocateTextureFile(textureName);
                    }

                    // We have not loaded the texture yet.  Do so now.
                    // ReSharper disable once InvertIf
                    if (textureFile != null)
                    {
                        using (Stream textureStream = textureFile.OpenStream())
                        {
                            textureForSprite = GorgonTexture2DView.FromStream(_renderer.Graphics,
                                                                              textureStream,
                                                                              _ddsCodec,
                                                                              textureFile.Size,
                                                                              new GorgonTexture2DLoadOptions
                            {
                                Name    = textureFile.FullPath,
                                Usage   = ResourceUsage.Default,
                                Binding = TextureBinding.ShaderResource
                            });
                        }
                    }
                }

                return(textureForSprite);
            }
        }
Exemple #10
0
        /// <summary>
        /// Function to load a sprite from the file system.
        /// </summary>
        /// <param name="path">Path to the file to load.</param>
        /// <returns>A byte array containing the data for a file from the file system.</returns>
        private byte[] LoadFile(string path)
        {
            IGorgonVirtualFile file = _fileSystem.GetFile(path);

            if (file == null)
            {
                throw new FileNotFoundException($"The file '{path}' was not found in the file system.");
            }

            using (Stream stream = file.OpenStream())
            {
                byte[] result = new byte[stream.Length];

                stream.Read(result, 0, result.Length);

                return(result);
            }
        }
        /// <summary>
        /// Function to load an associated texture file from the file system.
        /// </summary>
        /// <param name="file">The file that may contain the texture data.</param>
        /// <param name="codecs">The list of codecs to use when determining file type.</param>
        /// <returns>The image codec to use, or <b>null</b> if no appropriate codec was found.</returns>
        private static IGorgonImageCodec FindTextureCodec(IGorgonVirtualFile file, IEnumerable <IGorgonImageCodec> codecs)
        {
            Stream textureStream = file.OpenStream();

            try
            {
                if (!textureStream.CanSeek)
                {
                    Stream newStream = new DataStream((int)textureStream.Length, true, true);
                    textureStream.CopyTo(newStream);
                    newStream.Position = 0;
                    textureStream.Dispose();
                    textureStream = newStream;
                }

                // First try to find the codec by file extension.
                IEnumerable <IGorgonImageCodec> matchedExtensions =
                    codecs.Where(item => item.CodecCommonExtensions.Any(ext => string.Equals(file.Extension, ext, StringComparison.OrdinalIgnoreCase)));

                foreach (IGorgonImageCodec codec in matchedExtensions)
                {
                    if (codec.IsReadable(textureStream))
                    {
                        return(codec);
                    }
                }

                // If that failed, then look it up by testing all passed in codecs.
                foreach (IGorgonImageCodec codec in codecs)
                {
                    if (codec.IsReadable(textureStream))
                    {
                        return(codec);
                    }
                }
            }
            finally
            {
                textureStream.Dispose();
            }

            return(null);
        }
Exemple #12
0
        /// <summary>
        /// Function to load the text into the file system.
        /// </summary>
        private void LoadText()
        {
            DirectoryInfo physicalPath = GorgonExample.GetResourcePath(@"FolderSystem\");

            // Unload the mounted files.
            _writer.Unmount();
            _fileSystem.Unmount(physicalPath.FullName);

            _fileSystem.Mount(physicalPath.FullName);

            // Load the original before we mount the write directory.
            IGorgonVirtualFile file = _fileSystem.GetFile("/SomeText.txt");

            using (Stream stream = file.OpenStream())
            {
                byte[] textData = new byte[stream.Length];

                stream.Read(textData, 0, textData.Length);
                _originalText = Encoding.UTF8.GetString(textData);
            }

            // Set the write location to the users app data folder.
            _writer.Mount();

            // Load the modified version (if it exists, if it doesn't, the original will be loaded instead).
            file = _fileSystem.GetFile("/SomeText.txt");

            using (Stream stream = file.OpenStream())
            {
                byte[] textData = new byte[stream.Length];

                stream.Read(textData, 0, textData.Length);
                _changedText = Encoding.UTF8.GetString(textData);

                textDisplay.Text = string.Equals(_changedText, _originalText, StringComparison.CurrentCulture) ? _originalText : _changedText;
            }
        }
Exemple #13
0
        /// <summary>
        /// Function to locate the associated texture file for a sprite.
        /// </summary>
        /// <param name="textureName">The name of the texture.</param>
        /// <returns>The texture file.</returns>
        private IGorgonVirtualFile LocateTextureFile(string textureName)
        {
            // Check to see if the name has path information for the texture in the name.
            // The GorgonEditor from v2 does this.
            if (textureName.Contains("/"))
            {
                IGorgonVirtualFile textureFile = _fileSystem.GetFile(textureName);

                if (textureFile == null)
                {
                    return(null);
                }

                using (Stream imgFileStream = textureFile.OpenStream())
                {
                    if (_ddsCodec.IsReadable(imgFileStream))
                    {
                        return(textureFile);
                    }
                }
            }

            return(null);
        }
Exemple #14
0
        /// <summary>
        /// Function to load the metadata for the project.
        /// </summary>
        /// <param name="fileSystem">The file system we're working with.</param>
        /// <returns>The project metadata interface.</returns>
        internal static IProjectMetadata GetMetadata(this IGorgonFileSystem fileSystem)
        {
            FileInfo           externalProjectData = null;
            IGorgonVirtualFile jsonMetaDataFile    = fileSystem.GetFile(CommonEditorConstants.EditorMetadataFileName);

            // If we're loading directly from an editor file system folder, then check in the directory above the mount point.
            // The editor places all file system data in a directory called "fs", and the metadata is located in the directory above that, so
            // we just need to find the mount point for the "fs" directory, and go up one.
            if (jsonMetaDataFile == null)
            {
                foreach (GorgonFileSystemMountPoint mountPoint in fileSystem.MountPoints.Where(item => item.PhysicalPath.EndsWith(Path.DirectorySeparatorChar + "fs" + Path.DirectorySeparatorChar)))
                {
                    string dirName = Path.GetDirectoryName(mountPoint.PhysicalPath.Substring(0, mountPoint.PhysicalPath.Length - 1));
                    if (File.Exists(Path.Combine(dirName, CommonEditorConstants.EditorMetadataFileName)))
                    {
                        externalProjectData = new FileInfo(Path.Combine(dirName, CommonEditorConstants.EditorMetadataFileName));
                    }
                }

                if (externalProjectData == null)
                {
                    throw new GorgonException(GorgonResult.CannotRead, Resources.GOREDIT_ERR_NOT_EDITOR_PROJECT);
                }
            }

            int expectedVersion = Convert.ToInt32(CommonEditorConstants.EditorCurrentProjectVersion.Replace("GOREDIT", string.Empty));
            int fileVersion     = int.MaxValue;

            using (Stream stream = externalProjectData == null ? jsonMetaDataFile.OpenStream() : externalProjectData.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
                using (var reader = new StreamReader(stream, Encoding.UTF8))
                    using (var jsonReader = new JsonTextReader(reader))
                    {
                        // First property must be the version #.
                        if ((!jsonReader.Read()) || (!jsonReader.Read()))
                        {
                            throw new GorgonException(GorgonResult.CannotRead, Resources.GOREDIT_ERR_NOT_EDITOR_PROJECT);
                        }

                        if ((jsonReader.TokenType != JsonToken.PropertyName) ||
                            (!string.Equals(jsonReader.Value.ToString(), nameof(IProjectMetadata.Version), StringComparison.Ordinal)))
                        {
                            throw new GorgonException(GorgonResult.CannotRead, Resources.GOREDIT_ERR_NOT_EDITOR_PROJECT);
                        }

                        if (!int.TryParse(jsonReader.ReadAsString().Replace("GOREDIT", string.Empty), NumberStyles.Integer, CultureInfo.InvariantCulture, out fileVersion))
                        {
                            throw new GorgonException(GorgonResult.CannotRead, Resources.GOREDIT_ERR_NOT_EDITOR_PROJECT);
                        }

                        // Ensure we have the correct version.
                        if (expectedVersion < fileVersion)
                        {
                            throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_VERSION_MISMATCH, fileVersion, expectedVersion));
                        }
                    }

            using (Stream stream = externalProjectData == null ? jsonMetaDataFile.OpenStream() : externalProjectData.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
                using (var reader = new StreamReader(stream, Encoding.UTF8))
                {
                    string jsonString = reader.ReadToEnd();

                    switch (fileVersion)
                    {
                    case 30:
                        return(JsonConvert.DeserializeObject <EditorProjectMetadata30>(jsonString));

                    default:
                        throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_VERSION_MISMATCH, fileVersion, expectedVersion));
                    }
                }
        }
Exemple #15
0
 /// <summary>
 /// Initializes a new instance of the <see cref="RamDiskFileInfo"/> struct.
 /// </summary>
 /// <param name="file">The file.</param>
 public RamDiskFileInfo(IGorgonVirtualFile file)
     : this(file.FullPath, file.Size, file.CreateDate, file.LastModifiedDate)
 {
 }
Exemple #16
0
        /// <summary>
        /// Function to load a sprite from the editor file system.
        /// </summary>
        /// <param name="fileSystem">The file system containing the editor data.</param>
        /// <param name="renderer">The current renderer.</param>
        /// <param name="path">The path to the sprite.</param>
        /// <param name="textureUsage">[Optional] The intended usage for the texture.</param>
        /// <param name="spriteCodecs">[Optional] A list of additonal codecs used to read sprite data.</param>
        /// <param name="imageCodecs">[Optional] A list of additonal codecs used to read image data.</param>
        /// <param name="overrideTexture">[Optional] A texture view to use instead of loading the texture from the file system.</param>
        /// <returns>A new <see cref="GorgonSprite"/>, along with its associated texture.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="fileSystem"/>, <paramref name="renderer"/>, or the <paramref name="path"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="path"/> parameter is empty.</exception>
        /// <exception cref="GorgonException">Thrown if the file system isn't a Gorgon Editor file system, or the file could not be read.</exception>
        /// <remarks>
        /// <para>
        /// This method will load a sprite from a Gorgon Editor file system mounted as a <see cref="IGorgonFileSystem"/>.
        /// </para>
        /// <para>
        /// The <paramref name="spriteCodecs"/> parameter is used to allow custom sprite codecs to be used when loading data (assuming the sprite data was generated using one of the codecs supplied). This
        /// allows a user to create a custom sprite codec plug in and use that to read sprite data.  The <paramref name="imageCodecs"/> is used in exactly the same way, but only for image data.
        /// </para>
        /// <para>
        /// Providing the <paramref name="overrideTexture"/> will skip the texture loading and use the texture passed in.  In this case, the texture return value will be <b>null</b> as it is assumed the
        /// user already knows about the texture resource and is managing the lifetime of the texture elsewhere.
        /// </para>
        /// <para>
        /// When the method returns, it returns a tuple containing the sprite that was loaded, and the associated texture resource for the sprite. If the texture could not be loaded for any reason,
        /// and the <paramref name="overrideTexture"/> parameter is <b>null</b>, then the texture return value will be <b>null</b>, and no texture will be assigned to the sprite.
        /// </para>
        /// <para>
        /// <h2>Technical info</h2>
        /// <para>
        /// Plug ins must generate the following metadata for the files in the editor file system.
        /// </para>
        /// <para>
        /// The sprite file metadata must have the following attributes: <c>Type</c> with a value of "Sprite", and <c>SpriteCodec</c>, and its associated texture must have a dependency type of <c>Image</c> or else the sprite will not load.
        /// </para>
        /// <para>
        /// The associated texture file metadata must have the following attributes: <c>Type</c> with a value of "Image", and <c>ImageCodec</c> or the texure will not load.
        /// </para>
        /// </para>
        /// <para>
        /// <note type="important">
        /// <para>
        /// <b>Regarding textures:</b> This method will load the associated texture for the sprite into memory, and will do its best to only load that texture one time. When the texture is loaded, it will
        /// remain resident until Gorgon is shut down (typically when the application shuts down). In many cases, this is not ideal, so users must dispose of the <see cref="GorgonTexture2D"/> returned by
        /// this method if unloading the texture data is desired (e.g. a level changes and new graphics need to be loaded).
        /// </para>
        /// </note>
        /// </para>
        /// </remarks>
        public static (GorgonSprite sprite, GorgonTexture2D texture) LoadSprite(this IGorgonFileSystem fileSystem, Gorgon2D renderer, string path, ResourceUsage textureUsage = ResourceUsage.Default, IReadOnlyList <IGorgonSpriteCodec> spriteCodecs = null, IReadOnlyList <IGorgonImageCodec> imageCodecs = null, GorgonTexture2DView overrideTexture = null)
        {
            if (fileSystem == null)
            {
                throw new ArgumentNullException(nameof(fileSystem));
            }

            if (renderer == null)
            {
                throw new ArgumentNullException(nameof(renderer));
            }

            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            if (string.IsNullOrWhiteSpace(path))
            {
                throw new ArgumentEmptyException(nameof(path));
            }

            IProjectMetadata metaData = fileSystem.GetMetadata();

            if (!metaData.ProjectItems.TryGetValue(path, out ProjectItemMetadata fileMetadata))
            {
                throw new FileNotFoundException(string.Format(Resources.GOREDIT_ERR_FILE_NOT_FOUND, path));
            }

            IReadOnlyDictionary <string, IGorgonSpriteCodec> supportedSpriteCodecs = GetSpriteCodecs(renderer, spriteCodecs);
            IReadOnlyDictionary <string, IGorgonImageCodec>  supportedImageCodecs  = GetImageCodecs(imageCodecs);

            if ((!fileMetadata.Attributes.TryGetValue(CommonEditorConstants.ContentTypeAttr, out string contentType)) ||
                (!string.Equals(contentType, CommonEditorContentTypes.SpriteType, StringComparison.OrdinalIgnoreCase)))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_NOT_SPRITE, path));
            }

            if (!fileMetadata.Attributes.TryGetValue("SpriteCodec", out string codecTypeName))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_UNSUPPORTED_CODEC, string.Empty));
            }

            if (!supportedSpriteCodecs.TryGetValue(codecTypeName, out IGorgonSpriteCodec spriteCodec))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_UNSUPPORTED_CODEC, codecTypeName));
            }

            IGorgonVirtualFile file = fileSystem.GetFile(path);

            if (file == null)
            {
                throw new FileNotFoundException(string.Format(Resources.GOREDIT_ERR_FILE_NOT_FOUND, path));
            }

            GorgonTexture2D texture = null;

            if (overrideTexture == null)
            {
                if (fileMetadata.DependsOn.TryGetValue(CommonEditorContentTypes.ImageType, out string imagePath))
                {
                    texture         = GetTexture(renderer.Graphics, fileSystem, metaData, imagePath, textureUsage, supportedImageCodecs);
                    overrideTexture = texture.GetShaderResourceView();
                }
            }

            using (Stream stream = file.OpenStream())
            {
                return(spriteCodec.FromStream(stream, overrideTexture, (int)file.Size), texture);
            }
        }
 /// <summary>
 /// Function to open a stream to a file on the physical file system from the <see cref="IGorgonVirtualFile"/> passed in.
 /// </summary>
 /// <param name="file">The <see cref="IGorgonVirtualFile"/> that will be used to locate the file that will be opened on the physical file system.</param>
 /// <returns>A <see cref="Stream"/> to the file, or <b>null</b> if the file does not exist.</returns>
 /// <exception cref="ArgumentNullException">Thrown when the <paramref name="file"/> parameter is <b>null</b>.</exception>
 /// <remarks>
 /// <para>
 /// This will take the <see cref="IGorgonVirtualFile"/> and open its corresponding physical file location as a stream for reading. The stream that is returned will be opened, and as such, it is the
 /// responsibility of the user to close the stream when finished.
 /// </para>
 /// <para>
 /// If the file does not exist in the physical file system, this method should return <b>null</b>.
 /// </para>
 /// <para>
 /// Implementors of a <see cref="GorgonFileSystemProvider"/> plug in can overload this method to return a stream into a file within their specific native provider (e.g. a Zip file provider will
 /// return a stream into the zip file positioned at the location of the compressed file within the zip file).
 /// </para>
 /// </remarks>
 protected virtual GorgonFileSystemStream OnOpenFileStream(IGorgonVirtualFile file) => !File.Exists(file.PhysicalFile.FullPath) ? null : new GorgonFileSystemStream(file, File.Open(file.PhysicalFile.FullPath, FileMode.Open, FileAccess.Read, FileShare.Read));
 /// <summary>
 /// Function to open a stream to a file on the physical file system from the <see cref="IGorgonVirtualFile"/> passed in.
 /// </summary>
 /// <param name="file">The <see cref="IGorgonVirtualFile"/> that will be used to locate the file that will be opened on the physical file system.</param>
 /// <returns>A <see cref="Stream"/> to the file, or <b>null</b> if the file does not exist.</returns>
 /// <exception cref="ArgumentNullException">Thrown when the <paramref name="file"/> parameter is <b>null</b>.</exception>
 /// <remarks>
 /// <para>
 /// This will take the <see cref="IGorgonVirtualFile"/> and open its corresponding physical file location as a stream for reading. The stream that is returned will be opened, and as such, it is the
 /// responsibility of the user to close the stream when finished.
 /// </para>
 /// <para>
 /// If the file does not exist in the physical file system, this method should return <b>null</b>.
 /// </para>
 /// <para>
 /// Implementors of a <see cref="GorgonFileSystemProvider"/> plug in can overload this method to return a stream into a file within their specific native provider (e.g. a Zip file provider will
 /// return a stream into the zip file positioned at the location of the compressed file within the zip file).
 /// </para>
 /// </remarks>
 protected override GorgonFileSystemStream OnOpenFileStream(IGorgonVirtualFile file) => new ZipFileStream(file, File.Open(file.MountPoint.PhysicalPath, FileMode.Open, FileAccess.Read, FileShare.Read));
        /// <summary>
        /// Function to copy the file system data from a file system file.
        /// </summary>
        /// <param name="fileSystemFile">The file system file to copy.</param>
        /// <param name="provider">The provider to use.</param>
        /// <param name="fileSystemDir">The workspace directory to copy the files into.</param>
        /// <returns>The path to the metadata file.</returns>
        private async Task <FileInfo> CopyFileSystemAsync(FileInfo fileSystemFile, IGorgonFileSystemProvider provider, DirectoryInfo fileSystemDir)
        {
            IGorgonFileSystem fileSystem = new GorgonFileSystem(provider, Program.Log);

            fileSystem.Mount(fileSystemFile.FullName);

            IGorgonVirtualFile metaData = fileSystem.GetFile(Path.Combine("/", CommonEditorConstants.EditorMetadataFileName));

            // Get all directories and replicate them.
            IEnumerable <IGorgonVirtualDirectory> directories = fileSystem.FindDirectories("/", "*")
                                                                .OrderBy(item => item.FullPath.Length);

            foreach (IGorgonVirtualDirectory directory in directories)
            {
                var dirInfo = new DirectoryInfo(Path.Combine(fileSystemDir.FullName, directory.FullPath.FormatDirectory(Path.DirectorySeparatorChar).Substring(1)));

                if (dirInfo.Exists)
                {
                    continue;
                }

                dirInfo.Create();
            }

            // Copy all files into the directories we just created.
            var files = fileSystem.FindFiles("/", "*")
                        .Where(item => item != metaData)
                        .OrderByDescending(item => item.Size)
                        .ToList();

            int maxJobCount = (Environment.ProcessorCount * 2).Min(32).Max(1);
            int filesPerJob = (int)((float)files.Count / maxJobCount).FastCeiling();
            var jobs        = new List <Task>();

            if ((files.Count <= 100) || (maxJobCount < 2))
            {
                filesPerJob = files.Count;
            }

            Program.Log.Print($"Copying file system. {filesPerJob} files will be copied in a single job.", LoggingLevel.Verbose);

            void CopyFile(FileCopyJob job)
            {
                foreach (IGorgonVirtualFile file in job.Files)
                {
                    var fileInfo = new FileInfo(Path.Combine(fileSystemDir.FullName, file.Directory.FullPath.FormatDirectory(Path.DirectorySeparatorChar).Substring(1), file.Name));

                    using (Stream readStream = file.OpenStream())
                        using (Stream writeStream = fileInfo.OpenWrite())
                        {
                            readStream.CopyToStream(writeStream, (int)readStream.Length, job.ReadBuffer);
                        }
                }
            }

            // Build up the tasks for our jobs.
            while (files.Count > 0)
            {
                var jobData = new FileCopyJob();

                // Copy the file information to the file copy job data.
                int length = filesPerJob.Min(files.Count);
                for (int i = 0; i < length; ++i)
                {
                    jobData.Files.Add(files[i]);
                }
                files.RemoveRange(0, length);

                jobs.Add(Task.Run(() => CopyFile(jobData)));
            }

            Program.Log.Print($"{jobs.Count} jobs running for file copy from '{fileSystemFile.FullName}'.", LoggingLevel.Verbose);

            // Wait for the file copy to finish.
            await Task.WhenAll(jobs);

            var metaDataOutput = new FileInfo(Path.Combine(fileSystemDir.FullName, CommonEditorConstants.EditorMetadataFileName));

            if (metaData == null)
            {
                Program.Log.Print($"'{fileSystemFile.FullName}' has no metadata. A new metadata index will be generated.", LoggingLevel.Verbose);
                return(metaDataOutput);
            }

            Program.Log.Print($"'{fileSystemFile.FullName}' has metadata. Copying to the .", LoggingLevel.Verbose);
            byte[] writeBuffer = new byte[81920];
            using (Stream readStream = metaData.OpenStream())
                using (Stream writeStream = metaDataOutput.OpenWrite())
                {
                    readStream.CopyToStream(writeStream, (int)readStream.Length, writeBuffer);
                }

            metaDataOutput.Attributes = FileAttributes.Archive | FileAttributes.Normal;
            metaDataOutput.Refresh();

            return(metaDataOutput);
        }
        /// <summary>
        /// Function to load a <see cref="IGorgonAnimation"/> from a <see cref="GorgonFileSystem"/>.
        /// </summary>
        /// <param name="fileSystem">The file system to load the animation from.</param>
        /// <param name="renderer">The renderer for the animation.</param>
        /// <param name="path">The path to the animation file in the file system.</param>
        /// <param name="textureOptions">[Optional] Options for the texture loaded associated the sprite.</param>
        /// <param name="animationCodecs">The list of animation codecs to try and load the animation with.</param>
        /// <param name="imageCodecs">The list of image codecs to try and load the animation texture(s) with.</param>
        /// <returns>The animation data in the file as a <see cref="IGorgonAnimation"/>.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="fileSystem"/>, <paramref name="renderer"/>, or <paramref name="path"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="path"/> parameter is empty.</exception>
        /// <exception cref="FileNotFoundException">Thrown if the file in the <paramref name="path"/> was not found.</exception>
        /// <exception cref="GorgonException">Thrown if the animation data in the file system could not be loaded because a suitable codec was not found.</exception>
        /// <remarks>
        /// <para>
        /// This method extends a <see cref="GorgonFileSystem"/> so that animations can be loaded by calling a method on the file system object itself. This negates the need for users to create complex code
        /// for loading an animation.
        /// </para>
        /// <para>
        /// When loading an animation, the method will attempt to locate any <see cref="GorgonTexture2DView"/> objects associated with the animation (if they exist). When loading, it will check:
        /// <list type="number">
        ///     <item>
        ///         <description>For a texture resource with the same name that is already loaded into memory.</description>
        ///     </item>
        ///     <item>
        ///         <description>Use the local <see cref="IGorgonVirtualDirectory"/> for the sprite file and search for the texture in that directory.</description>
        ///     </item>
        ///     <item>
        ///         <description>Check the entire <paramref name="fileSystem"/> for a file if the texture name contains path information (this is done by the GorgonEditor from v2).</description>
        ///     </item>
        /// </list>
        /// If the file is found, and can be loaded by one of the <paramref name="imageCodecs"/>, then it is loaded and assigned to the sprite.
        /// </para>
        /// <para>
        /// The <paramref name="animationCodecs"/> is a list of codecs for loading sprite data. If the user specifies this parameter, the only the codecs provided will be used for determining if an
        /// animation can be read. If it is not supplied, then all built-in (i.e. not plug in based) sprite codecs will be used.
        /// </para>
        /// <para>
        /// The <paramref name="imageCodecs"/> is a list of codecs for loading image data. If the user specifies this parameter, the only the codecs provided will be used for determining if an image can be
        /// read. If it is not supplied, then all built-in (i.e. not plug in based) image codecs will be used.
        /// </para>
        /// </remarks>
        /// <seealso cref="GorgonFileSystem"/>
        /// <seealso cref="GorgonTexture2DView"/>
        /// <seealso cref="IGorgonAnimation"/>
        public static IGorgonAnimation LoadAnimationFromFileSystem(this GorgonFileSystem fileSystem,
                                                                   Gorgon2D renderer,
                                                                   string path,
                                                                   GorgonTexture2DLoadOptions textureOptions           = null,
                                                                   IEnumerable <IGorgonAnimationCodec> animationCodecs = null,
                                                                   IEnumerable <IGorgonImageCodec> imageCodecs         = null)
        {
            if (fileSystem == null)
            {
                throw new ArgumentNullException(nameof(fileSystem));
            }

            IGorgonVirtualFile file = fileSystem.GetFile(path);

            if (file == null)
            {
                throw new FileNotFoundException(string.Format(Resources.GOR2DIO_ERR_FILE_NOT_FOUND, path));
            }

            if ((imageCodecs == null) || (!imageCodecs.Any()))
            {
                // If we don't specify any codecs, then use the built in ones.
                imageCodecs = new IGorgonImageCodec[]
                {
                    new GorgonCodecPng(),
                    new GorgonCodecBmp(),
                    new GorgonCodecDds(),
                    new GorgonCodecGif(),
                    new GorgonCodecJpeg(),
                    new GorgonCodecTga(),
                };
            }
            else
            {
                // Only use codecs that can decode image data.
                imageCodecs = imageCodecs.Where(item => item.CanDecode);
            }

            if ((animationCodecs == null) || (!animationCodecs.Any()))
            {
                // Use all built-in codecs if we haven't asked for any.
                animationCodecs = new IGorgonAnimationCodec[]
                {
                    new GorgonV3AnimationBinaryCodec(renderer),
                    new GorgonV3AnimationJsonCodec(renderer),
                    new GorgonV1AnimationCodec(renderer)
                };
            }
            else
            {
                // Only use codecs that can decode sprite data.
                animationCodecs = animationCodecs.Where(item => item.CanDecode);
            }

            Stream animStream = file.OpenStream();

            try
            {
                if (!animStream.CanSeek)
                {
                    Stream newStream = new DataStream((int)animStream.Length, true, true);
                    animStream.CopyTo(newStream);
                    newStream.Position = 0;

                    animStream.Dispose();
                    animStream = newStream;
                }

                IGorgonAnimationCodec animationCodec = GetAnimationCodec(animStream, animationCodecs);

                if (animationCodec == null)
                {
                    throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOR2DIO_ERR_NO_SUITABLE_ANIM_CODEC_FOUND, path));
                }

                // Load the animation.
                IGorgonAnimation animation = animationCodec.FromStream(animStream, (int)file.Size);

                // We have no textures to update, leave.
                if (animation.Texture2DTrack.KeyFrames.Count == 0)
                {
                    return(animation);
                }

                // Try to locate the textures.

                // V1 sprite animations need texture coordinate correction.
                bool needsCoordinateFix = animationCodec is GorgonV1AnimationCodec;

                foreach (GorgonKeyTexture2D textureKey in animation.Texture2DTrack.KeyFrames)
                {
                    // Let's try and load the texture into memory.
                    // This does this by:
                    // 1. Checking to see if a texture resource with the name specified is already available in memory.
                    // 2. Checking the local directory of the file to see if the texture is there.
                    // 3. A file system wide search.

                    // ReSharper disable once InvertIf
                    (IGorgonImageCodec codec, IGorgonVirtualFile textureFile, bool loaded) =
                        LocateTextureCodecAndFile(fileSystem, file.Directory, renderer, textureKey.TextureName, imageCodecs);

                    // We have not loaded the texture yet.  Do so now.
                    // ReSharper disable once InvertIf
                    if ((!loaded) && (textureFile != null) && (codec != null))
                    {
                        using (Stream textureStream = textureFile.OpenStream())
                        {
                            textureKey.Value = GorgonTexture2DView.FromStream(renderer.Graphics,
                                                                              textureStream,
                                                                              codec,
                                                                              textureFile.Size, GetTextureOptions(textureFile.FullPath, textureOptions));
                        }
                    }

                    if ((needsCoordinateFix) && (textureKey.Value != null))
                    {
                        textureKey.TextureCoordinates = new RectangleF(textureKey.TextureCoordinates.X / textureKey.Value.Width,
                                                                       textureKey.TextureCoordinates.Y / textureKey.Value.Height,
                                                                       textureKey.TextureCoordinates.Width / textureKey.Value.Width,
                                                                       textureKey.TextureCoordinates.Height / textureKey.Value.Height);
                    }
                }

                return(animation);
            }
            finally
            {
                animStream?.Dispose();
            }
        }
Exemple #21
0
 /// <summary>
 /// Function to open a stream to a file on the physical file system from the <see cref="IGorgonVirtualFile"/> passed in.
 /// </summary>
 /// <param name="file">The <see cref="IGorgonVirtualFile"/> that will be used to locate the file that will be opened on the physical file system.</param>
 /// <returns>A <see cref="Stream"/> to the file, or <b>null</b> if the file does not exist.</returns>
 /// <exception cref="ArgumentNullException">Thrown when the <paramref name="file"/> parameter is <b>null</b>.</exception>
 /// <remarks>
 /// <para>
 /// This will take the <see cref="IGorgonVirtualFile"/> and open its corresponding physical file location as a stream for reading. The stream that is returned will be opened, and as such, it is the
 /// responsibility of the user to close the stream when finished.
 /// </para>
 /// </remarks>
 protected virtual Stream OnOpenFileStream(IGorgonVirtualFile file) => FileData.OpenReadStream(file.FullPath);
Exemple #22
0
        /// <summary>
        /// Function to load an image from the editor file system.
        /// </summary>
        /// <param name="fileSystem">The file system containing the editor data.</param>
        /// <param name="path">The path to the sprite.</param>
        /// <param name="imageCodecs">[Optional] A list of additonal codecs used to read image data.</param>
        /// <returns>A new <see cref="IGorgonImage"/> containing the image data from the file system.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="fileSystem"/>, or the <paramref name="path"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="path"/> parameter is empty.</exception>
        /// <exception cref="GorgonException">Thrown if the file system isn't a Gorgon Editor file system, or the file could not be read.</exception>
        /// <remarks>
        /// <para>
        /// This method will load an image from a Gorgon Editor file system mounted as a <see cref="IGorgonFileSystem"/>.
        /// </para>
        /// <para>
        /// The <paramref name="imageCodecs"/> parameter is used to allow custom image codecs to be used when loading data (assuming the image data was saved using one of the codecs supplied). This
        /// allows a user to create a custom image codec plug in and use that to read image data.
        /// </para>
        /// <para>
        /// <h2>Technical info</h2>
        /// <para>
        /// Plug ins must generate the following metadata for the files in the editor file system.
        /// </para>
        /// <para>
        /// The image file metadata must have the following attributes: <c>Type</c> with a value of "Image", and <c>ImageCodec</c> or else the image will not load.
        /// </para>
        /// <para>
        /// If image file has been marked as premultiplied in the editor, then the texture will be converted to use premultiplied alpha when loading. This is only done when the texture is read from the
        /// file system, cached textures will left as-is.
        /// </para>
        /// </para>
        /// </remarks>
        public static IGorgonImage LoadImage(this IGorgonFileSystem fileSystem, string path, IReadOnlyList <IGorgonImageCodec> imageCodecs = null)
        {
            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.ProjectItems.TryGetValue(path, out ProjectItemMetadata fileMetadata))
            {
                throw new FileNotFoundException(string.Format(Resources.GOREDIT_ERR_FILE_NOT_FOUND, path));
            }

            IReadOnlyDictionary <string, IGorgonImageCodec> supportedImageCodecs = GetImageCodecs(imageCodecs);

            if ((!fileMetadata.Attributes.TryGetValue(CommonEditorConstants.ContentTypeAttr, out string contentType)) ||
                (!string.Equals(contentType, CommonEditorContentTypes.ImageType, StringComparison.OrdinalIgnoreCase)))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_NOT_IMAGE, path));
            }

            if (!fileMetadata.Attributes.TryGetValue("ImageCodec", out string codecTypeName))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_UNSUPPORTED_CODEC, string.Empty));
            }

            if (!supportedImageCodecs.TryGetValue(codecTypeName, out IGorgonImageCodec imageCodec))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_UNSUPPORTED_CODEC, codecTypeName));
            }

            IGorgonVirtualFile file = fileSystem.GetFile(path);

            if (file == null)
            {
                throw new FileNotFoundException(string.Format(Resources.GOREDIT_ERR_FILE_NOT_FOUND, path));
            }

            bool shouldConvertToPremultiplied = false;

            if (fileMetadata.Attributes.TryGetValue("PremultipliedAlpha", out string isPremultiplied))
            {
#pragma warning disable CA1806 // Do not ignore method results
                bool.TryParse(isPremultiplied, out shouldConvertToPremultiplied);
#pragma warning restore CA1806 // Do not ignore method results
            }

            using (Stream stream = file.OpenStream())
            {
                return(shouldConvertToPremultiplied ? imageCodec.LoadFromStream(stream, (int)file.Size).ConvertToPremultipliedAlpha() : imageCodec.LoadFromStream(stream, (int)file.Size));
            }
        }
 /// <summary>
 /// Function to open a stream to a file on the physical file system from the <see cref="IGorgonVirtualFile"/> passed in.
 /// </summary>
 /// <param name="file">The <see cref="IGorgonVirtualFile"/> that will be used to locate the file that will be opened on the physical file system.</param>
 /// <returns>A <see cref="Stream"/> to the file, or <b>null</b> if the file does not exist.</returns>
 /// <exception cref="ArgumentNullException">Thrown when the <paramref name="file"/> parameter is <b>null</b>.</exception>
 /// <remarks>
 /// <para>
 /// This will take the <see cref="IGorgonVirtualFile"/> and open its corresponding physical file location as a stream for reading. The stream that is returned will be opened, and as such, it is the
 /// responsibility of the user to close the stream when finished.
 /// </para>
 /// <para>
 /// If the file does not exist in the physical file system, this method should return <b>null</b>.
 /// </para>
 /// <para>
 /// Implementors of a <see cref="GorgonFileSystemProvider"/> plug in can overload this method to return a stream into a file within their specific native provider (e.g. a Zip file provider will
 /// return a stream into the zip file positioned at the location of the compressed file within the zip file).
 /// </para>
 /// </remarks>
 protected override GorgonFileSystemStream OnOpenFileStream(IGorgonVirtualFile file) => new GorPackFileStream(file,
                                                                                                              File.Open(file.MountPoint.PhysicalPath, FileMode.Open, FileAccess.Read, FileShare.Read),
                                                                                                              file.PhysicalFile.CompressedLength);
        /// <summary>
        /// Function to copy data from a file system to the file system linked to this writer.
        /// </summary>
        /// <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="files">The files in the source file system to copy.</param>
        /// <param name="directories">The directories in the source file system to copy.</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(Func <GorgonWriterCopyProgress, bool> progress,
                                                                  bool allowOverwrite,
                                                                  IGorgonVirtualFile[] files,
                                                                  IGorgonVirtualDirectory[] directories,
                                                                  CancellationToken token)
        {
            int directoryCount = 0;
            int fileCount      = 0;

            // Create all the directories.
            foreach (IGorgonVirtualDirectory directory in directories)
            {
                if (token.IsCancellationRequested)
                {
                    return(directoryCount, fileCount);
                }

                CreateDirectory(directory.FullPath);
                ++directoryCount;

                if (token.IsCancellationRequested)
                {
                    return(0, 0);
                }
            }

            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(directoryCount, fileCount);
                }
            }

            return(directoryCount, fileCount);
        }
        /// <summary>
        /// Function to load a <see cref="GorgonPolySprite"/> from a <see cref="GorgonFileSystem"/>.
        /// </summary>
        /// <param name="fileSystem">The file system to load the sprite from.</param>
        /// <param name="renderer">The renderer for the sprite.</param>
        /// <param name="path">The path to the sprite file in the file system.</param>
        /// <param name="textureOptions">[Optional] Options for the texture loaded associated the sprite.</param>
        /// <param name="spriteCodecs">The list of polygonal sprite codecs to try and load the sprite with.</param>
        /// <param name="imageCodecs">The list of image codecs to try and load the sprite texture with.</param>
        /// <returns>The sprite data in the file as a <see cref="GorgonSprite"/>.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="fileSystem"/>, <paramref name="renderer"/>, or <paramref name="path"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="path"/> parameter is empty.</exception>
        /// <exception cref="FileNotFoundException">Thrown if the file in the <paramref name="path"/> was not found.</exception>
        /// <exception cref="GorgonException">Thrown if the sprite data in the file system could not be loaded because a suitable codec was not found.</exception>
        /// <remarks>
        /// <para>
        /// This method extends a <see cref="GorgonFileSystem"/> so that sprites can be loaded by calling a method on the file system object itself. This negates the need for users to create complex code
        /// for loading a sprite.
        /// </para>
        /// <para>
        /// When loading a sprite, the method will attempt to locate the <see cref="GorgonTexture2DView"/> associated with the sprite (if it exists). When loading, it will check:
        /// <list type="number">
        ///     <item>
        ///         <description>For a texture resource with the same name that is already loaded into memory.</description>
        ///     </item>
        ///     <item>
        ///         <description>Use the local <see cref="IGorgonVirtualDirectory"/> for the sprite file and search for the texture in that directory.</description>
        ///     </item>
        ///     <item>
        ///         <description>Check the entire <paramref name="fileSystem"/> for a file if the texture name contains path information (this is done by the GorgonEditor from v2).</description>
        ///     </item>
        /// </list>
        /// If the file is found, and can be loaded by one of the <paramref name="imageCodecs"/>, then it is loaded and assigned to the sprite.
        /// </para>
        /// <para>
        /// The <paramref name="spriteCodecs"/> is a list of codecs for loading polygonal sprite data. If the user specifies this parameter, the only the codecs provided will be used for determining if a
        /// sprite can be read. If it is not supplied, then all built-in (i.e. not plug in based) sprite codecs will be used.
        /// </para>
        /// <para>
        /// The <paramref name="imageCodecs"/> is a list of codecs for loading image data. If the user specifies this parameter, the only the codecs provided will be used for determining if an image can be
        /// read. If it is not supplied, then all built-in (i.e. not plug in based) image codecs will be used.
        /// </para>
        /// </remarks>
        /// <seealso cref="GorgonFileSystem"/>
        /// <seealso cref="GorgonTexture2DView"/>
        /// <seealso cref="GorgonPolySprite"/>
        public static GorgonPolySprite LoadPolySpriteFromFileSystem(this GorgonFileSystem fileSystem,
                                                                    Gorgon2D renderer,
                                                                    string path,
                                                                    GorgonTexture2DLoadOptions textureOptions         = null,
                                                                    IEnumerable <IGorgonPolySpriteCodec> spriteCodecs = null,
                                                                    IEnumerable <IGorgonImageCodec> imageCodecs       = null)
        {
            if (fileSystem == null)
            {
                throw new ArgumentNullException(nameof(fileSystem));
            }

            IGorgonVirtualFile file = fileSystem.GetFile(path);

            if (file == null)
            {
                throw new FileNotFoundException(string.Format(Resources.GOR2DIO_ERR_FILE_NOT_FOUND, path));
            }

            if ((imageCodecs == null) || (!imageCodecs.Any()))
            {
                // If we don't specify any codecs, then use the built in ones.
                imageCodecs = new IGorgonImageCodec[]
                {
                    new GorgonCodecPng(),
                    new GorgonCodecBmp(),
                    new GorgonCodecDds(),
                    new GorgonCodecGif(),
                    new GorgonCodecJpeg(),
                    new GorgonCodecTga(),
                };
            }
            else
            {
                // Only use codecs that can decode image data.
                imageCodecs = imageCodecs.Where(item => item.CanDecode);
            }

            if ((spriteCodecs == null) || (!spriteCodecs.Any()))
            {
                // Use all built-in codecs if we haven't asked for any.
                spriteCodecs = new IGorgonPolySpriteCodec[]
                {
                    new GorgonV3PolySpriteBinaryCodec(renderer),
                    new GorgonV3PolySpriteJsonCodec(renderer)
                };
            }
            else
            {
                // Only use codecs that can decode sprite data.
                spriteCodecs = spriteCodecs.Where(item => item.CanDecode);
            }

            Stream spriteStream = file.OpenStream();

            try
            {
                if (!spriteStream.CanSeek)
                {
                    Stream newStream = new DataStream((int)spriteStream.Length, true, true);
                    spriteStream.CopyTo(newStream);
                    newStream.Position = 0;
                    spriteStream.Dispose();
                    spriteStream = newStream;
                }

                IGorgonPolySpriteCodec spriteCodec = GetPolySpriteCodec(spriteStream, spriteCodecs);

                if (spriteCodec == null)
                {
                    throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOR2DIO_ERR_NO_SUITABLE_SPRITE_CODEC_FOUND, path));
                }

                // Try to locate the texture.
                string textureName = spriteCodec.GetAssociatedTextureName(spriteStream);

                GorgonTexture2DView textureForSprite = null;

                // Let's try and load the texture into memory.
                // This does this by:
                // 1. Checking to see if a texture resource with the name specified is already available in memory.
                // 2. Checking the local directory of the file to see if the texture is there.
                // 3. A file system wide search.

                // ReSharper disable once InvertIf
                if (!string.IsNullOrWhiteSpace(textureName))
                {
                    (IGorgonImageCodec codec, IGorgonVirtualFile textureFile, bool loaded) =
                        LocateTextureCodecAndFile(fileSystem, file.Directory, renderer, textureName, imageCodecs);

                    // We have not loaded the texture yet.  Do so now.
                    // ReSharper disable once InvertIf
                    if ((!loaded) && (textureFile != null) && (codec != null))
                    {
                        using (Stream textureStream = textureFile.OpenStream())
                        {
                            textureForSprite = GorgonTexture2DView.FromStream(renderer.Graphics,
                                                                              textureStream,
                                                                              codec,
                                                                              textureFile.Size,
                                                                              GetTextureOptions(textureFile.FullPath, textureOptions));
                        }
                    }
                }

                return(spriteCodec.FromStream(spriteStream, textureForSprite, (int)file.Size));
            }
            finally
            {
                spriteStream?.Dispose();
            }
        }
        /// <summary>
        /// Function to save the specified image file.
        /// </summary>
        /// <param name="name">The name of the file to write into.</param>
        /// <param name="image">The image to save.</param>
        /// <param name="pixelFormat">The pixel format for the image.</param>
        /// <param name="codec">[Optional] The codec to use when saving the image.</param>
        /// <returns>The updated working file.</returns>
        public IGorgonVirtualFile SaveImageFile(string name, IGorgonImage image, BufferFormat pixelFormat, IGorgonImageCodec codec = null)
        {
            if (codec == null)
            {
                codec = DefaultCodec;
            }

            // We absolutely need to have an extension, or else the texconv tool will not work.
            if ((codec.CodecCommonExtensions.Count > 0) &&
                (!string.Equals(Path.GetExtension(name), codec.CodecCommonExtensions[0], StringComparison.OrdinalIgnoreCase)))
            {
                _log.Print("Adding extension to working file or else external tools may not be able to read it.", LoggingLevel.Verbose);
                name = Path.ChangeExtension(name, codec.CodecCommonExtensions[0]);
            }

            IGorgonVirtualFile workFile = ScratchArea.FileSystem.GetFile(name);
            var formatInfo = new GorgonFormatInfo(pixelFormat);

            // The file doesn't exist, so we need to create a dummy file.
            if (workFile == null)
            {
                using (Stream tempStream = ScratchArea.OpenStream(name, FileMode.Create))
                {
                    tempStream.WriteString("TEMP_WORKING_FILE");
                }

                workFile = ScratchArea.FileSystem.GetFile(name);
            }

            _log.Print($"Working image file: '{workFile.FullPath}'.", LoggingLevel.Verbose);


            IGorgonVirtualFile result;

            // For compressed images, we need to rely on an external tool to do the job.
            if (formatInfo.IsCompressed)
            {
                _log.Print($"Pixel format [{pixelFormat}] is a block compression format, compressing using external tool...", LoggingLevel.Intermediate);
                if ((_compressor == null) || (!codec.SupportsBlockCompression))
                {
                    throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GORIMG_ERR_COMPRESSED_FILE, formatInfo.Format));
                }

                // Send the image data as uncompressed to our working file, so we can have something to compress.
                using (Stream outStream = ScratchArea.OpenStream(workFile.FullPath, FileMode.Create))
                {
                    codec.SaveToStream(image, outStream);
                }

                _log.Print($"Saving to working file '{workFile.FullPath}'...", LoggingLevel.Simple);
                result = _compressor.Compress(workFile, pixelFormat, image.MipCount);

                // Convert to an uncompressed format if we aren't already in that format.
                switch (pixelFormat)
                {
                case BufferFormat.BC5_SNorm when image.Format != BufferFormat.R8G8_SNorm:
                    image.ConvertToFormat(BufferFormat.R8G8_SNorm);
                    break;

                case BufferFormat.BC5_Typeless when image.Format != BufferFormat.R8G8_UNorm:
                case BufferFormat.BC5_UNorm when image.Format != BufferFormat.R8G8_UNorm:
                    image.ConvertToFormat(BufferFormat.R8G8_UNorm);
                    break;

                case BufferFormat.BC6H_Sf16 when image.Format != BufferFormat.R16G16B16A16_Float:
                case BufferFormat.BC6H_Typeless when image.Format != BufferFormat.R16G16B16A16_Float:
                case BufferFormat.BC6H_Uf16 when image.Format != BufferFormat.R16G16B16A16_Float:
                    image.ConvertToFormat(BufferFormat.R16G16B16A16_Float);
                    break;

                case BufferFormat.BC4_SNorm when image.Format != BufferFormat.R8G8_SNorm:
                    image.ConvertToFormat(BufferFormat.R8_SNorm);
                    break;

                case BufferFormat.BC4_Typeless when image.Format != BufferFormat.R8G8_UNorm:
                case BufferFormat.BC4_UNorm when image.Format != BufferFormat.R8G8_UNorm:
                    image.ConvertToFormat(BufferFormat.R8_UNorm);
                    break;

                case BufferFormat.BC1_Typeless when image.Format != BufferFormat.R8G8B8A8_UNorm:
                case BufferFormat.BC1_UNorm when image.Format != BufferFormat.R8G8B8A8_UNorm:
                case BufferFormat.BC2_Typeless when image.Format != BufferFormat.R8G8B8A8_UNorm:
                case BufferFormat.BC2_UNorm when image.Format != BufferFormat.R8G8B8A8_UNorm:
                case BufferFormat.BC3_Typeless when image.Format != BufferFormat.R8G8B8A8_UNorm:
                case BufferFormat.BC3_UNorm when image.Format != BufferFormat.R8G8B8A8_UNorm:
                case BufferFormat.BC7_Typeless when image.Format != BufferFormat.R8G8B8A8_UNorm:
                case BufferFormat.BC7_UNorm when image.Format != BufferFormat.R8G8B8A8_UNorm:
                    image.ConvertToFormat(BufferFormat.R8G8B8A8_UNorm);
                    break;

                case BufferFormat.BC1_UNorm_SRgb when image.Format != BufferFormat.R8G8B8A8_UNorm_SRgb:
                case BufferFormat.BC2_UNorm_SRgb when image.Format != BufferFormat.R8G8B8A8_UNorm_SRgb:
                case BufferFormat.BC3_UNorm_SRgb when image.Format != BufferFormat.R8G8B8A8_UNorm_SRgb:
                case BufferFormat.BC7_UNorm_SRgb when image.Format != BufferFormat.R8G8B8A8_UNorm_SRgb:
                    image.ConvertToFormat(BufferFormat.R8G8B8A8_UNorm_SRgb);
                    break;
                }

                if (result == null)
                {
                    throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GORIMG_ERR_COMPRESSED_FILE, formatInfo.Format));
                }
            }
            else
            {
                // We've changed the pixel format, so convert prior to saving.
                if (pixelFormat != image.Format)
                {
                    _log.Print($"Image pixel format [{image.Format}] is different than requested format of [{pixelFormat}], converting...", LoggingLevel.Intermediate);
                    image.ConvertToFormat(pixelFormat);
                    _log.Print($"Converted image '{workFile.Name}' to pixel format: [{pixelFormat}].", LoggingLevel.Simple);
                }

                _log.Print($"Saving to working file '{workFile.FullPath}'...", LoggingLevel.Simple);
                using (Stream outStream = ScratchArea.OpenStream(workFile.FullPath, FileMode.Create))
                {
                    codec.SaveToStream(image, outStream);
                }

                ScratchArea.FileSystem.Refresh();
                result = ScratchArea.FileSystem.GetFile(workFile.FullPath);
            }

            return(result);
        }
        /// <summary>
        /// Function to import an image file from the physical file system into the current image.
        /// </summary>
        /// <param name="codec">The codec used to open the file.</param>
        /// <param name="filePath">The path to the file to import.</param>
        /// <returns>The source file information, image data, the virtual file entry for the working file and the original pixel format of the file.</returns>
        public (FileInfo file, IGorgonImage image, IGorgonVirtualFile workingFile, BufferFormat originalFormat) ImportImage(IGorgonImageCodec codec, string filePath)
        {
            var file = new FileInfo(filePath);
            IGorgonImageCodec  importCodec  = codec;
            IGorgonImageInfo   metaData     = null;
            IGorgonVirtualFile workFile     = null;
            IGorgonImage       importImage  = null;
            string             workFilePath = $"{Path.GetFileNameWithoutExtension(filePath)}_import_{Guid.NewGuid().ToString("N")}";

            // Try to determine if we can actually read the file using an installed codec, if we can't, then try to find a suitable codec.
            using (FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                if ((importCodec == null) || (!importCodec.IsReadable(stream)))
                {
                    importCodec = null;

                    foreach (IGorgonImageCodec newCodec in InstalledCodecs.Codecs.Where(item => (item.CodecCommonExtensions.Count > 0) && (item.CanDecode)))
                    {
                        if (newCodec.IsReadable(stream))
                        {
                            importCodec = newCodec;
                            break;
                        }
                    }
                }

                if (importCodec == null)
                {
                    throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GORIMG_ERR_NO_CODEC, filePath));
                }

                metaData = importCodec.GetMetaData(stream);


                // We absolutely need to have an extension, or else the texconv tool will not work.
                var codecExtension = new GorgonFileExtension(importCodec.CodecCommonExtensions[0]);
                _log.Print($"Adding {codecExtension.Extension} extension to working file or else external tools may not be able to read it.", LoggingLevel.Verbose);
                workFilePath = $"{workFilePath}.{codecExtension.Extension}";

                using (Stream outStream = ScratchArea.OpenStream(workFilePath, FileMode.Create))
                {
                    stream.CopyTo(outStream);
                }
            }

            workFile = ScratchArea.FileSystem.GetFile(workFilePath);
            var formatInfo = new GorgonFormatInfo(metaData.Format);

            // This is always in DDS format.
            if (formatInfo.IsCompressed)
            {
                _log.Print($"Image is compressed using [{formatInfo.Format}] as its pixel format.", LoggingLevel.Intermediate);

                if (_compressor == null)
                {
                    throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GORIMG_ERR_COMPRESSED_FILE, formatInfo.Format));
                }

                _log.Print($"Loading image '{workFile.FullPath}'...", LoggingLevel.Simple);
                importImage = _compressor.Decompress(ref workFile, metaData);

                if (importImage == null)
                {
                    throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GORIMG_ERR_COMPRESSED_FILE, formatInfo.Format));
                }

                _log.Print($"Loaded compressed ([{formatInfo.Format}]) image data as [{importImage.Format}]", LoggingLevel.Intermediate);
            }
            else
            {
                using (Stream workStream = workFile.OpenStream())
                {
                    importImage = importCodec.LoadFromStream(workStream);
                }
            }

            return(file, importImage, workFile, metaData.Format);
        }
Exemple #28
0
        /// <summary>
        /// Function to initialize the application.
        /// </summary>
        /// <returns>The main window for the application.</returns>
        private static FormMain Initialize()
        {
            GorgonExample.ResourceBaseDirectory   = new DirectoryInfo(Settings.Default.ResourceLocation);
            GorgonExample.PlugInLocationDirectory = new DirectoryInfo(Settings.Default.PlugInLocation);

            FormMain window = GorgonExample.Initialize(new DX.Size2(Settings.Default.Resolution.Width, Settings.Default.Resolution.Height), "Depth");

            try
            {
                IReadOnlyList <IGorgonVideoAdapterInfo> videoDevices = GorgonGraphics.EnumerateAdapters(log: GorgonApplication.Log);

                if (videoDevices.Count == 0)
                {
                    throw new GorgonException(GorgonResult.CannotCreate,
                                              "Gorgon requires at least a Direct3D 11.4 capable video device.\nThere is no suitable device installed on the system.");
                }

                // Find the best video device.
                _graphics = new GorgonGraphics(videoDevices.OrderByDescending(item => item.FeatureSet).First());

                _screen = new GorgonSwapChain(_graphics,
                                              window,
                                              new GorgonSwapChainInfo("Gorgon2D Depth Buffer Example")
                {
                    Width  = Settings.Default.Resolution.Width,
                    Height = Settings.Default.Resolution.Height,
                    Format = BufferFormat.R8G8B8A8_UNorm
                });

                _depthBuffer = GorgonDepthStencil2DView.CreateDepthStencil(_graphics, new GorgonTexture2DInfo(_screen.RenderTargetView)
                {
                    Binding = TextureBinding.DepthStencil,
                    Format  = BufferFormat.D24_UNorm_S8_UInt
                });

                // Tell the graphics API that we want to render to the "screen" swap chain.
                _graphics.SetRenderTarget(_screen.RenderTargetView, _depthBuffer);

                // Initialize the renderer so that we are able to draw stuff.
                _renderer = new Gorgon2D(_graphics);

                GorgonExample.LoadResources(_graphics);

                // Load our packed file system plug in.
                _assemblyCache = new GorgonMefPlugInCache(GorgonApplication.Log);
                _assemblyCache.LoadPlugInAssemblies(GorgonExample.GetPlugInPath().FullName, "Gorgon.FileSystem.GorPack.dll");
                IGorgonPlugInService plugIns = new GorgonMefPlugInService(_assemblyCache);

                // Load the file system containing our application data (sprites, images, etc...)
                IGorgonFileSystemProviderFactory providerFactory = new GorgonFileSystemProviderFactory(plugIns, GorgonApplication.Log);
                IGorgonFileSystemProvider        provider        = providerFactory.CreateProvider("Gorgon.IO.GorPack.GorPackProvider");
                IGorgonFileSystem fileSystem = new GorgonFileSystem(provider, GorgonApplication.Log);

                // We can load the editor file system directly.
                // This is handy for switching a production environment where your data may be stored
                // as a compressed file, and a development environment where your data consists of loose
                // files.
                // fileSystem.Mount(@"D:\unpak\scratch\DeepAsAPuddle.gorPack\fs\");

                // For now though, we'll load the packed file.
                fileSystem.Mount(Path.Combine(GorgonExample.GetResourcePath(@"FileSystems").FullName, "Depth.gorPack"));

                // Get our sprites.  These make up the frames of animation for our Guy.
                // If and when there's an animation editor, we'll only need to create a single sprite and load the animation.
                IGorgonVirtualFile[] spriteFiles = fileSystem.FindFiles("/Sprites/", "*", true).ToArray();

                // Load our sprite data (any associated textures will be loaded as well).
                Dictionary <string, GorgonSprite> sprites = new Dictionary <string, GorgonSprite>(StringComparer.OrdinalIgnoreCase);

                for (int i = 0; i < spriteFiles.Length; i++)
                {
                    IGorgonVirtualFile file = spriteFiles[i];
                    (GorgonSprite sprite, GorgonTexture2D texture) = fileSystem.LoadSprite(_renderer, file.FullPath);

                    // The LoadSprite extension method will automatically find and load your associated texture if you're using
                    // a Gorgon editor file system. So it's important that you leep track of your textures, disposing of just
                    // the associated GorgonTexture2DView won't cut it here, so you'll need to dispose the actual texture resource
                    // when you're done with it.
                    if (!_textures.Contains(texture))
                    {
                        _textures.Add(texture);
                    }

                    // At super duper resolution, the example graphics would be really hard to see, so we'll scale them up.
                    sprite.Scale       = new DX.Vector2((_screen.Width / (_screen.Height / 2)) * 2.0f);
                    sprites[file.Name] = sprite;
                }

                _snowTile       = sprites["Snow"];
                _snowTile.Depth = 0.5f;

                _icicle       = sprites["Icicle"];
                _icicle.Depth = 0.2f;

                _guySprite       = sprites["Guy_Up_0"];
                _guySprite.Depth = 0.1f;
                _guyPosition     = new DX.Vector2(_screen.Width / 2 + _guySprite.ScaledSize.Width * 1.25f, _screen.Height / 2 + _guySprite.ScaledSize.Height);

                BuildAnimations(sprites);
            }
            finally
            {
                GorgonExample.EndInit();
            }

            return(window);
        }