var Utils = require("../util"), Constants = Utils.Constants; /* The central directory file header */ module.exports = function () { var _verMade = 20, // v2.0 _version = 10, // v1.0 _flags = 0, _method = 0, _time = 0, _crc = 0, _compressedSize = 0, _size = 0, _fnameLen = 0, _extraLen = 0, _comLen = 0, _diskStart = 0, _inattr = 0, _attr = 0, _offset = 0; _verMade |= Utils.isWin ? 0x0a00 : 0x0300; // Set EFS flag since filename and comment fields are all by default encoded using UTF-8. // Without it file names may be corrupted for other apps when file names use unicode chars _flags |= Constants.FLG_EFS; const _localHeader = { extraLen: 0 }; // casting const uint32 = (val) => Math.max(0, val) >>> 0; const uint16 = (val) => Math.max(0, val) & 0xffff; const uint8 = (val) => Math.max(0, val) & 0xff; _time = Utils.fromDate2DOS(new Date()); return { get made() { return _verMade; }, set made(val) { _verMade = val; }, get version() { return _version; }, set version(val) { _version = val; }, get flags() { return _flags; }, set flags(val) { _flags = val; }, get flags_efs() { return (_flags & Constants.FLG_EFS) > 0; }, set flags_efs(val) { if (val) { _flags |= Constants.FLG_EFS; } else { _flags &= ~Constants.FLG_EFS; } }, get flags_desc() { return (_flags & Constants.FLG_DESC) > 0; }, set flags_desc(val) { if (val) { _flags |= Constants.FLG_DESC; } else { _flags &= ~Constants.FLG_DESC; } }, get method() { return _method; }, set method(val) { switch (val) { case Constants.STORED: this.version = 10; case Constants.DEFLATED: default: this.version = 20; } _method = val; }, get time() { return Utils.fromDOS2Date(this.timeval); }, set time(val) { this.timeval = Utils.fromDate2DOS(val); }, get timeval() { return _time; }, set timeval(val) { _time = uint32(val); }, get timeHighByte() { return uint8(_time >>> 8); }, get crc() { return _crc; }, set crc(val) { _crc = uint32(val); }, get compressedSize() { return _compressedSize; }, set compressedSize(val) { _compressedSize = uint32(val); }, get size() { return _size; }, set size(val) { _size = uint32(val); }, get fileNameLength() { return _fnameLen; }, set fileNameLength(val) { _fnameLen = val; }, get extraLength() { return _extraLen; }, set extraLength(val) { _extraLen = val; }, get extraLocalLength() { return _localHeader.extraLen; }, set extraLocalLength(val) { _localHeader.extraLen = val; }, get commentLength() { return _comLen; }, set commentLength(val) { _comLen = val; }, get diskNumStart() { return _diskStart; }, set diskNumStart(val) { _diskStart = uint32(val); }, get inAttr() { return _inattr; }, set inAttr(val) { _inattr = uint32(val); }, get attr() { return _attr; }, set attr(val) { _attr = uint32(val); }, // get Unix file permissions get fileAttr() { return (_attr || 0) >> 16 & 0xfff; }, get offset() { return _offset; }, set offset(val) { _offset = uint32(val); }, get encrypted() { return (_flags & Constants.FLG_ENC) === Constants.FLG_ENC; }, get centralHeaderSize() { return Constants.CENHDR + _fnameLen + _extraLen + _comLen; }, get realDataOffset() { return _offset + Constants.LOCHDR + _localHeader.fnameLen + _localHeader.extraLen; }, get localHeader() { return _localHeader; }, loadLocalHeaderFromBinary: function (/*Buffer*/ input) { var data = input.slice(_offset, _offset + Constants.LOCHDR); // 30 bytes and should start with "PK\003\004" if (data.readUInt32LE(0) !== Constants.LOCSIG) { throw Utils.Errors.INVALID_LOC(); } // version needed to extract _localHeader.version = data.readUInt16LE(Constants.LOCVER); // general purpose bit flag _localHeader.flags = data.readUInt16LE(Constants.LOCFLG); // compression method _localHeader.method = data.readUInt16LE(Constants.LOCHOW); // modification time (2 bytes time, 2 bytes date) _localHeader.time = data.readUInt32LE(Constants.LOCTIM); // uncompressed file crc-32 valu _localHeader.crc = data.readUInt32LE(Constants.LOCCRC); // compressed size _localHeader.compressedSize = data.readUInt32LE(Constants.LOCSIZ); // uncompressed size _localHeader.size = data.readUInt32LE(Constants.LOCLEN); // filename length _localHeader.fnameLen = data.readUInt16LE(Constants.LOCNAM); // extra field length _localHeader.extraLen = data.readUInt16LE(Constants.LOCEXT); // read extra data const extraStart = _offset + Constants.LOCHDR + _localHeader.fnameLen; const extraEnd = extraStart + _localHeader.extraLen; return input.slice(extraStart, extraEnd); }, loadFromBinary: function (/*Buffer*/ data) { // data should be 46 bytes and start with "PK 01 02" if (data.length !== Constants.CENHDR || data.readUInt32LE(0) !== Constants.CENSIG) { throw Utils.Errors.INVALID_CEN(); } // version made by _verMade = data.readUInt16LE(Constants.CENVEM); // version needed to extract _version = data.readUInt16LE(Constants.CENVER); // encrypt, decrypt flags _flags = data.readUInt16LE(Constants.CENFLG); // compression method _method = data.readUInt16LE(Constants.CENHOW); // modification time (2 bytes time, 2 bytes date) _time = data.readUInt32LE(Constants.CENTIM); // uncompressed file crc-32 value _crc = data.readUInt32LE(Constants.CENCRC); // compressed size _compressedSize = data.readUInt32LE(Constants.CENSIZ); // uncompressed size _size = data.readUInt32LE(Constants.CENLEN); // filename length _fnameLen = data.readUInt16LE(Constants.CENNAM); // extra field length _extraLen = data.readUInt16LE(Constants.CENEXT); // file comment length _comLen = data.readUInt16LE(Constants.CENCOM); // volume number start _diskStart = data.readUInt16LE(Constants.CENDSK); // internal file attributes _inattr = data.readUInt16LE(Constants.CENATT); // external file attributes _attr = data.readUInt32LE(Constants.CENATX); // LOC header offset _offset = data.readUInt32LE(Constants.CENOFF); }, localHeaderToBinary: function () { // LOC header size (30 bytes) var data = Buffer.alloc(Constants.LOCHDR); // "PK\003\004" data.writeUInt32LE(Constants.LOCSIG, 0); // version needed to extract data.writeUInt16LE(_version, Constants.LOCVER); // general purpose bit flag data.writeUInt16LE(_flags, Constants.LOCFLG); // compression method data.writeUInt16LE(_method, Constants.LOCHOW); // modification time (2 bytes time, 2 bytes date) data.writeUInt32LE(_time, Constants.LOCTIM); // uncompressed file crc-32 value data.writeUInt32LE(_crc, Constants.LOCCRC); // compressed size data.writeUInt32LE(_compressedSize, Constants.LOCSIZ); // uncompressed size data.writeUInt32LE(_size, Constants.LOCLEN); // filename length data.writeUInt16LE(_fnameLen, Constants.LOCNAM); // extra field length data.writeUInt16LE(_localHeader.extraLen, Constants.LOCEXT); return data; }, centralHeaderToBinary: function () { // CEN header size (46 bytes) var data = Buffer.alloc(Constants.CENHDR + _fnameLen + _extraLen + _comLen); // "PK\001\002" data.writeUInt32LE(Constants.CENSIG, 0); // version made by data.writeUInt16LE(_verMade, Constants.CENVEM); // version needed to extract data.writeUInt16LE(_version, Constants.CENVER); // encrypt, decrypt flags data.writeUInt16LE(_flags, Constants.CENFLG); // compression method data.writeUInt16LE(_method, Constants.CENHOW); // modification time (2 bytes time, 2 bytes date) data.writeUInt32LE(_time, Constants.CENTIM); // uncompressed file crc-32 value data.writeUInt32LE(_crc, Constants.CENCRC); // compressed size data.writeUInt32LE(_compressedSize, Constants.CENSIZ); // uncompressed size data.writeUInt32LE(_size, Constants.CENLEN); // filename length data.writeUInt16LE(_fnameLen, Constants.CENNAM); // extra field length data.writeUInt16LE(_extraLen, Constants.CENEXT); // file comment length data.writeUInt16LE(_comLen, Constants.CENCOM); // volume number start data.writeUInt16LE(_diskStart, Constants.CENDSK); // internal file attributes data.writeUInt16LE(_inattr, Constants.CENATT); // external file attributes data.writeUInt32LE(_attr, Constants.CENATX); // LOC header offset data.writeUInt32LE(_offset, Constants.CENOFF); return data; }, toJSON: function () { const bytes = function (nr) { return nr + " bytes"; }; return { made: _verMade, version: _version, flags: _flags, method: Utils.methodToString(_method), time: this.time, crc: "0x" + _crc.toString(16).toUpperCase(), compressedSize: bytes(_compressedSize), size: bytes(_size), fileNameLength: bytes(_fnameLen), extraLength: bytes(_extraLen), commentLength: bytes(_comLen), diskNumStart: _diskStart, inAttr: _inattr, attr: _attr, offset: _offset, centralHeaderSize: bytes(Constants.CENHDR + _fnameLen + _extraLen + _comLen) }; }, toString: function () { return JSON.stringify(this.toJSON(), null, "\t"); } }; };