UNPKG

bps.js

Version:

BPS patching utility

160 lines (130 loc) 5.91 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); exports.default = applyBpsPatch; var _crcHash = require('crc-hash'); var _crcHash2 = _interopRequireDefault(_crcHash); var _utils = require('./utils'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function applyBpsPatch(sourceFile, patchFile) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var _options$log = options.log, log = _options$log === undefined ? _utils.defaultLog : _options$log; var sourceFileLength = sourceFile.length; var patchFileLength = patchFile.length; var patchFileLengthWithoutFooter = patchFileLength - _utils.BPS_FOOTER_LENGTH; var index = 0; var outputOffset = 0; var sourceRelativeOffset = 0; var targetRelativeOffset = 0; function readPatchData(length) { if (index + length > patchFileLength) { throw new Error('Unexpected end of file tried to read ' + length + ' bytes at ' + (0, _utils.formatHex)(index, 3) + ')'); } var start = index; index += length; return patchFile.slice(start, index); } function readPatchChecksum() { return (0, _utils.toIntBE)(Buffer.from(readPatchData(4)).reverse()).toString(16).padStart(8, 0); } function readPatchDataNumber() { var _decodeNumber = (0, _utils.decodeNumber)(patchFile, index), _decodeNumber2 = _slicedToArray(_decodeNumber, 2), value = _decodeNumber2[0], nextIndex = _decodeNumber2[1]; index = nextIndex; return value; } if (!readPatchData(_utils.BPS_START.length).equals(_utils.BPS_START)) { throw new Error('Patch file does not have BPS header'); } var sourceFileSize = readPatchDataNumber(); var targetFileSize = readPatchDataNumber(); var metaDataLength = readPatchDataNumber(); if (sourceFileLength !== sourceFileSize) { throw new Error('Expected source filesize of ' + sourceFileSize + ' bytes, got ' + sourceFileLength + ' bytes instead'); } var metaData = readPatchData(metaDataLength); if (metaDataLength) { // @TODO: Check this... log('Metadata: %s', metaData.toString()); } var targetFile = Buffer.alloc(targetFileSize); function sourceRead(length) { var i = length; log('sourceRead: Copy ' + length + ' source bytes to ' + (0, _utils.formatHex)(outputOffset, 3)); while (i--) { targetFile.writeUInt8(sourceFile[outputOffset], outputOffset); outputOffset++; } } function targetRead(length) { var i = length; log('targetRead: Write ' + length + ' bytes to ' + (0, _utils.formatHex)(outputOffset, 3)); while (i--) { targetFile.writeUInt8((0, _utils.toIntBE)(readPatchData(1)), outputOffset++); } } function sourceCopy(length) { var i = length; var data = readPatchDataNumber(); sourceRelativeOffset += (data & 1 ? -1 : 1) * (data >> 1); log('sourceCopy: Copy ' + length + ' source bytes at ' + (0, _utils.formatHex)(sourceRelativeOffset, 3) + ' to ' + (0, _utils.formatHex)(outputOffset, 3)); while (i--) { targetFile.writeUInt8(sourceFile[sourceRelativeOffset++], outputOffset++); } } function targetCopy(length) { var i = length; var data = readPatchDataNumber(); targetRelativeOffset += (data & 1 ? -1 : +1) * (data >> 1); log('targetCopy: Copy ' + length + ' target bytes at ' + (0, _utils.formatHex)(targetRelativeOffset, 3) + ' to ' + (0, _utils.formatHex)(outputOffset, 3)); while (i--) { targetFile.writeUInt8(targetFile[targetRelativeOffset++], outputOffset++); } } while (index < patchFileLengthWithoutFooter) { var data = readPatchDataNumber(); var command = data & 3; var length = (data >>> 2) + 1; switch (command) { case _utils.SOURCE_READ: sourceRead(length); break; case _utils.TARGET_READ: targetRead(length); break; case _utils.SOURCE_COPY: sourceCopy(length); break; case _utils.TARGET_COPY: targetCopy(length); break; default: throw new Error('Unrecognized command ' + (0, _utils.formatHex)(command, 1)); } } if (index !== patchFileLengthWithoutFooter) { throw new Error('Missed the footer?'); } index = patchFileLengthWithoutFooter; var sourceChecksum = readPatchChecksum(); var targetChecksum = readPatchChecksum(); var patchChecksum = readPatchChecksum(); var sourceCrc32 = _crcHash2.default.createHash('crc32').update(sourceFile).digest('hex'); var targetCrc32 = _crcHash2.default.createHash('crc32').update(targetFile).digest('hex'); var patchCrc32 = _crcHash2.default.createHash('crc32').update(patchFile.slice(0, -4)).digest('hex'); if (sourceCrc32 !== sourceChecksum) { throw new Error('Source file CRC32 mismatch! Expected ' + sourceChecksum + ' but received ' + sourceCrc32); } if (targetCrc32 !== targetChecksum) { throw new Error('Target file CRC32 mismatch! Expected ' + targetChecksum + ' but received ' + targetCrc32); } if (patchCrc32 !== patchChecksum) { throw new Error('Target file CRC32 mismatch! Expected ' + patchChecksum + ' but received ' + patchCrc32); } return targetFile; }