( packFiles: NewPackedFile[], path: string, existingPackToAppend?: Pack, replaceDuplicates?: boolean, dependencyPacks: string[] = [], )
| 1684 | }; |
| 1685 | // Fast append-only implementation for existing packs - much faster than sorted insertion |
| 1686 | export const writePackAppendFast = async ( |
| 1687 | packFiles: NewPackedFile[], |
| 1688 | path: string, |
| 1689 | existingPackToAppend?: Pack, |
| 1690 | replaceDuplicates?: boolean, |
| 1691 | dependencyPacks: string[] = [], |
| 1692 | ) => { |
| 1693 | let outFile: BinaryFile | undefined; |
| 1694 | let sourceFile: BinaryFile | undefined; |
| 1695 | const timestamp = format(new Date(), "ddMMyy_HHmmss"); |
| 1696 | const outPath = existingPackToAppend ? `${path}_${timestamp}` : path; |
| 1697 | try { |
| 1698 | if (packFiles.length < 1) return; |
| 1699 | ensurePackFilesReadyForWrite(packFiles); |
| 1700 | if (existingPackToAppend) { |
| 1701 | // FAST PATH: Clone existing pack and append new files |
| 1702 | console.log("Fast append mode: cloning existing pack and appending new files"); |
| 1703 | // Open source file for reading |
| 1704 | sourceFile = new BinaryFile(existingPackToAppend.path, "r", true); |
| 1705 | console.log("trying to open source file:", existingPackToAppend.path); |
| 1706 | await sourceFile.open(); |
| 1707 | console.log("source file opened"); |
| 1708 | // Open output file for writing |
| 1709 | outFile = new BinaryFile(outPath, "w", true); |
| 1710 | await outFile.open(); |
| 1711 | // Handle duplicate replacement logic |
| 1712 | const newFileNames = new Set(packFiles.map((pf) => pf.name)); |
| 1713 | const filesToKeep = replaceDuplicates |
| 1714 | ? existingPackToAppend.packedFiles.filter((pf) => !newFileNames.has(pf.name)) |
| 1715 | : existingPackToAppend.packedFiles; |
| 1716 | // Calculate new counts and sizes |
| 1717 | const originalFileCount = existingPackToAppend.packedFiles.length; |
| 1718 | const keptFileCount = filesToKeep.length; |
| 1719 | const replacedFileCount = originalFileCount - keptFileCount; |
| 1720 | const newFileCount = keptFileCount + packFiles.length; |
| 1721 | // Use existing dependency packs if not provided |
| 1722 | const finalDependencyPacks = |
| 1723 | dependencyPacks.length > 0 ? dependencyPacks : existingPackToAppend.dependencyPacks || []; |
| 1724 | // Calculate pack_file_index_size based on dependency packs |
| 1725 | const pack_file_index_size = finalDependencyPacks.reduce( |
| 1726 | (acc, dep) => acc + Buffer.byteLength(dep, "utf8") + 1, |
| 1727 | 0, |
| 1728 | ); |
| 1729 | // Calculate additional index size needed for new files |
| 1730 | const newIndexSize = packFiles.reduce((acc, pack) => acc + new Blob([pack.name]).size + 1 + 5, 0); |
| 1731 | // For pack files, the packed_file_index_size in header refers to the file index section |
| 1732 | // We need to calculate the total size of existing + new file index entries |
| 1733 | const keptIndexSize = filesToKeep.reduce((acc, pf) => acc + new Blob([pf.name]).size + 1 + 5, 0); |
| 1734 | const totalIndexSize = keptIndexSize + newIndexSize; |
| 1735 | // Write updated header with new counts |
| 1736 | const headerAccumulator = new BufferAccumulator(outFile); |
| 1737 | headerAccumulator.addString(gameToPackHeader[appData.currentGame]); |
| 1738 | headerAccumulator.addInt32(existingPackToAppend.packHeader.byteMask); |
| 1739 | headerAccumulator.addInt32(existingPackToAppend.packHeader.refFileCount); |
| 1740 | headerAccumulator.addInt32(pack_file_index_size); |
| 1741 | headerAccumulator.addInt32(newFileCount); // Updated file count |
| 1742 | headerAccumulator.addInt32(totalIndexSize); // Updated index size |
| 1743 | headerAccumulator.addInt32(0x7fffffff); // header_buffer |
no test coverage detected