diff --git a/src/ssn/getPatch.ts b/src/ssn/getPatch.ts index 26bf306..1f567d4 100644 --- a/src/ssn/getPatch.ts +++ b/src/ssn/getPatch.ts @@ -11,6 +11,7 @@ import readSsnFile from './reader/readSsnFile'; import getFileFromDisks from './streams/getFileFromDisks'; import verifyPatch from './verify/verifyPatch'; import verifyProductName from './verify/verifyProductName'; +import performDiffing from './xdelta3/performDiffing'; interface IGetPatchArgs { /** The product that should be patched. */ @@ -103,9 +104,31 @@ export default async function getPatch({ product, from, to, sourceDirectory, tar const fileStream = await getFileFromDisks(diskFilenames, { diskStart: file.diskNumberStart, offset: file.offset, length: file.compressedSize }); const fileContents = await extractFileAsStream(file, fileStream); - //TODO: need to apply xdelta3 diffing, then write to disk - //if source === target, need to create .temp file, and when it's done, delete source file and rename .temp to actual name - console.warn(`Could not extract "${file.name}"; xdelta3 diffing is not yet implemented.`); + //need to apply xdelta3 diffing, then write to disk + const sourceFile = path.join(sourceDir, file.name); + const outputName = path.join(targetDir, file.name); + const outputNameTemp = `${outputName}.tmp`; + await performDiffing(sourceFile, fileContents, outputNameTemp); + + //clean up: delete source file if necessary, and remove .tmp file extension + if (sourceDir === targetDir) { + fs.unlink(sourceFile, (deleteError) => { + if (deleteError) { + throw new Error(`Could not delete old source file "${sourceFile}": ${deleteError.name}`); + } + fs.rename(outputNameTemp, outputName, (renameError) => { + if (renameError) { + throw new Error(`Could not rename output file "${outputNameTemp}": ${renameError.name}`); + } + }); + }); + } else { + fs.rename(outputNameTemp, outputName, (renameError) => { + if (renameError) { + throw new Error(`Could not rename output file "${outputNameTemp}": ${renameError.name}`); + } + }); + } } catch (error) { console.error(`Could not extract file "${file.name}"`, error); } diff --git a/src/ssn/xdelta3/performDiffing.ts b/src/ssn/xdelta3/performDiffing.ts new file mode 100644 index 0000000..d6a04cb --- /dev/null +++ b/src/ssn/xdelta3/performDiffing.ts @@ -0,0 +1,26 @@ +import * as childProcess from 'child_process'; +import * as fs from 'fs'; +import * as stream from 'stream'; + +export default function performDiffing(sourceFile: string, diffStream: stream.Readable, targetFile: string): Promise { + return new Promise((resolve, reject) => { + //const sourceStream = fs.createReadStream(sourceFile); + const targetStream = fs.createWriteStream(targetFile); + + //spawn xdelta3 process, set up listeners + const process = childProcess.spawn('xdelta3', ['-d', '-s', sourceFile]); + diffStream.pipe(process.stdin); + process.stdout.pipe(targetStream); + process.stderr.on('data', (chunk) => { + reject(`Error during xdelta3: ${chunk}`); + }); + + process.on('exit', (code, signal) => { + if (code === 0) { + resolve(); + } else { + reject(`xdelta3 exited with code "${code}" due to signal "${signal}".`); + } + }); + }); +}