nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

index.js (4231B)


      1 import utf8 from 'utf8';
      2 
      3 import {BinStream} from '~/lib/binary-stream';
      4 
      5 // Reference:
      6 // https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html
      7 // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
      8 
      9 const CRC_TABLE = new Uint32Array(256);
     10 
     11 (function fillCrcTable() {
     12     for (let i = 0; i < 256; ++i) {
     13         let crc = i;
     14         for (let j = 0; j < 8; ++j) {
     15             crc = (crc >>> 1) ^ (crc & 0x01 && 0xedb88320);
     16         }
     17         CRC_TABLE[i] = crc;
     18     }
     19 })();
     20 
     21 function crc32(s) {
     22     let crc = -1;
     23     for (let i = 0, l = s.length; i < l; i++) {
     24         crc = (crc >>> 8) ^ CRC_TABLE[(crc & 0xff) ^ s.charCodeAt(i)];
     25     }
     26     return (crc ^ -1) >>> 0;
     27 }
     28 
     29 function buildDOSDateTime(date) {
     30     return {
     31         dosTime: (date.getSeconds() >> 1) | (date.getMinutes() << 5) | (date.getHours() << 11),
     32         dosDate: date.getDate() | ((date.getMonth() + 1) << 5) | ((date.getFullYear() - 1980) << 9),
     33     };
     34 }
     35 
     36 function writeLocalFileHeader(stream, fileName, size, date, crc) {
     37     const dosDateTime = buildDOSDateTime(date);
     38     stream.writeBinaryString('\x50\x4b\x03\x04'); // signature
     39     stream.writeUint16(10); // Version to extract
     40     stream.writeUint16(1 << 11); // Flags = language encoding
     41     stream.writeUint16(0); // No compression
     42     stream.writeUint16(dosDateTime.dosTime);
     43     stream.writeUint16(dosDateTime.dosDate);
     44     stream.writeUint32(crc);
     45     stream.writeUint32(size);
     46     stream.writeUint32(size);
     47     stream.writeUint16(fileName.length);
     48     stream.writeUint16(0); // Extra field len
     49     stream.writeBinaryString(fileName);
     50 }
     51 
     52 function writeCentralDirectoryFileHeader(stream, fileName, size, date, crc, localHeaderOffset) {
     53     const dosDateTime = buildDOSDateTime(date);
     54     stream.writeBinaryString('\x50\x4b\x01\x02'); // signature
     55     stream.writeUint8(10); // Version made by = 1.0
     56     stream.writeUint8(3); // Made in OS = UNIX
     57     stream.writeUint16(10); // Version to extract = 1.0
     58     stream.writeUint16(1 << 11); // Flags = language encoding
     59     stream.writeUint16(0); // No compression
     60     stream.writeUint16(dosDateTime.dosTime);
     61     stream.writeUint16(dosDateTime.dosDate);
     62     stream.writeUint32(crc);
     63     stream.writeUint32(size);
     64     stream.writeUint32(size);
     65     stream.writeUint16(fileName.length);
     66     stream.writeUint16(0); // Extra field len
     67     stream.writeUint16(0); // File comment length
     68     stream.writeUint16(0); // Disk # start
     69     stream.writeUint16(0); // Internal attr
     70     stream.writeUint32(0); // External attr
     71     stream.writeUint32(localHeaderOffset); // External attr
     72     stream.writeBinaryString(fileName);
     73 }
     74 
     75 function writeEndOfCentralDirectory(stream, filesNumber, centralDirectoryOffset, centralDirectorySize) {
     76     stream.writeBinaryString('\x50\x4b\x05\x06');
     77     stream.writeUint16(0); // Disk number
     78     stream.writeUint16(0); // Disk number with central directory
     79     stream.writeUint16(filesNumber);
     80     stream.writeUint16(filesNumber);
     81     stream.writeUint32(centralDirectorySize);
     82     stream.writeUint32(centralDirectoryOffset);
     83     stream.writeUint16(0); // Comment length
     84 }
     85 
     86 // files: Array of Object with items:
     87 //     "filename" - string
     88 //     "content" - binary string
     89 // returns: ArrayBuffer
     90 function createZipFile(files) {
     91     const stream = new BinStream(true);
     92     const now = new Date();
     93     const encodedFilenames = [];
     94     const checkSums = [];
     95     const headerOffsets = [];
     96 
     97     for (const [i, file] of files.entries()) {
     98         encodedFilenames[i] = utf8.encode(file.filename);
     99         checkSums[i] = crc32(file.content);
    100         headerOffsets[i] = stream.tell();
    101         writeLocalFileHeader(stream, encodedFilenames[i], file.content.length, now, checkSums[i]);
    102         stream.writeBinaryString(file.content);
    103     }
    104 
    105     const centralDirectoryOffset = stream.tell();
    106     for (const [i, file] of files.entries()) {
    107         writeCentralDirectoryFileHeader(
    108             stream,
    109             encodedFilenames[i],
    110             file.content.length,
    111             now,
    112             checkSums[i],
    113             headerOffsets[i]
    114         );
    115     }
    116 
    117     writeEndOfCentralDirectory(stream, files.length, centralDirectoryOffset, stream.tell() - centralDirectoryOffset);
    118 
    119     return stream.getBuffer();
    120 }
    121 
    122 export {createZipFile};