/// <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; } }
/// <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 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(); } }
/// <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 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); }