Пример #1
0
        public string GetEntryPath(FileEntryKey key, string prefix)
        {
            string      entryPath = prefix;
            StringBTree nameBTree = new StringBTree(TableOfContents.Data, (int)TableOfContents.NameTreeOffset);

            if (nameBTree.TryFindIndex(key.NameIndex, out StringKey nameKey))
            {
                entryPath += nameKey.Value;
            }

            if (key.Flags.HasFlag(EntryKeyFlags.File))
            {
                // If it's a file, find the extension aswell
                StringBTree extBTree = new StringBTree(TableOfContents.Data, (int)TableOfContents.FileExtensionTreeOffset);

                if (extBTree.TryFindIndex(key.FileExtensionIndex, out StringKey extKey) && !string.IsNullOrEmpty(extKey.Value))
                {
                    entryPath += extKey.Value;
                }
            }
            else if (key.Flags.HasFlag(EntryKeyFlags.Directory))
            {
                entryPath += '/';
            }

            lastEntryPath = entryPath;

            return(entryPath);
        }
Пример #2
0
        /// <summary>
        /// Registers a file (not a path).
        /// </summary>
        /// <param name="entryIndex"></param>
        /// <param name="name"></param>
        /// <param name="extension"></param>
        public void RegisterFile(uint entryIndex, string name, string extension)
        {
            uint nameIndex = RegisterFilename(name);
            uint extIndex  = RegisterExtension(extension); // No extension is also an entry

            var newEntry = new FileEntryKey();

            if (!string.IsNullOrEmpty(extension))
            {
                newEntry.Flags = EntryKeyFlags.File;
            }
            newEntry.NameIndex          = nameIndex;
            newEntry.FileExtensionIndex = extIndex;
            newEntry.EntryIndex         = FileInfos.Entries[^ 1].FileIndex;
Пример #3
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);
                }
            }
        }
Пример #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);
        }
Пример #5
0
        private void PackFile(PackCache packCache, string outputDir, bool packAllAsNewEntries, PackCache newCache, FileEntryKey tocFile, InputPackEntry file)
        {
            Program.Log($"[:] Pack: Processing {file.VolumeDirPath}");
            FileInfoKey key = FileInfos.GetByFileIndex(tocFile.EntryIndex);

            if (packAllAsNewEntries && !file.IsAddedFile)
            {
                uint oldEntryFileIndex = key.FileIndex;
                key = ModifyExistingEntryAsNew(key, file.VolumeDirPath);
                Program.Log($"[:] Entry key for {file.VolumeDirPath} changed as new: {oldEntryFileIndex} -> {key.FileIndex}");
            }

            uint   newUncompressedSize = (uint)file.FileSize;
            uint   newCompressedSize   = (uint)file.FileSize;
            string pfsFilePath         = PDIPFSPathResolver.GetPathFromSeed(tocFile.EntryIndex);

            // Check for cached file
            if (ParentVolume.UsePackingCache && packCache.HasValidCachedEntry(file, key.FileIndex, out PackedCacheEntry validCacheEntry))
            {
                string oldFilePath = Path.Combine(outputDir, pfsFilePath);
                if (File.Exists(oldFilePath))
                {
                    newCache.Entries.Add(file.VolumeDirPath, validCacheEntry);
                    Program.Log($"[:] Pack: {file.VolumeDirPath} found in cache file, does not need compressing/encrypting");

                    string movePath = Path.Combine($"{outputDir}_temp", pfsFilePath);
                    Directory.CreateDirectory(Path.GetDirectoryName(movePath));
                    File.Move(oldFilePath, Path.Combine($"{outputDir}_temp", pfsFilePath));
                    UpdateKeyAndRetroactiveAdjustSegments(key, (uint)validCacheEntry.CompressedFileSize, (uint)validCacheEntry.FileSize);
                    return;
                }
                else
                {
                    Program.Log($"[:] Pack: {file.VolumeDirPath} found in cache file but actual file is missing ({pfsFilePath}) - recreating it");
                }
            }

            byte[] fileData = File.ReadAllBytes(file.FullPath);
            if (ParentVolume.NoCompress)
            {
                key.Flags &= ~FileInfoFlags.Compressed;
            }
            else if (key.Flags.HasFlag(FileInfoFlags.Compressed))
            {
                Program.Log($"[:] Pack: Compressing {file.VolumeDirPath}");
                fileData          = MiscUtils.ZlibCompress(fileData);
                newCompressedSize = (uint)fileData.Length;
            }

            Program.Log($"[:] Pack: Saving and encrypting {file.VolumeDirPath} -> {pfsFilePath}");

            // Will also update the ones we pre-registered
            UpdateKeyAndRetroactiveAdjustSegments(key, newCompressedSize, newUncompressedSize);
            ParentVolume.Keyset.CryptBytes(fileData, fileData, key.FileIndex);

            string outputFile = Path.Combine($"{outputDir}_temp", pfsFilePath);

            Directory.CreateDirectory(Path.GetDirectoryName(outputFile));

            File.WriteAllBytes(outputFile, fileData);

            if (ParentVolume.UsePackingCache)
            {
                // Add to our new cache
                var newCacheEntry = new PackedCacheEntry()
                {
                    FileIndex          = tocFile.EntryIndex,
                    FileSize           = newUncompressedSize,
                    LastModified       = file.LastModified,
                    VolumePath         = file.VolumeDirPath,
                    CompressedFileSize = newCompressedSize,
                };

                newCache.Entries.Add(file.VolumeDirPath, newCacheEntry);
            }
        }