/// <summary> /// Function to retrieve a plug in list item view model based on the plug in passed in. /// </summary> /// <param name="plugin">The plug in to retrieve data from.</param> /// <returns>The view model.</returns> private ISettingsPlugInListItem GetPlugInListItem(IGorgonFileSystemProvider plugin) { var result = new SettingsPlugInListItem(); result.Initialize(new SettingsPlugInListItemParameters(plugin, _viewModelInjection)); return(result); }
/// <summary> /// Function to find the most suitable provider for the file specified in the path. /// </summary> /// <param name="file">The file to evaluate.</param> /// <returns>The best suitable provider, or <b>null</b> if none could be located.</returns> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="file"/> parameter is <b>null</b>.</exception> public IGorgonFileSystemProvider GetBestReader(FileInfo file) { if (file == null) { throw new ArgumentNullException(nameof(file)); } // First, try to locate by extension. IGorgonFileSystemProvider result = _readers.Select(item => item.Value) .FirstOrDefault(item => item.PreferredExtensions.Contains(file.Extension)); // No provider is registered with that extension, fall back to trying to read each file. if (result == null) { return(GetBestFit(file)); } else { if (!result.CanReadFileSystem(file.FullName)) { return(GetBestFit(file)); } } return(result); }
/// <summary> /// Function to load in the file system for the application. /// </summary> /// <param name="path">The path to the file system.</param> public void Load(string path) { // 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"); _fileSystem = new GorgonFileSystem(provider, GorgonApplication.Log); _fileSystem.Mount(path); }
/// <summary>Initializes a new instance of the <see cref="SettingsPlugInListItemParameters"/> class.</summary> /// <param name="plugin">The plugin to evaluate.</param> /// <param name="commonServices">Common application services.</param> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="plugin"/> is parameter <b>null</b>.</exception> public SettingsPlugInListItemParameters(IGorgonFileSystemProvider plugin, IViewModelInjection commonServices) : base(commonServices) { if (plugin == null) { throw new ArgumentNullException(nameof(plugin)); } Name = string.IsNullOrWhiteSpace(plugin.Description) ? plugin.Name : plugin.Description; Type = PlugInType.Reader; State = Resources.GOREDIT_PLUGIN_STATE_LOADED; DisabledReason = string.Empty; Path = plugin.ProviderPath; }
/// <summary> /// Function to open a project from a file on the disk. /// </summary> /// <param name="path">The path to the project file.</param> /// <param name="providers">The providers used to read the project file.</param> /// <param name="workspace">The workspace directory that will receive the files from the project file.</param> /// <returns>A task for asynchronous operation.</returns> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="path"/>, or the <paramref name="workspace"/> parameter is <b>null</b>.</exception> /// <exception cref="FileNotFoundException">Thrown if the file specified by the <paramref name="path"/> does not exist.</exception> /// <exception cref="GorgonException">Thrown if no provider could be found to load the file.</exception> public async Task OpenPackFileProjectAsync(FileInfo path, DirectoryInfo workspace) { if (path == null) { throw new ArgumentNullException(nameof(path)); } if (workspace == null) { throw new ArgumentNullException(nameof(workspace)); } if (!path.Exists) { throw new FileNotFoundException(string.Format(Resources.GOREDIT_ERR_PROJECT_NOT_FOUND, path.FullName)); } IGorgonFileSystemProvider provider = Providers.GetBestReader(path); if (provider == null) { throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_NO_PROVIDER, path.Name)); } // Unlock the directory if we have it locked. // Unlock our own lock prior to opening. Unlock(workspace); if (IsDirectoryLocked(workspace)) { throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_PROJECT_OPEN_LOCKED, workspace.FullName)); } if (workspace.Exists) { PurgeStaleDirectories(workspace, true); } (DirectoryInfo _, DirectoryInfo fsDir, DirectoryInfo tempDir, DirectoryInfo srcDir) = SetupProjectFolders(workspace.FullName); try { FileInfo metaData = await CopyFileSystemAsync(path, provider, fsDir); // Pull the meta data file into the root of the project directory. if (metaData.Exists) { metaData.MoveTo(Path.Combine(workspace.FullName, metaData.Name)); return; } Program.Log.Print("No metadata file exists. A new one will be created.", LoggingLevel.Verbose); // Create a dummy project, so we have something to serialize. var dummyProject = new Project(workspace, tempDir, fsDir, srcDir); // If we have v2 meatdata, upgrade the file. var v2Metadata = new FileInfo(Path.Combine(workspace.FullName, V2MetadataImporter.V2MetadataFilename)); if (!v2Metadata.Exists) { metaData = new FileInfo(Path.Combine(workspace.FullName, metaData.Name)); BuildMetadataDatabase(dummyProject, metaData); return; } var importer = new V2MetadataImporter(v2Metadata); importer.Import(dummyProject); PersistMetadata(dummyProject, CancellationToken.None); } finally { // For the file import, we will unlock the directory once we're done writing into it. // The open project functionality will re-establish the lock after this method completes. Unlock(workspace); } }
/// <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 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); }