var Utils = require("../util"), Constants = Utils.Constants; /* The central directory file header */ module.exports = function () { var _verMade = 0x0A, _version = 0x0A, _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; var _dataHeader = {}; function setTime(val) { val = new Date(val); _time = (val.getFullYear() - 1980 & 0x7f) << 25 // b09-16 years from 1980 | (val.getMonth() + 1) << 21 // b05-08 month | val.getDate() << 16 // b00-04 hour // 2 bytes time | val.getHours() << 11 // b11-15 hour | val.getMinutes() << 5 // b05-10 minute | val.getSeconds() >> 1; // b00-04 seconds divided by 2 } setTime(+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 method () { return _method; }, set method (val) { _method = val; }, get time () { return new Date( ((_time >> 25) & 0x7f) + 1980, ((_time >> 21) & 0x0f) - 1, (_time >> 16) & 0x1f, (_time >> 11) & 0x1f, (_time >> 5) & 0x3f, (_time & 0x1f) << 1 ); }, set time (val) { setTime(val); }, get crc () { return _crc; }, set crc (val) { _crc = val; }, get compressedSize () { return _compressedSize; }, set compressedSize (val) { _compressedSize = val; }, get size () { return _size; }, set size (val) { _size = val; }, get fileNameLength () { return _fnameLen; }, set fileNameLength (val) { _fnameLen = val; }, get extraLength () { return _extraLen }, set extraLength (val) { _extraLen = val; }, get commentLength () { return _comLen }, set commentLength (val) { _comLen = val }, get diskNumStart () { return _diskStart }, set diskNumStart (val) { _diskStart = val }, get inAttr () { return _inattr }, set inAttr (val) { _inattr = val }, get attr () { return _attr }, set attr (val) { _attr = val }, get offset () { return _offset }, set offset (val) { _offset = val }, get encripted () { return (_flags & 1) === 1 }, get entryHeaderSize () { return Constants.CENHDR + _fnameLen + _extraLen + _comLen; }, get realDataOffset () { return _offset + Constants.LOCHDR + _dataHeader.fnameLen + _dataHeader.extraLen; }, get dataHeader () { return _dataHeader; }, loadDataHeaderFromBinary : 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 new Error(Utils.Errors.INVALID_LOC); } _dataHeader = { // version needed to extract version : data.readUInt16LE(Constants.LOCVER), // general purpose bit flag flags : data.readUInt16LE(Constants.LOCFLG), // compression method method : data.readUInt16LE(Constants.LOCHOW), // modification time (2 bytes time, 2 bytes date) time : data.readUInt32LE(Constants.LOCTIM), // uncompressed file crc-32 value crc : data.readUInt32LE(Constants.LOCCRC), // compressed size compressedSize : data.readUInt32LE(Constants.LOCSIZ), // uncompressed size size : data.readUInt32LE(Constants.LOCLEN), // filename length fnameLen : data.readUInt16LE(Constants.LOCNAM), // extra field length extraLen : data.readUInt16LE(Constants.LOCEXT) } }, 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 new Error(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); }, dataHeaderToBinary : 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(_extraLen, Constants.LOCEXT); return data; }, entryHeaderToBinary : 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); // fill all with data.fill(0x00, Constants.CENHDR); return data; }, toString : function() { return '{\n' + '\t"made" : ' + _verMade + ",\n" + '\t"version" : ' + _version + ",\n" + '\t"flags" : ' + _flags + ",\n" + '\t"method" : ' + Utils.methodToString(_method) + ",\n" + '\t"time" : ' + this.time + ",\n" + '\t"crc" : 0x' + _crc.toString(16).toUpperCase() + ",\n" + '\t"compressedSize" : ' + _compressedSize + " bytes,\n" + '\t"size" : ' + _size + " bytes,\n" + '\t"fileNameLength" : ' + _fnameLen + ",\n" + '\t"extraLength" : ' + _extraLen + " bytes,\n" + '\t"commentLength" : ' + _comLen + " bytes,\n" + '\t"diskNumStart" : ' + _diskStart + ",\n" + '\t"inAttr" : ' + _inattr + ",\n" + '\t"attr" : ' + _attr + ",\n" + '\t"offset" : ' + _offset + ",\n" + '\t"entryHeaderSize" : ' + (Constants.CENHDR + _fnameLen + _extraLen + _comLen) + " bytes\n" + '}'; } } };