(mods: Mod[], existingPath?: string)
| 920 | return compareModNames(packFirst.name, packSecond.name); |
| 921 | }; |
| 922 | export const mergeMods = async (mods: Mod[], existingPath?: string) => { |
| 923 | const dataFolder = appData.gamesToGameFolderPaths[appData.currentGame].dataFolder; |
| 924 | if (!dataFolder) return; |
| 925 | let outFile: BinaryFile | undefined; |
| 926 | try { |
| 927 | const targetPath = |
| 928 | existingPath || |
| 929 | nodePath.join(dataFolder, "merged-" + format(new Date(), "dd-MM-yyyy-HH-mm-ss") + ".pack"); |
| 930 | await fsExtra.ensureDirSync(nodePath.dirname(targetPath)); |
| 931 | const packFieldsSettled = await Promise.allSettled( |
| 932 | mods.map((mod) => readPack(mod.path, { skipParsingTables: true })), |
| 933 | ); |
| 934 | const sources = ( |
| 935 | packFieldsSettled.filter((pfs) => pfs.status === "fulfilled") as PromiseFulfilledResult<Pack>[] |
| 936 | ) |
| 937 | .map((r) => r.value) |
| 938 | .filter((packData) => packData); |
| 939 | const header = gameToPackHeader[appData.currentGame]; |
| 940 | const byteMask = 3; |
| 941 | const refFileCount = 0; |
| 942 | const pack_file_index_size = 0; |
| 943 | outFile = new BinaryFile(targetPath, "w", true); |
| 944 | await outFile.open(); |
| 945 | await outFile.writeString(header); |
| 946 | await outFile.writeInt32(byteMask); |
| 947 | await outFile.writeInt32(refFileCount); |
| 948 | await outFile.writeInt32(pack_file_index_size); |
| 949 | const packedFileNameToPackFileLookup: Record<string, PackedFile> = {}; |
| 950 | const packedFileNameToPackSource: Record<string, Pack> = {}; |
| 951 | const reverseSortedPacksByName = sources.sort(sortByPackName).reverse(); |
| 952 | for (const sourcePack of reverseSortedPacksByName) { |
| 953 | for (const packFile of sourcePack.packedFiles) { |
| 954 | const packedFileName = packFile.name; |
| 955 | packedFileNameToPackFileLookup[packedFileName] = packFile; |
| 956 | packedFileNameToPackSource[packedFileName] = sourcePack; |
| 957 | } |
| 958 | } |
| 959 | await outFile.writeInt32(Object.keys(packedFileNameToPackFileLookup).length); |
| 960 | const index_size = Object.keys(packedFileNameToPackFileLookup).reduce( |
| 961 | (acc, name) => acc + new Blob([name]).size + 1 + 5, |
| 962 | 0, |
| 963 | ); |
| 964 | await outFile.writeInt32(index_size); |
| 965 | await outFile.writeInt32(0x7fffffff); // header_buffer |
| 966 | const sortedPackFileNames = Object.keys(packedFileNameToPackFileLookup).sort(compareModNames); |
| 967 | const sortedPackFileNamesDBFirst = sortedPackFileNames |
| 968 | .filter((name) => name.startsWith("db\\")) |
| 969 | .concat(sortedPackFileNames.filter((name) => !name.startsWith("db\\"))); |
| 970 | for (const packedFileName of sortedPackFileNamesDBFirst) { |
| 971 | const packedFile = packedFileNameToPackFileLookup[packedFileName]; |
| 972 | const { name, file_size } = packedFile; |
| 973 | await outFile.writeInt32(file_size); |
| 974 | await outFile.writeInt8(0); |
| 975 | await outFile.writeString(name + "\0"); |
| 976 | } |
| 977 | const packNameToFileHandle: Record<string, BinaryFile> = {}; |
| 978 | for (const sourcePack of sources) { |
| 979 | const sourceFile = new BinaryFile(sourcePack.path, "r", true); |
no test coverage detected