/// <summary> /// Function to returnthe files that are linked within the specified directory. /// </summary> /// <param name="directory">The directory to look up.</param> /// <returns>A list of files within the directory (or sub directories) that are linked to other files.</returns> public static IList <EditorFile> GetFileLinks(GorgonFileSystemDirectory directory) { var ownerFiles = new List <EditorFile>(); if (directory.Directories.Count > 0) { foreach (IList <EditorFile> subFiles in directory.Directories.Select(GetFileLinks).Where(subFiles => subFiles.Count > 0)) { ownerFiles.AddRange(subFiles); } } IEnumerable <EditorFile> editorFiles = from fileEntry in directory.Files where Files.Contains(fileEntry.FullPath) select Files[fileEntry.FullPath]; foreach (IList <EditorFile> files in editorFiles.Select(GetFileLinks).Where(files => files.Count > 0)) { ownerFiles.AddRange(files); } return(ownerFiles); }
/// <summary> /// Function to update the node with new directory information. /// </summary> /// <param name="newDir">New directory info.</param> public void UpdateNode(GorgonFileSystemDirectory newDir) { Name = newDir.FullPath; Text = newDir.Name; if ((Nodes.Count > 0) && (IsExpanded)) { Collapse(); } }
/// <summary> /// Function to add a directory to the tree. /// </summary> /// <param name="nodes">Source collection.</param> /// <param name="directory">Directory to add.</param> /// <param name="autoExpand">TRUE to expand the new node (if it has children) after it's been added, FALSE to leave collapsed.</param> /// <returns>The new node.</returns> public static TreeNodeDirectory AddDirectory(this TreeNodeCollection nodes, GorgonFileSystemDirectory directory, bool autoExpand) { if (directory == null) { throw new ArgumentNullException("directory"); } // Find check to ensure the node is unique. TreeNodeDirectory result = (from node in nodes.Cast <TreeNode>() let dirNode = node as TreeNodeDirectory where dirNode != null && string.Equals(node.Name, directory.FullPath, StringComparison.OrdinalIgnoreCase) select dirNode).FirstOrDefault(); if (result != null) { return(result); } result = new TreeNodeDirectory(directory) { ForeColor = Color.White, Text = directory.Name }; if ((directory.Directories.Count > 0) || (directory.Files.Count > 0)) { // Add a dummy node to indicate that there are children. result.Nodes.Add("DummyNode"); } nodes.Add(result); // Expand the parent. if ((result.Parent != null) && (!result.Parent.IsExpanded)) { TreeNode parent = result.Parent; parent.Expand(); result = (TreeNodeDirectory)parent.Nodes[result.Name]; } // Expand the node if necessary. if ((autoExpand) && (result.Nodes.Count > 0)) { result.Expand(); } return(result); }
/// <summary> /// Function to determine if the directory or its sub-directories contain files that are linked. /// </summary> /// <param name="directory">Directory to evaluate.</param> /// <returns>TRUE if the directory or sub directory contains file links, FALSE if not.</returns> public static bool HasFileLinks(GorgonFileSystemDirectory directory) { if ((directory.Directories.Count > 0) && (directory.Directories.Any(HasFileLinks))) { return(true); } return(directory.Files.Any(item => { EditorFile file; return Files.TryGetValue(item.FullPath, out file) && HasFileLinks(file); })); }
/// <summary> /// Function to enumerate the files and directories for a mount point. /// </summary> /// <param name="physicalMountPoint">Mount point being enumerated.</param> /// <param name="mountPoint">Directory to hold the sub directories and files.</param> /// <param name="physicalDirectories">A list of directories in the physical file system (formatted to the virtual file system).</param> /// <param name="physicalFiles">A list of files in the physical file system (formatted to the virtual file system).</param> protected override void Enumerate(string physicalMountPoint, GorgonFileSystemDirectory mountPoint, out string[] physicalDirectories, out PhysicalFileInfo[] physicalFiles) { using (var reader = new GorgonBinaryReader(File.Open(physicalMountPoint, FileMode.Open, FileAccess.Read, FileShare.Read), false)) { // Skip the header. reader.ReadString(); int indexLength = reader.ReadInt32(); byte[] indexData = Decompress(reader.ReadBytes(indexLength)); string xmlData = Encoding.UTF8.GetString(indexData); ParseIndexXML(physicalMountPoint, mountPoint, XDocument.Parse(xmlData, LoadOptions.None), reader.BaseStream.Position, out physicalDirectories, out physicalFiles); } }
/// <summary> /// Function to enumerate the files and directories for a mount point. /// </summary> /// <param name="physicalMountPoint">Path on the physical file system to enumerate.</param> /// <param name="mountPoint">Directory to hold the sub directories and files.</param> /// <param name="physicalDirectories">The directories in the physical file system.</param> /// <param name="physicalFiles">The files in the physical file system.</param> protected override void Enumerate(string physicalMountPoint, GorgonFileSystemDirectory mountPoint, out string[] physicalDirectories, out PhysicalFileInfo[] physicalFiles) { var directories = new List <string>(); var files = new List <PhysicalFileInfo>(); using (var zipStream = new ZipInputStream(File.Open(physicalMountPoint, FileMode.Open, FileAccess.Read, FileShare.Read))) { ZipEntry entry; while ((entry = zipStream.GetNextEntry()) != null) { if (!entry.IsDirectory) { string directoryName = Path.GetDirectoryName(entry.Name).FormatDirectory('/'); string fileName = Path.GetFileName(entry.Name).FormatFileName(); directoryName = mountPoint.FullPath + directoryName; if (string.IsNullOrWhiteSpace(directoryName)) { directoryName = "/"; } directories.Add(directoryName); files.Add(new PhysicalFileInfo(physicalMountPoint + "::/" + entry.Name, fileName, entry.DateTime, entry.Offset, entry.Size, directoryName + fileName)); } else { directories.Add(mountPoint.FullPath + entry.Name); } } } physicalDirectories = directories.ToArray(); physicalFiles = files.ToArray(); directories.Clear(); files.Clear(); }
/// <summary> /// Function to create a new path node. /// </summary> /// <param name="directory">Directory to retrieve information from.</param> /// <returns>A new node element with the directory information.</returns> private static XElement CreatePathNode(GorgonFileSystemDirectory directory) { return(new XElement("Path", new XAttribute("Name", (directory.Name == "/") ? @"\" : directory.Name), new XAttribute("FullPath", directory.FullPath.FormatDirectory(Path.DirectorySeparatorChar)))); }
/// <summary> /// Function to compress the files in a given directory. /// </summary> /// <param name="output">The output data stream.</param> /// <param name="directory">Directory containing the files to compress.</param> /// <param name="rootNode">Root node for the file allocation table.</param> private void CompressFiles(Stream output, GorgonFileSystemDirectory directory, XElement rootNode) { // If we have sub directories, then create those entries. foreach (var subDir in directory.Directories) { var dirNode = CreatePathNode(subDir); if (_token.IsCancellationRequested) { return; } CompressFiles(output, subDir, dirNode); if (_token.IsCancellationRequested) { return; } // Attach to the root. rootNode.Add(dirNode); } // Compress the files. foreach (var fileEntry in directory.Files) { // Load the file into a buffer for compression. long fileStart = output.Position; long fileSize; long compressedSize = 0; UpdateStatus(string.Format(Resources.GORPKW_SAVING_MSG, fileEntry.FullPath.Ellipses(45, true)), 0); if (_token.IsCancellationRequested) { return; } using (var sourceData = ScratchFileSystem.OpenStream(fileEntry, false)) { // Load the data into memory. using (var fileData = new GorgonDataStream((int)sourceData.Length)) { if (_token.IsCancellationRequested) { return; } sourceData.CopyTo(fileData); if (_token.IsCancellationRequested) { return; } fileSize = fileData.Length; fileData.Position = 0; using (var compressedData = new MemoryStream()) { if (_token.IsCancellationRequested) { return; } CompressData(fileData, compressedData); if (_token.IsCancellationRequested) { return; } compressedData.Position = 0; fileData.Position = 0; // Write the compressed data out to our blob file. if (compressedData.Length < fileSize) { if (_token.IsCancellationRequested) { return; } compressedData.CopyTo(output); if (_token.IsCancellationRequested) { return; } compressedSize = compressedData.Length; } else { if (_token.IsCancellationRequested) { return; } // We didn't compress anything, so just dump the file. fileData.CopyTo(output); if (_token.IsCancellationRequested) { return; } } } } } // Add to our directory. rootNode.Add(CreateFileNode(fileEntry, fileStart, fileSize, compressedSize)); } }
/// <summary> /// Initializes a new instance of the <see cref="TreeNodeDirectory"/> class. /// </summary> /// <param name="directory">The directory to associate with this node.</param> public TreeNodeDirectory(GorgonFileSystemDirectory directory) { Name = directory.FullPath; ExpandedImage = APIResources.folder_open_16x16; CollapsedImage = APIResources.folder_16x16; }
/// <summary> /// Function to parse the XML directory index. /// </summary> /// <param name="physicalPath">Path to the packed file.</param> /// <param name="mountPoint">Directory to hold the sub directories and files.</param> /// <param name="index">Index to parse.</param> /// <param name="physicalOffset">Offset in the file to add to the file offsets.</param> /// <param name="physicalDirectories">The list of directories in the physical file system.</param> /// <param name="physicalFiles">The list of files in the physical file system.</param> private void ParseIndexXML(string physicalPath, GorgonFileSystemDirectory mountPoint, XContainer index, long physicalOffset, out string[] physicalDirectories, out PhysicalFileInfo[] physicalFiles) { var dirNodeNames = new List <string>(); var fileNodeInfo = new List <PhysicalFileInfo>(); var directories = index.Descendants("Path"); _compressedFiles = new Dictionary <string, CompressedFileEntry>(); foreach (var directoryNode in directories) { var pathAttrib = directoryNode.Attribute("FullPath"); if ((pathAttrib == null) || (string.IsNullOrWhiteSpace(pathAttrib.Value))) { throw new FileLoadException(Resources.GORFS_FILEINDEX_CORRUPT); } string path = (mountPoint.FullPath + pathAttrib.Value).FormatDirectory('/'); var files = directoryNode.Elements("File"); // Add the directory. dirNodeNames.Add(path); foreach (var file in files) { var fileNameNode = file.Element("Filename"); var fileExtensionNode = file.Element("Extension"); var fileOffsetNode = file.Element("Offset"); var fileCompressedSizeNode = file.Element("CompressedSize"); var fileSizeNode = file.Element("Size"); var fileDateNode = file.Element("FileDate"); // We need these nodes. if ((fileNameNode == null) || (fileOffsetNode == null) || (fileSizeNode == null) || (fileDateNode == null) || (string.IsNullOrWhiteSpace(fileNameNode.Value)) || (string.IsNullOrWhiteSpace(fileDateNode.Value))) { throw new FileLoadException(Resources.GORFS_FILEINDEX_CORRUPT); } string fileName = fileNameNode.Value; long fileOffset; long fileSize; DateTime fileDate; // If we don't have a creation date, then don't allow the file to be processed. if (!DateTime.TryParse(fileDateNode.Value, CultureInfo.InvariantCulture, DateTimeStyles.None, out fileDate)) { throw new FileLoadException(Resources.GORFS_FILEINDEX_CORRUPT); } if (!Int64.TryParse(fileOffsetNode.Value, out fileOffset)) { throw new FileLoadException(Resources.GORFS_FILEINDEX_CORRUPT); } fileOffset += physicalOffset; if (!Int64.TryParse(fileSizeNode.Value, out fileSize)) { throw new FileLoadException(Resources.GORFS_FILEINDEX_CORRUPT); } if (fileExtensionNode != null) { string fileExtension = fileExtensionNode.Value; if ((!string.IsNullOrWhiteSpace(fileExtension)) && (!string.IsNullOrWhiteSpace(fileName))) { fileName += fileExtension; } } // If the file is compressed, then add it to a special list. if (fileCompressedSizeNode != null) { long compressedSize; if (!Int64.TryParse(fileCompressedSizeNode.Value, out compressedSize)) { throw new FileLoadException(Resources.GORFS_FILEINDEX_CORRUPT); } if (compressedSize > 0) { // Add to our list of compressed files for processing later. _compressedFiles.Add(path + fileName, new CompressedFileEntry(fileSize, compressedSize)); } } fileNodeInfo.Add(new PhysicalFileInfo(physicalPath + "::/" + path + fileName, fileName, fileDate, fileOffset, fileSize, path + fileName)); } } physicalDirectories = dirNodeNames.ToArray(); physicalFiles = fileNodeInfo.ToArray(); dirNodeNames.Clear(); fileNodeInfo.Clear(); }
/// <summary> /// Function to fill the file system tree view. /// </summary> /// <param name="directory">Parent directory to fill, or NULL (Nothing in VB.Net) to fill the root directory.</param> private void FillTree(GorgonFileSystemDirectory directory) { TreeNodeCollection nodes; TreeNode parentNode; if (directory == null) { directory = _fileSystem.RootDirectory; nodes = treeFileSystem.Nodes; // Set root node. parentNode = new TreeNode("/") { Name = "/" }; parentNode.SelectedImageIndex = parentNode.ImageIndex = 0; } else { // Find the node with the directory. var searchNodes = treeFileSystem.Nodes.Find(directory.FullPath, true); if (searchNodes.Length > 0) { parentNode = searchNodes[0]; nodes = parentNode.Nodes; } else { GorgonDialogs.ErrorBox(this, "Could not find the virtual directory '" + directory.FullPath + "'"); return; } } parentNode.Tag = directory; // Turn off the expand event. treeFileSystem.BeforeExpand -= treeFileSystem_BeforeExpand; try { // Clean up the tree node. treeFileSystem.BeginUpdate(); nodes.Clear(); // Add the root node if necessary. if (parentNode.Tag == _fileSystem.RootDirectory) { nodes.Add(parentNode); } // Enumerate the data. For the purposed of this example, we will filter out known binary files from our file system. var directories = directory.Directories.OrderBy(item => item.Name); var files = directory.Files.OrderBy(item => item.Name).Where(item => item.Extension != ".gorSprite" && item.Extension != ".gal"); // Get directories. foreach (var subDirectory in directories) { var directoryNode = new TreeNode(subDirectory.Name) { Name = subDirectory.FullPath, Tag = subDirectory }; // Put a special icon on the zip file so we have a visual representation // of where it is in our VFS. // The VFS does not care if the data is in a zip file or folder, and Gorgon // does very little to differentiate it. After all, the whole point of // have a unified file system is to abstract away the differences. if (subDirectory.Name != "ZipFile") { directoryNode.SelectedImageIndex = directoryNode.ImageIndex = 0; } else { directoryNode.SelectedImageIndex = directoryNode.ImageIndex = 2; } // Add a dummy node if there are files or sub directories. if ((subDirectory.Directories.Count > 0) || (subDirectory.Files.Count(item => item.Extension != ".gorSprite" && item.Extension != ".gal") > 0)) { directoryNode.Nodes.Add(new TreeNode("This is a dummy node.")); } parentNode.Nodes.Add(directoryNode); } // Get files. foreach (var file in files) { if (file.Extension == ".gorSprite") { continue; } var fileNode = new TreeNode(file.Name) { Name = file.FullPath, Tag = file }; fileNode.SelectedImageIndex = fileNode.ImageIndex = 1; parentNode.Nodes.Add(fileNode); } parentNode.Expand(); } finally { treeFileSystem.EndUpdate(); treeFileSystem.BeforeExpand += treeFileSystem_BeforeExpand; } }
/// <summary> /// The main entry point for the application. /// </summary> static void Main() { try { Console.WindowHeight = 28; Console.BufferHeight = Console.WindowHeight; // Create a new file system. _fileSystem = new GorgonFileSystem(); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("This example will mount a zip file as the root of a virtual file system. The"); Console.WriteLine("virtual file system is capable of mounting various types of data such as a zip,"); Console.WriteLine("a file system folder, etc... as the root or a sub directory. You can even"); Console.WriteLine("mount a zip file as the root, and a physical file system directory as a virtual"); Console.WriteLine("sub directory in the same virtual file system and access them as a single"); Console.WriteLine("unified file system."); // Unlike the folder file system example, we need to load // a provider to handle zip files before trying to mount // one. if (!LoadZipProviderPlugIn()) { return; } // Set the following zip file as root on the virtual file system. // // If we wanted to, we could mount a zip file as a sub directory of // the root of the virtual file system. For example, mounting the // directory D:\Dir\zipFile.zip with Mount(@"D:\Dir", "/VFSDir"); would mount // the contents of the D:\Dir\zipFile.zip directory under /VFSDir. // // It's also important to point out that the old Gorgon "file system" // would load files from the system into memory when mounting a // directory. While this version only loads directory and file // information when mounting. This is considerably more efficient. var physicalPath = GetResourcePath(@"FileSystem.zip"); _fileSystem.Mount(physicalPath); Console.Write("\nMounted: "); Console.ForegroundColor = ConsoleColor.Cyan; Console.Write("'{0}'", physicalPath.Ellipses(Console.WindowWidth - 20, true)); Console.ForegroundColor = ConsoleColor.White; Console.Write(" as "); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("'/'\n"); Console.ForegroundColor = ConsoleColor.White; // Get a count of all sub directories and files under the root directory. var directoryList = _fileSystem.FindDirectories("*", true).ToArray(); // Display directories. Console.WriteLine("Virtual file system contents:"); for (int i = -1; i < directoryList.Length; i++) { GorgonFileSystemDirectory directory = _fileSystem.RootDirectory; // Go into the sub directories under root. if (i > -1) { directory = directoryList[i]; } Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("{0}", directory.FullPath); Console.ForegroundColor = ConsoleColor.Yellow; foreach (var file in directory.Files) { Console.Write(" {0}", file.Name); // Align the size to the same place. Console.CursorLeft = 65; Console.WriteLine("{0}", file.Size.FormatMemory()); } } Console.ResetColor(); Console.WriteLine("\nPress any key to close."); Console.ReadKey(); } catch (Exception ex) { // Catch all exceptions here. If we had logging for the application enabled, then this // would record the exception in the log. GorgonException.Catch(ex, () => { Console.Clear(); Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Exception:\n{0}\n\nStack Trace:{1}", ex.Message, ex.StackTrace); Console.ResetColor(); #if DEBUG Console.ReadKey(); #endif }); } }