Esempio n. 1
0
 public PakNode(Node parent, string name, PakFile pak, PakDirectory directory)
 {
     _parent       = parent;
     _name         = name;
     _pak          = pak;
     _pakDirectory = directory;
 }
Esempio n. 2
0
        private static PakDirectory ConstructDirectoryTree(BinaryReader reader)
        {
            var name    = reader.ReadString();
            var fname   = reader.ReadString();
            var entType = (PakDirectoryType)reader.ReadByte();

            var dir = new PakDirectory
            {
                Name          = name,
                FileName      = fname,
                DirectoryType = entType
            };

            if (entType == PakDirectoryType.File)
            {
                dir.DataLength = reader.ReadInt64();
                dir.DataStart  = reader.ReadInt64();
            }
            else
            {
                var childrenCount = reader.ReadInt32();

                for (var i = 0; i < childrenCount; i++)
                {
                    var child = ConstructDirectoryTree(reader);
                    dir.Children.Add(child);
                }
            }

            return(dir);
        }
Esempio n. 3
0
        public Stream LoadData(PakDirectory directory)
        {
            if (directory == null)
            {
                throw new ArgumentNullException(nameof(directory));
            }

            if (directory.DirectoryType != PakDirectoryType.File)
            {
                throw new InvalidOperationException("Cannot load bitstream data of a PakFile folder.");
            }

            _pakStream.Seek(_dataStart + directory.DataStart, SeekOrigin.Begin);

            var memStream = new MemoryStream();

            var dataLen = directory.DataLength;
            var block   = new byte[2048];

            while (dataLen > 0)
            {
                var readCount = (int)Math.Min(block.Length, dataLen);
                _pakStream.Read(block, 0, readCount);
                memStream.Write(block, 0, readCount);
                dataLen -= readCount;
            }

            memStream.Seek(0, SeekOrigin.Begin);

            return(memStream);
        }
Esempio n. 4
0
 public PakFile(string file, Stream stream, PakDirectory directoryTree, long dataStart)
 {
     _dataStart     = dataStart;
     _filePath      = file;
     _pakStream     = stream;
     _directoryTree = directoryTree;
 }
Esempio n. 5
0
        private static void WriteDirectoryData(PakDirectory directory, BinaryWriter writer)
        {
            writer.Write(directory.Name);
            writer.Write(directory.FileName);
            writer.Write((byte)directory.DirectoryType);

            if (directory.DirectoryType == PakDirectoryType.File)
            {
                writer.Write(directory.DataLength);
                writer.Write(directory.DataStart);
            }
            else
            {
                writer.Write(directory.Children.Count);
                foreach (var child in directory.Children)
                {
                    WriteDirectoryData(child, writer);
                }
            }
        }
Esempio n. 6
0
        public static void MakePak(string sourceDirectory, string pakDestination)
        {
            // Check to make sure the source directory exists.
            if (!Directory.Exists(sourceDirectory))
            {
                throw new InvalidOperationException("Cannot make a Pak file out of that directory.",
                                                    new DirectoryNotFoundException(sourceDirectory));
            }

            // Open the destination file.
            Logger.Log("Opening new Pak file: " + pakDestination);
            using var file = File.OpenWrite(pakDestination);

            // Create the root directory node.
            Logger.Log("Creating root Pak Directory...");
            var rootDirectory = new PakDirectory
            {
                Name          = "/",
                FileName      = "/",
                DirectoryType = PakDirectoryType.Root
            };

            // Memory stream for file contents.
            Logger.Log("Allocating memory for temporary PakFile bitstream...");
            using (var pakStream = File.OpenWrite(pakDestination + ".raw"))
            {
                // Recurse through the file system and build the pak directory tree. When
                // this method returns, memStream will be filled with a contiguous blob of
                // every single file's contents.
                Logger.Log("Gathering PAK data...");
                GatherPakContents(rootDirectory, pakStream, sourceDirectory);
                Logger.Log("Done gathering Pak data..");
            }

            // Now we can start writing the pak file.
            // Let's use BinaryWriter to help.
            Logger.Log("Preparing to write PakFile to disk...");
            using var writer = new BinaryWriter(file, Encoding.UTF8);

            // Start by writing the header.
            Logger.Log("Writing Pak header...");
            writer.Write(PakMagic);

            // Now we can write the version code. We'll use 1.0 for now.
            Logger.Log("Writing Pak version...");
            writer.Write(1);

            // Use BinaryPack to serialize the directory tree.
            using (var memStream = new MemoryStream())
            {
                Logger.Log("Serializing directory structure...");
                using (var hWriter = new BinaryWriter(memStream, Encoding.UTF8, true))
                {
                    WriteDirectoryData(rootDirectory, hWriter);
                }

                memStream.Seek(0, SeekOrigin.Begin);

                // Write the stream length so we know how long the tree is.
                Logger.Log($"Directory structure data is {memStream.Length} bytes long.");
                writer.Write(memStream.Length);

                // CopyTo will help us out here.
                Logger.Log("Writing directory structure information to PakFile...");
                memStream.CopyTo(file);
            }

            // The engine will reconstruct the directory tree when mounting the Pak file.
            // The directory tree contains the relative start points of files' data and their byte lengths
            // in the Pak file.
            //
            // The engine will calculate the absolute start of the file data based on the size of the pak
            // header. We don't need to write it.
            //
            // So now we can just write the pak file contents.
            using (var pakStream = File.OpenRead(pakDestination + ".raw"))
            {
                Logger.Log($"PakFile data is {pakStream.Length} bytes in size. Writing to disk...");
                pakStream.CopyTo(file);
            }

            // Clean-up
            Logger.Log("Cleaning up...");
            File.Delete(pakDestination + ".raw");

            // And we're done.
            Logger.Log($"PakFile {pakDestination} - Written successfully!", LogLevel.Message);
        }
Esempio n. 7
0
        private static void GatherPakContents(PakDirectory folderOrRoot, Stream pakData, string sourceDirectory)
        {
            // Go through each file in the source directory.
            Logger.Log($"Gathering files in {sourceDirectory}...");
            foreach (var file in Directory.GetFiles(sourceDirectory))
            {
                // Open the file so we can read its data.
                Logger.Log("Opening " + file + "...");
                var fileStream = File.OpenRead(file);

                // Get the file name we're going to use for the directory entry.
                // We're going to omit the extension in the Pak file for consistency between
                // the old MGCB content system and the Pak content system. Less legacy code to
                // rewrite that way.
                Logger.Log($"Importing {file}...");
                var fname         = Path.GetFileNameWithoutExtension(file);
                var withExtension = Path.GetFileName(file);

                Logger.Log(" >>> File Name: " + fname);

                if (string.IsNullOrWhiteSpace(fname))
                {
                    Logger.Log("DOTFILE: Skipping an obvious dotfile, these are not supported by the PakFS.", LogLevel.Warning);
                    continue;
                }

                // File length. We'll need this.
                var fileLength = fileStream.Length;
                Logger.Log($" >>> Data Length: {fileLength} bytes");

                // Current position of the pak data stream.
                var dataStart = pakData.Position;
                Logger.Log($" >>> Pak Start: d+{dataStart} bytes");

                // Create the file entry. We have all the info we need for that.
                Logger.Log("Creating file entry...");
                var fileEntry = new PakDirectory
                {
                    Name          = fname,
                    FileName      = withExtension,
                    DirectoryType = PakDirectoryType.File,
                    DataStart     = dataStart,
                    DataLength    = fileLength
                };

                // Add the child to its parent folder.
                folderOrRoot.Children.Add(fileEntry);
                Logger.Log("File entry added to Pak directory.");

                // Now let's start reading the data from the file into the pak data stream.
                // I would use CopyTo for this but doing it manually means we can log the progress.
                Logger.Log("Preparing to copy file data into PakFile...");
                var block        = new byte[8 * 1024 * 1024];
                var lastProgress = -1d;
                while (fileLength > 0)
                {
                    var progress  = Math.Round((fileStream.Position / (float)fileStream.Length) * 100);
                    var readCount = (int)Math.Min(fileLength, block.Length);

                    if (lastProgress < progress)
                    {
                        Logger.Log(
                            $" >>> Progress {progress}% - Written: {fileStream.Position} - Left: {fileLength} - Block size: {block.Length} - Next read: {readCount}");
                        lastProgress = progress;
                    }

                    fileStream.Read(block, 0, readCount);
                    pakData.Write(block, 0, readCount);
                    fileLength -= readCount;
                }

                Logger.Log(" >>> Done.");
            }

            Logger.Log("Files imported successfully.");

            // Create child folders and gather their contents.
            Logger.Log("Gathering directories in " + sourceDirectory);
            foreach (var childFolder in Directory.GetDirectories(sourceDirectory))
            {
                // Create the child entry with matching file name.
                Logger.Log("Creating child directory entry for " + childFolder);
                var dname = Path.GetFileName(childFolder);

                if (string.IsNullOrWhiteSpace(dname))
                {
                    Logger.Log("DOTFILE: This directory is a dotfile, skipping.", LogLevel.Warning);
                    continue;
                }

                var childEntry = new PakDirectory
                {
                    Name          = dname,
                    FileName      = dname,
                    DirectoryType = PakDirectoryType.Folder
                };

                // Add child to its parent directory.
                Logger.Log("Added child directory " + dname + " to parent directory " + folderOrRoot.Name);
                folderOrRoot.Children.Add(childEntry);

                // Gather the child's pak contents.
                Logger.Log("Gathering child directory data...");
                GatherPakContents(childEntry, pakData, childFolder);
            }
        }