54 lines
2 KiB
TypeScript
54 lines
2 KiB
TypeScript
//Similar to extractFile.ts, but instead of receiving and returning an ArrayBuffer, works with Node.js streams.
|
|
|
|
import * as stream from 'stream';
|
|
import * as zlib from 'zlib';
|
|
import { ISsnFileEntry } from '../interfaces/ISsnFileEntry';
|
|
import decryptStream from './streams/decryptStream';
|
|
import streamSetMaxLength from './streams/streamSetMaxLength';
|
|
|
|
/** Extracts the file with the given metadata from the stream.
|
|
* The stream must already start at the .zip's local file header
|
|
* and must transparently span across multiple disks if necessary.
|
|
*/
|
|
export default function extractFileStream(file: ISsnFileEntry, inputStream: stream.Readable): stream.Readable {
|
|
const localFileHeader: Buffer = inputStream.read(30);
|
|
|
|
//Local file header signature
|
|
const magic = localFileHeader.readUInt32LE(0);
|
|
if (magic !== 0x04034B50) {
|
|
throw new Error(`Local file header had wrong magic; expected 0x04034B50 but got 0x${magic.toString(16).padStart(8, '0')}`);
|
|
}
|
|
//All fields in the local file header are copies of the central file header, so we can skip them.
|
|
//FIXME: Maybe we should actually read these fields to verify that they are identical?
|
|
//skip 22 bytes
|
|
const localFilenameSize = localFileHeader.readUInt16LE(26);
|
|
const localExtraSize = localFileHeader.readUInt16LE(28);
|
|
|
|
//skip local file name and extra field
|
|
inputStream.read(localFilenameSize + localExtraSize);
|
|
|
|
//-------------------------------------------------
|
|
|
|
let curStream = inputStream;
|
|
|
|
//set max length
|
|
const maxLength = streamSetMaxLength(curStream, file.compressedSize);
|
|
curStream = maxLength;
|
|
|
|
//pipe into decryption if file is encrypted
|
|
if (file.decryptionKeys !== undefined) {
|
|
const decryptTransform = decryptStream(curStream, file.decryptionKeys);
|
|
curStream = decryptTransform;
|
|
}
|
|
|
|
//pipe into decompression
|
|
const decompressTransform = zlib.createInflateRaw();
|
|
curStream.pipe(decompressTransform);
|
|
curStream = decompressTransform;
|
|
|
|
//set max length
|
|
const maxLength2 = streamSetMaxLength(curStream, file.size);
|
|
curStream = maxLength2;
|
|
|
|
return curStream;
|
|
}
|