zip.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. /* jshint esversion: 6 */
  2. /* global pako */
  3. var zip = zip || {};
  4. zip.Archive = class {
  5. constructor(buffer) {
  6. this._entries = [];
  7. const stream = buffer instanceof Uint8Array ? new zip.BinaryReader(buffer) : buffer;
  8. const signature = [ 0x50, 0x4B, 0x01, 0x02 ];
  9. if (stream.length < 4 || !stream.peek(2).every((value, index) => value === signature[index])) {
  10. throw new zip.Error('Invalid Zip archive.');
  11. }
  12. const lookup = (stream, signature) => {
  13. let position = stream.length;
  14. do {
  15. position = Math.max(0, position - 64000);
  16. stream.seek(position);
  17. const buffer = stream.read(Math.min(stream.length - position, 65536));
  18. for (let i = buffer.length - 4; i >= 0; i--) {
  19. if (signature[0] === buffer[i] && signature[1] === buffer[i + 1] && signature[2] === buffer[i + 2] && signature[3] === buffer[i + 3]) {
  20. stream.seek(position + i + 4);
  21. return true;
  22. }
  23. }
  24. }
  25. while (position > 0);
  26. return false;
  27. };
  28. if (!lookup(stream, [ 0x50, 0x4B, 0x05, 0x06 ])) {
  29. throw new zip.Error('End of central directory not found.');
  30. }
  31. let reader = new zip.BinaryReader(stream.read(16));
  32. reader.skip(12);
  33. let offset = reader.uint32(); // central directory offset
  34. if (offset > stream.length) {
  35. console.log("*");
  36. if (!lookup(stream, [ 0x50, 0x4B, 0x06, 0x06 ])) {
  37. throw new zip.Error('Zip64 end of central directory not found.');
  38. }
  39. reader = new zip.BinaryReader(stream.read(52));
  40. reader.skip(44);
  41. offset = reader.uint32();
  42. if (reader.uint32() !== 0) {
  43. throw new zip.Error('Zip 64-bit central directory offset not supported.');
  44. }
  45. }
  46. if (offset > stream.length) {
  47. throw new zip.Error('Invalid central directory offset.');
  48. }
  49. stream.seek(offset); // central directory offset
  50. const entries = [];
  51. while (stream.position + 4 < stream.length && stream.read(4).every((value, index) => value === signature[index])) {
  52. const entry = {};
  53. const reader = new zip.BinaryReader(stream.read(42));
  54. reader.uint16(); // version made by
  55. reader.skip(2); // version needed to extract
  56. const flags = reader.uint16();
  57. if ((flags & 1) == 1) {
  58. throw new zip.Error('Encrypted entries not supported.');
  59. }
  60. entry.compressionMethod = reader.uint16();
  61. reader.uint32(); // date
  62. reader.uint32(); // crc32
  63. entry.compressedSize = reader.uint32();
  64. entry.size = reader.uint32();
  65. entry.nameLength = reader.uint16(); // file name length
  66. const extraDataLength = reader.uint16();
  67. const commentLength = reader.uint16();
  68. entry.disk = reader.uint16(); // disk number start
  69. reader.uint16(); // internal file attributes
  70. reader.uint32(); // external file attributes
  71. entry.localHeaderOffset = reader.uint32();
  72. entry.nameBuffer = stream.read(entry.nameLength);
  73. const extraData = stream.read(extraDataLength);
  74. if (extraData.length > 0) {
  75. const reader = new zip.BinaryReader(extraData);
  76. while (reader.position < reader.length) {
  77. const type = reader.uint16();
  78. reader.uint16(); // length
  79. switch (type) {
  80. case 0x0001:
  81. if (entry.size === 0xffffffff) {
  82. entry.size = reader.uint32();
  83. if (reader.uint32() !== 0) {
  84. throw new zip.Error('Zip 64-bit offset not supported.');
  85. }
  86. }
  87. if (entry.compressedSize === 0xffffffff) {
  88. entry.compressedSize = reader.uint32();
  89. if (reader.uint32() !== 0) {
  90. throw new zip.Error('Zip 64-bit offset not supported.');
  91. }
  92. }
  93. if (entry.localHeaderOffset === 0xffffffff) {
  94. entry.localHeaderOffset = reader.uint32();
  95. if (reader.uint32() !== 0) {
  96. throw new zip.Error('Zip 64-bit offset not supported.');
  97. }
  98. }
  99. if (entry.disk === 0xffff) {
  100. entry.disk = reader.uint32();
  101. }
  102. break;
  103. }
  104. }
  105. }
  106. stream.read(commentLength); // comment
  107. entries.push(entry);
  108. }
  109. for (const entry of entries) {
  110. this._entries.push(new zip.Entry(stream, entry));
  111. }
  112. stream.seek(0);
  113. }
  114. get entries() {
  115. return this._entries;
  116. }
  117. };
  118. zip.Entry = class {
  119. constructor(stream, entry) {
  120. stream.seek(entry.localHeaderOffset);
  121. const signature = [ 0x50, 0x4B, 0x03, 0x04 ];
  122. if (stream.position + 4 > stream.length || !stream.read(4).every((value, index) => value === signature[index])) {
  123. throw new zip.Error('Invalid local file header signature.');
  124. }
  125. const reader = new zip.BinaryReader(stream.read(26));
  126. reader.skip(22);
  127. entry.nameLength = reader.uint16();
  128. const extraDataLength = reader.uint16();
  129. entry.nameBuffer = stream.read(entry.nameLength);
  130. stream.skip(extraDataLength);
  131. this._name = '';
  132. for (const c of entry.nameBuffer) {
  133. this._name += String.fromCharCode(c);
  134. }
  135. this._stream = stream.stream(entry.compressedSize);
  136. switch (entry.compressionMethod) {
  137. case 0: { // Stored
  138. if (entry.size !== entry.compressedSize) {
  139. throw new zip.Error('Invalid compression size.');
  140. }
  141. break;
  142. }
  143. case 8: {
  144. // Deflate
  145. this._stream = new zip.InflaterStream(this._stream, entry.size);
  146. break;
  147. }
  148. default:
  149. throw new zip.Error('Invalid compression method.');
  150. }
  151. }
  152. get name() {
  153. return this._name;
  154. }
  155. get stream() {
  156. return this._stream;
  157. }
  158. get data() {
  159. return this.stream.peek();
  160. }
  161. };
  162. zip.Inflater = class {
  163. inflateRaw(data, length) {
  164. let buffer = null;
  165. if (typeof process === 'object' && typeof process.versions == 'object' && typeof process.versions.node !== 'undefined') {
  166. buffer = require('zlib').inflateRawSync(data);
  167. }
  168. else if (typeof pako !== 'undefined') {
  169. buffer = pako.inflateRaw(data);
  170. }
  171. else {
  172. const reader = new zip.BitReader(data);
  173. const writer = length === undefined ? new zip.BlockWriter() : new zip.BufferWriter(length);
  174. if (!zip.Inflater._staticLengthTree) {
  175. zip.Inflater._codeLengths = new Uint8Array(19);
  176. zip.Inflater._codeOrder = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
  177. zip.Inflater._lengthBase = [ 24, 32, 40, 48, 56, 64, 72, 80, 89, 105, 121, 137, 154, 186, 218, 250, 283, 347, 411, 475, 540, 668, 796, 924, 1053, 1309, 1565, 1821, 2064, 7992, 7992, 7992 ];
  178. zip.Inflater._distanceBase = [ 16, 32, 48, 64, 81, 113, 146, 210, 275, 403, 532, 788, 1045, 1557, 2070, 3094, 4119, 6167, 8216, 12312, 16409, 24601, 32794, 49178, 65563, 98331, 131100, 196636, 262173, 393245, 1048560, 1048560 ];
  179. }
  180. let type;
  181. do {
  182. type = reader.bits(3);
  183. switch (type >>> 1) {
  184. case 0: { // uncompressed block
  185. this._copyUncompressedBlock(reader, writer);
  186. break;
  187. }
  188. case 1: { // block with fixed huffman trees
  189. if (!zip.Inflater._staticLengthTree) {
  190. zip.Inflater._staticLengthTree = zip.HuffmanTree.create(new Uint8Array([].concat.apply([], [[144, 8], [112, 9], [24, 7], [8, 8]].map((x) => [...Array(x[0])].map(() => x[1])))));
  191. zip.Inflater._staticDistanceTree = zip.HuffmanTree.create(new Uint8Array([...Array(32)].map(() => 5)));
  192. }
  193. this._lengthTree = zip.Inflater._staticLengthTree;
  194. this._distanceTree = zip.Inflater._staticDistanceTree;
  195. this._inflateBlock(reader, writer);
  196. break;
  197. }
  198. case 2: { // block with dynamic huffman trees
  199. this._decodeTrees(reader);
  200. this._inflateBlock(reader, writer);
  201. break;
  202. }
  203. default: {
  204. throw new zip.Error('Unknown block type.');
  205. }
  206. }
  207. } while ((type & 1) == 0);
  208. if (length !== undefined && length !== writer.length) {
  209. throw new zip.Error('Invalid uncompressed size.');
  210. }
  211. buffer = writer.toBuffer();
  212. }
  213. if (length !== undefined && length !== buffer.length) {
  214. throw new zip.Error('Invalid uncompressed size.');
  215. }
  216. return buffer;
  217. }
  218. _copyUncompressedBlock(reader, writer) {
  219. const length = reader.uint16();
  220. const inverseLength = reader.uint16();
  221. if (length !== (~inverseLength & 0xffff)) {
  222. throw new zip.Error('Invalid uncompressed block length.');
  223. }
  224. writer.write(reader.read(length));
  225. }
  226. _decodeTrees(reader) {
  227. const hlit = reader.bits(5) + 257;
  228. const hdist = reader.bits(5) + 1;
  229. const hclen = reader.bits(4) + 4;
  230. const codeLengths = zip.Inflater._codeLengths;
  231. for (let i = 0; i < codeLengths.length; i++) {
  232. codeLengths[i] = 0;
  233. }
  234. const codeOrder = zip.Inflater._codeOrder;
  235. for (let i = 0; i < hclen; i++) {
  236. codeLengths[codeOrder[i]] = reader.bits(3);
  237. }
  238. const codeTree = zip.HuffmanTree.create(codeLengths);
  239. const codeMask = codeTree.length - 1;
  240. const lengths = new Uint8Array(hlit + hdist);
  241. let value = 0;
  242. let length = 0;
  243. for (let i = 0; i < hlit + hdist;) {
  244. const code = codeTree[reader.bits16() & codeMask];
  245. reader.position += code & 0x0f;
  246. const literal = code >>> 4;
  247. switch (literal) {
  248. case 16: length = reader.bits(2) + 3; break;
  249. case 17: length = reader.bits(3) + 3; value = 0; break;
  250. case 18: length = reader.bits(7) + 11; value = 0; break;
  251. default: length = 1; value = literal; break;
  252. }
  253. for (; length > 0; length--) {
  254. lengths[i++] = value;
  255. }
  256. }
  257. this._lengthTree = zip.HuffmanTree.create(lengths.subarray(0, hlit));
  258. this._distanceTree = zip.HuffmanTree.create(lengths.subarray(hlit, hlit + hdist));
  259. }
  260. _inflateBlock(reader, writer) {
  261. const lengthTree = this._lengthTree;
  262. const distanceTree = this._distanceTree;
  263. const lengthMask = lengthTree.length - 1;
  264. const distanceMask = distanceTree.length - 1;
  265. const buffer = writer.buffer;
  266. const threshold = writer.threshold !== undefined ? writer.threshold : writer.length;
  267. let position = writer.position;
  268. for (;;) {
  269. if (position > threshold) {
  270. position = writer.push(position);
  271. }
  272. const code = lengthTree[reader.bits16() & lengthMask];
  273. reader.position += code & 0x0f;
  274. const literal = code >>> 4;
  275. if (literal < 256) {
  276. buffer[position++] = literal;
  277. }
  278. else if (literal === 256) {
  279. writer.push(position);
  280. return;
  281. }
  282. else {
  283. let length = literal - 254;
  284. if (literal > 264) {
  285. const lengthBase = zip.Inflater._lengthBase[literal - 257];
  286. length = (lengthBase >>> 3) + reader.bits(lengthBase & 0x07);
  287. }
  288. const code = distanceTree[reader.bits16() & distanceMask];
  289. reader.position += code & 0x0f;
  290. const distanceBase = zip.Inflater._distanceBase[code >>> 4];
  291. const bits = distanceBase & 0x0f;
  292. const distance = (distanceBase >>> 4) + (reader.bits16() & ((1 << bits) - 1));
  293. reader.position += bits;
  294. let offset = position - distance;
  295. for (let i = 0; i < length; i++) {
  296. buffer[position++] = buffer[offset++];
  297. }
  298. }
  299. }
  300. }
  301. };
  302. zip.HuffmanTree = class {
  303. static create(tree) {
  304. let bits = tree[0];
  305. for (let i = 1; i < tree.length; ++i) {
  306. if (tree[i] > bits) {
  307. bits = tree[i];
  308. }
  309. }
  310. // Algorithm from https://github.com/photopea/UZIP.js
  311. let rev15 = zip.HuffmanTree._rev15;
  312. if (!rev15) {
  313. const length = 1 << 15;
  314. rev15 = new Uint16Array(length);
  315. for (let i = 0; i < length; i++) {
  316. let x = i;
  317. x = (((x & 0xaaaaaaaa) >>> 1) | ((x & 0x55555555) << 1));
  318. x = (((x & 0xcccccccc) >>> 2) | ((x & 0x33333333) << 2));
  319. x = (((x & 0xf0f0f0f0) >>> 4) | ((x & 0x0f0f0f0f) << 4));
  320. x = (((x & 0xff00ff00) >>> 8) | ((x & 0x00ff00ff) << 8));
  321. rev15[i] = (((x >>> 16) | (x << 16))) >>> 17;
  322. }
  323. zip.HuffmanTree._rev15 = rev15;
  324. zip.HuffmanTree._bitLengthCounts = new Uint16Array(16);
  325. zip.HuffmanTree._nextCodes = new Uint16Array(16);
  326. }
  327. const length = tree.length;
  328. const bitLengthCounts = zip.HuffmanTree._bitLengthCounts;
  329. for (let i = 0; i < 16; i++) {
  330. bitLengthCounts[i] = 0;
  331. }
  332. for (let i = 0; i < length; i++) {
  333. bitLengthCounts[tree[i]]++;
  334. }
  335. const nextCodes = zip.HuffmanTree._nextCodes;
  336. let code = 0;
  337. bitLengthCounts[0] = 0;
  338. for (let i = 0; i < bits; i++) {
  339. code = (code + bitLengthCounts[i]) << 1;
  340. nextCodes[i + 1] = code;
  341. }
  342. const codes = new Uint16Array(length);
  343. for (let i = 0; i < length; i++) {
  344. const index = tree[i];
  345. if (index !== 0) {
  346. codes[i] = nextCodes[index];
  347. nextCodes[index]++;
  348. }
  349. }
  350. const shift = 15 - bits;
  351. const table = new Uint16Array(1 << bits);
  352. for (let i = 0; i < length; i++) {
  353. const c = tree[i];
  354. if (c !== 0) {
  355. const value = (i << 4) | c;
  356. const rest = bits - c;
  357. let index = codes[i] << rest;
  358. const max = index + (1 << rest);
  359. for (; index != max; index++) {
  360. table[rev15[index] >>> shift] = value;
  361. }
  362. }
  363. }
  364. return table;
  365. }
  366. };
  367. zip.BitReader = class {
  368. constructor(buffer) {
  369. this.buffer = buffer;
  370. this.position = 0;
  371. }
  372. bits(count) {
  373. const offset = (this.position / 8) >> 0;
  374. const shift = (this.position & 7);
  375. this.position += count;
  376. return ((this.buffer[offset] | (this.buffer[offset + 1] << 8)) >>> shift) & ((1 << count) - 1);
  377. }
  378. bits16() {
  379. const offset = (this.position / 8) >> 0;
  380. return ((this.buffer[offset] | (this.buffer[offset + 1] << 8) | (this.buffer[offset + 2] << 16)) >>> (this.position & 7));
  381. }
  382. read(length) {
  383. this.position = (this.position + 7) & ~7; // align
  384. const offset = (this.position / 8) >> 0;
  385. this.position += length * 8;
  386. return this.buffer.subarray(offset, offset + length);
  387. }
  388. uint16() {
  389. this.position = (this.position + 7) & ~7; // align
  390. const offset = (this.position / 8) >> 0;
  391. this.position += 16;
  392. return this.buffer[offset] | (this.buffer[offset + 1] << 8);
  393. }
  394. };
  395. zip.BlockWriter = class {
  396. constructor() {
  397. this.blocks = [];
  398. this.buffer = new Uint8Array(65536);
  399. this.position = 0;
  400. this.length = 0;
  401. this.threshold = 0xf400;
  402. }
  403. push(position) {
  404. this.blocks.push(new Uint8Array(this.buffer.subarray(this.position, position)));
  405. this.length += position - this.position;
  406. this.position = position;
  407. return this._reset();
  408. }
  409. write(buffer) {
  410. this.blocks.push(buffer);
  411. const length = buffer.length;
  412. this.length += length;
  413. if (length > 32768) {
  414. this.buffer.set(buffer.subarray(length - 32768, length), 0);
  415. this.position = 32768;
  416. }
  417. else {
  418. this._reset();
  419. this.buffer.set(buffer, this.position);
  420. this.position += length;
  421. }
  422. }
  423. toBuffer() {
  424. const buffer = new Uint8Array(this.length);
  425. let offset = 0;
  426. for (const block of this.blocks) {
  427. buffer.set(block, offset);
  428. offset += block.length;
  429. }
  430. return buffer;
  431. }
  432. _reset() {
  433. if (this.position > 32768) {
  434. this.buffer.set(this.buffer.subarray(this.position - 32768, this.position), 0);
  435. this.position = 32768;
  436. }
  437. return this.position;
  438. }
  439. };
  440. zip.BufferWriter = class {
  441. constructor(length) {
  442. this.buffer = new Uint8Array(length);
  443. this.length = length;
  444. this.position = 0;
  445. }
  446. push(position) {
  447. this.position = position;
  448. if (this.position > this.length) {
  449. throw new zip.Error('Invalid size.');
  450. }
  451. return this.position;
  452. }
  453. write(buffer) {
  454. this.buffer.set(buffer, this.position);
  455. this.position += buffer.length;
  456. if (this.position > this.length) {
  457. throw new zip.Error('Invalid size.');
  458. }
  459. return this.position;
  460. }
  461. toBuffer() {
  462. return this.buffer;
  463. }
  464. };
  465. zip.InflaterStream = class {
  466. constructor(stream, length) {
  467. this._stream = stream;
  468. this._position = 0;
  469. this._length = length;
  470. }
  471. get position() {
  472. return this._position;
  473. }
  474. get length() {
  475. return this._length;
  476. }
  477. seek(position) {
  478. if (this._buffer === undefined) {
  479. this._inflate();
  480. }
  481. this._position = position >= 0 ? position : this._length + position;
  482. }
  483. skip(offset) {
  484. if (this._buffer === undefined) {
  485. this._inflate();
  486. }
  487. this._position += offset;
  488. }
  489. peek(length) {
  490. const position = this._position;
  491. length = length !== undefined ? length : this._length - position;
  492. this.skip(length);
  493. const end = this._position;
  494. this.seek(position);
  495. if (position === 0 && length === this._length) {
  496. return this._buffer;
  497. }
  498. return this._buffer.subarray(position, end);
  499. }
  500. read(length) {
  501. const position = this._position;
  502. length = length !== undefined ? length : this._length - position;
  503. this.skip(length);
  504. if (position === 0 && length === this._length) {
  505. return this._buffer;
  506. }
  507. return this._buffer.subarray(position, this._position);
  508. }
  509. byte() {
  510. const position = this._position;
  511. this.skip(1);
  512. return this._buffer[position];
  513. }
  514. _inflate() {
  515. if (this._buffer === undefined) {
  516. const buffer = this._stream.peek();
  517. this._buffer = new zip.Inflater().inflateRaw(buffer, this._length);
  518. delete this._stream;
  519. }
  520. }
  521. };
  522. zip.BinaryReader = class {
  523. constructor(buffer) {
  524. this._buffer = buffer;
  525. this._length = buffer.length;
  526. this._position = 0;
  527. this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  528. }
  529. get position() {
  530. return this._position;
  531. }
  532. get length() {
  533. return this._length;
  534. }
  535. create(buffer) {
  536. return new zip.BinaryReader(buffer);
  537. }
  538. stream(length) {
  539. return this.create(this.read(length));
  540. }
  541. seek(position) {
  542. this._position = position >= 0 ? position : this._length + position;
  543. }
  544. skip(offset) {
  545. this._position += offset;
  546. }
  547. peek(length) {
  548. if (this._position === 0 && length === undefined) {
  549. return this._buffer;
  550. }
  551. const position = this._position;
  552. this.skip(length !== undefined ? length : this._length - this._position);
  553. const end = this._position;
  554. this.seek(position);
  555. return this._buffer.subarray(position, end);
  556. }
  557. read(length) {
  558. if (this._position === 0 && length === undefined) {
  559. this._position = this._length;
  560. return this._buffer;
  561. }
  562. const position = this._position;
  563. this.skip(length !== undefined ? length : this._length - this._position);
  564. return this._buffer.subarray(position, this._position);
  565. }
  566. byte() {
  567. const position = this._position;
  568. this.skip(1);
  569. return this._buffer[position];
  570. }
  571. uint16() {
  572. const position = this._position;
  573. this.skip(2);
  574. return this._view.getUint16(position, true);
  575. }
  576. uint32() {
  577. const position = this._position;
  578. this.skip(4);
  579. return this._view.getUint32(position, true);
  580. }
  581. };
  582. zip.Error = class extends Error {
  583. constructor(message) {
  584. super(message);
  585. this.name = 'Zip Error';
  586. }
  587. };
  588. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  589. module.exports.Archive = zip.Archive;
  590. module.exports.Inflater = zip.Inflater;
  591. }