Beispiel #1
0
        /// <summary>
        /// Pack all provided files and edit the table of contents accordingly.
        /// </summary>
        /// <param name="FilesToPack">Files to pack.</param>
        /// <param name="outputDir">Main output dir to use to expose the packed files.</param>
        public PackCache PackFilesForPatchFileSystem(Dictionary <string, InputPackEntry> FilesToPack, PackCache packCache, string[] filesToRemove, string outputDir, bool packAllAsNewEntries)
        {
            // If we are packing as new, ensure the TOC is before all the files (that will come after it)
            if (packAllAsNewEntries)
            {
                ParentHeader.TOCEntryIndex = NextEntryIndex();
            }

            if (filesToRemove.Length > 0)
            {
                RemoveFilesFromTOC(filesToRemove);
            }

            var newCache = new PackCache();

            if (FilesToPack.Count > 0)
            {
                // Pick up files we're going to add if there's any
                PreRegisterNewFilesToPack(FilesToPack);

                Dictionary <string, FileEntryKey> tocFiles = GetAllRegisteredFileMap();

                // Pack Non-Added files first
                foreach (var tocFile in tocFiles)
                {
                    if (FilesToPack.TryGetValue(tocFile.Key, out InputPackEntry file) && !file.IsAddedFile)
                    {
                        PackFile(packCache, outputDir, packAllAsNewEntries, newCache, tocFile.Value, file);
                    }
                }

                // Pack then added files
                foreach (var addedFile in FilesToPack.Where(e => e.Value.IsAddedFile))
                {
                    var tocFile = tocFiles[addedFile.Value.VolumeDirPath];
                    PackFile(packCache, outputDir, packAllAsNewEntries, newCache, tocFile, addedFile.Value);
                }
            }

            return(newCache);
        }
Beispiel #2
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);
            }
        }
Beispiel #3
0
        public void PackFiles(string outrepackDir, string[] filesToRemove, bool packAllAsNew, string customTitleID)
        {
            if (FilesToPack.Count == 0 && filesToRemove.Length == 0)
            {
                Program.Log("[X] Found no files to pack or remove from volume.", forceConsolePrint: true);
                Console.WriteLine("[?] Continue? (Y/N)");
                if (Console.ReadKey().Key != ConsoleKey.Y)
                {
                    return;
                }
            }

            // Leftover?
            if (Directory.Exists($"{outrepackDir}_temp"))
            {
                Directory.Delete($"{outrepackDir}_temp", true);
            }

            // Create temp to make sure we aren't transfering user leftovers
            Directory.CreateDirectory($"{outrepackDir}_temp");

            Program.Log($"[-] Preparing to pack {FilesToPack.Count} files, and remove {filesToRemove.Length} files");
            PackCache newCache = TableOfContents.PackFilesForPatchFileSystem(FilesToPack, _packCache, filesToRemove, outrepackDir, packAllAsNew);

            if (UsePackingCache)
            {
                newCache.Save(".pack_cache");
            }

            // Delete main one if needed
            if (Directory.Exists(outrepackDir))
            {
                Directory.Delete(outrepackDir, true);
            }

            Directory.Move($"{outrepackDir}_temp", outrepackDir);

            Program.Log($"[-] Verifying and fixing Table of Contents segment sizes if needed");
            if (!TableOfContents.TryCheckAndFixInvalidSegmentIndexes())
            {
                Program.Log($"[-] Re-ordered segment indexes.");
            }
            else
            {
                Program.Log($"[/] Segment sizes are correct.");
            }

            if (packAllAsNew)
            {
                Program.Log($"[-] Packing as new: New TOC Entry Index is {VolumeHeader.TOCEntryIndex}.");
            }

            Program.Log($"[-] Saving Table of Contents ({PDIPFSPathResolver.GetPathFromSeed(VolumeHeader.TOCEntryIndex)})");
            TableOfContents.SaveToPatchFileSystem(outrepackDir, out uint compressedSize, out uint uncompressedSize);

            if (!string.IsNullOrEmpty(customTitleID) && customTitleID.Length <= 128)
            {
                VolumeHeader.HasCustomGameID = true;
                VolumeHeader.TitleID         = customTitleID;
            }

            VolumeHeader.CompressedTOCSize = compressedSize;
            VolumeHeader.TOCSize           = uncompressedSize;
            VolumeHeader.TotalVolumeSize   = TableOfContents.GetTotalPatchFileSystemSize(compressedSize);

            Program.Log($"[-] Saving main volume header ({PDIPFSPathResolver.Default})");
            byte[] header = VolumeHeader.Serialize();

            Span <uint> headerBlocks = MemoryMarshal.Cast <byte, uint>(header);

            Keyset.EncryptBlocks(headerBlocks, headerBlocks);
            Keyset.CryptData(header, BASE_VOLUME_ENTRY_INDEX);

            string headerPath = Path.Combine(outrepackDir, PDIPFSPathResolver.Default);

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

            File.WriteAllBytes(headerPath, header);

            Program.Log($"[/] Done packing.", forceConsolePrint: true);
        }