bps.js
Version:
BPS patching utility
160 lines (130 loc) • 5.91 kB
JavaScript
;
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;
}