예제 #1
0
        /// <summary>
        /// Unpacks all the files within the volume.
        /// </summary>
        public void UnpackFiles(IEnumerable <int> fileIndexesToExtract)
        {
            FileEntryBTree rootEntries = new FileEntryBTree(this.TableOfContents.Data, (int)TableOfContents.RootAndFolderOffsets[0]);

            var unpacker = new EntryUnpacker(this, OutputDirectory, "", fileIndexesToExtract.ToList());

            rootEntries.TraverseAndUnpack(unpacker);
        }
예제 #2
0
        public void UnpackFromKey(FileEntryKey entryKey)
        {
            string entryPath = _volume.GetEntryPath(entryKey, ParentDirectory);

            if (string.IsNullOrEmpty(entryPath))
            {
                Program.Log($"Could not determine entry path for Entry key at name Index {entryKey.NameIndex}");
                return;
            }

            string fullEntryPath = Path.Combine(OutDir ?? string.Empty, entryPath);

            if (entryKey.Flags.HasFlag(EntryKeyFlags.Directory))
            {
                if (_fileIndexesToExtract.Count == 0) // Make sure not to spam when not needed
                {
                    if (!_volume.IsPatchVolume || _volume.NoUnpack)
                    {
                        Program.Log($"[:] Entering Directory: {entryPath}");
                    }
                }

                var childEntryBTree = new FileEntryBTree(_volume.TableOfContents.Data, (int)_volume.TableOfContents.RootAndFolderOffsets[(int)entryKey.EntryIndex]);
                var childUnpacker   = new EntryUnpacker(_volume, OutDir, entryPath, _fileIndexesToExtract);
                childEntryBTree.TraverseAndUnpack(childUnpacker);
            }
            else
            {
                if (_fileIndexesToExtract.Count != 0 && !_fileIndexesToExtract.Contains((int)entryKey.EntryIndex))
                {
                    return;
                }

                if (!_volume.IsPatchVolume || _volume.NoUnpack)
                {
                    Program.Log($"[:] Extracting: {entryPath}");
                }

                var nodeBTree = new FileInfoBTree(_volume.TableOfContents.Data, (int)_volume.TableOfContents.NodeTreeOffset);
                var nodeKey   = new FileInfoKey(entryKey.EntryIndex);

                uint nodeIndex = nodeBTree.SearchIndexByKey(nodeKey);

                if (nodeIndex != FileInfoKey.InvalidIndex)
                {
                    _volume.UnpackNode(nodeKey, fullEntryPath);
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Serializes the table of contents and its B-Trees.
        /// </summary>
        public byte[] Serialize()
        {
            using var ms = new MemoryStream();
            using var bs = new BinaryStream(ms, ByteConverter.Big);

            bs.Write(SEGMENT_MAGIC);
            bs.Position = 16;
            bs.WriteUInt32((uint)Files.Count);
            bs.Position += sizeof(uint) * Files.Count;

            uint fileNamesOffset = (uint)bs.Position;

            FileNames.Serialize(bs);

            uint extOffset = (uint)bs.Position;

            Extensions.Serialize(bs);

            uint fileInfoOffset = (uint)bs.Position;

            FileInfos.Serialize(bs);

            // The list of file entry btrees mostly consist of the relation between files, folder, extensions and data
            // Thus it is writen at the end
            // Each tree is a seperate general subdir
            const int baseListPos = 20;

            for (int i = 0; i < Files.Count; i++)
            {
                FileEntryBTree f          = Files[i];
                uint           treeOffset = (uint)bs.Position;
                bs.Position = baseListPos + (i * sizeof(uint));
                bs.WriteUInt32(treeOffset);
                bs.Position = treeOffset;
                f.Serialize(bs, (uint)FileNames.Entries.Count, (uint)Extensions.Entries.Count);
            }

            // Go back to write the meta data
            bs.Position = 4;
            bs.WriteUInt32(fileNamesOffset);
            bs.WriteUInt32(extOffset);
            bs.WriteUInt32(fileInfoOffset);
            return(ms.ToArray());
        }
예제 #4
0
        /// <summary>
        /// Modifies a current key as a new entry.
        /// </summary>
        /// <param name="infoKey">Entry to modify.</param>
        /// <param name="newEntryPath">Path for the entry.</param>
        /// <returns></returns>
        private FileInfoKey ModifyExistingEntryAsNew(FileInfoKey infoKey, string newEntryPath)
        {
            // Check paths
            string[]       pathParts      = newEntryPath.Split(Path.AltDirectorySeparatorChar);
            FileEntryBTree currentSubTree = Files[0];

            uint newKeyIndex = NextEntryIndex();

            // Find the entry key and update it
            for (int i = 0; i < pathParts.Length; i++)
            {
                if (i != pathParts.Length - 1)
                {
                    // Check actual folders
                    int keyIndex = FileNames.GetIndexOfString(pathParts[i]);
                    if (keyIndex == -1)
                    {
                        throw new ArgumentNullException($"Entry Key for file info key ({infoKey}) has missing file name key: {pathParts[i]}");
                    }

                    FileEntryKey subTreeKey = currentSubTree.GetFolderEntryByNameIndex((uint)keyIndex);

                    if (subTreeKey is null)
                    {
                        throw new InvalidOperationException($"Tried to modify existing key {newEntryPath} (str index: {keyIndex}), but missing in entries");
                    }
                    else if (!subTreeKey.Flags.HasFlag(EntryKeyFlags.Directory))
                    {
                        throw new InvalidOperationException($"Tried to modify existing key {newEntryPath} but entry key ({subTreeKey}) is not marked as directory. Is the volume corrupted?");
                    }

                    currentSubTree = Files[(int)subTreeKey.EntryIndex];
                }
                else
                {
                    // Got the location for the subtree

                    // Get our actual file entry key
                    FileEntryKey entryKey = currentSubTree.Entries.FirstOrDefault(e => e.EntryIndex == infoKey.FileIndex);
                    if (entryKey is null)
                    {
                        throw new ArgumentNullException($"Entry Key for file info key ({infoKey}) is missing while modifying.");
                    }

                    // Update it actually
                    entryKey.EntryIndex = newKeyIndex;
                }
            }

            // Find the original entry key, copy from it, add to the tree
            foreach (FileEntryBTree tree in Files)
            {
                foreach (FileEntryKey child in tree.Entries)
                {
                    if (child.EntryIndex == infoKey.FileIndex) // If the entry key exists, add it
                    {
                        var fileInfo = new FileInfoKey(newKeyIndex);
                        fileInfo.CompressedSize   = infoKey.CompressedSize;
                        fileInfo.UncompressedSize = infoKey.UncompressedSize;
                        fileInfo.SegmentIndex     = NextSegmentIndex(); // Pushed to the end, so technically the segment is new, will be readjusted at the end anyway
                        fileInfo.Flags            = infoKey.Flags;
                        FileInfos.Entries.Add(fileInfo);
                        return(fileInfo);
                    }
                }
            }

            // If it wasn't found, then we already have it
            infoKey.FileIndex = newKeyIndex;

            // Move it to the last
            FileInfos.Entries.Remove(infoKey);
            FileInfos.Entries.Add(infoKey);
            return(infoKey);
        }