1/* 2 * JSUnzip 3 * 4 * Copyright (c) 2011 by Erik Moller 5 * All Rights Reserved 6 * 7 * This software is provided 'as-is', without any express 8 * or implied warranty. In no event will the authors be 9 * held liable for any damages arising from the use of 10 * this software. 11 * 12 * Permission is granted to anyone to use this software 13 * for any purpose, including commercial applications, 14 * and to alter it and redistribute it freely, subject to 15 * the following restrictions: 16 * 17 * 1. The origin of this software must not be 18 * misrepresented; you must not claim that you 19 * wrote the original software. If you use this 20 * software in a product, an acknowledgment in 21 * the product documentation would be appreciated 22 * but is not required. 23 * 24 * 2. Altered source versions must be plainly marked 25 * as such, and must not be misrepresented as 26 * being the original software. 27 * 28 * 3. This notice may not be removed or altered from 29 * any source distribution. 30 */ 31 32var tinf; 33 34function JSUnzip() { 35 36 this.getInt = function(offset, size) { 37 switch (size) { 38 case 4: 39 return (this.data.charCodeAt(offset + 3) & 0xff) << 24 | 40 (this.data.charCodeAt(offset + 2) & 0xff) << 16 | 41 (this.data.charCodeAt(offset + 1) & 0xff) << 8 | 42 (this.data.charCodeAt(offset + 0) & 0xff); 43 break; 44 case 2: 45 return (this.data.charCodeAt(offset + 1) & 0xff) << 8 | 46 (this.data.charCodeAt(offset + 0) & 0xff); 47 break; 48 default: 49 return this.data.charCodeAt(offset) & 0xff; 50 break; 51 } 52 }; 53 54 this.getDOSDate = function(dosdate, dostime) { 55 var day = dosdate & 0x1f; 56 var month = ((dosdate >> 5) & 0xf) - 1; 57 var year = 1980 + ((dosdate >> 9) & 0x7f) 58 var second = (dostime & 0x1f) * 2; 59 var minute = (dostime >> 5) & 0x3f; 60 hour = (dostime >> 11) & 0x1f; 61 return new Date(year, month, day, hour, minute, second); 62 } 63 64 this.open = function(data) { 65 this.data = data; 66 this.files = []; 67 68 if (this.data.length < 22) 69 return { 'status' : false, 'error' : 'Invalid data' }; 70 var endOfCentralDirectory = this.data.length - 22; 71 while (endOfCentralDirectory >= 0 && this.getInt(endOfCentralDirectory, 4) != 0x06054b50) 72 --endOfCentralDirectory; 73 if (endOfCentralDirectory < 0) 74 return { 'status' : false, 'error' : 'Invalid data' }; 75 if (this.getInt(endOfCentralDirectory + 4, 2) != 0 || this.getInt(endOfCentralDirectory + 6, 2) != 0) 76 return { 'status' : false, 'error' : 'No multidisk support' }; 77 78 var entriesInThisDisk = this.getInt(endOfCentralDirectory + 8, 2); 79 var centralDirectoryOffset = this.getInt(endOfCentralDirectory + 16, 4); 80 var globalCommentLength = this.getInt(endOfCentralDirectory + 20, 2); 81 this.comment = this.data.slice(endOfCentralDirectory + 22, endOfCentralDirectory + 22 + globalCommentLength); 82 83 var fileOffset = centralDirectoryOffset; 84 85 for (var i = 0; i < entriesInThisDisk; ++i) { 86 if (this.getInt(fileOffset + 0, 4) != 0x02014b50) 87 return { 'status' : false, 'error' : 'Invalid data' }; 88 if (this.getInt(fileOffset + 6, 2) > 20) 89 return { 'status' : false, 'error' : 'Unsupported version' }; 90 if (this.getInt(fileOffset + 8, 2) & 1) 91 return { 'status' : false, 'error' : 'Encryption not implemented' }; 92 93 var compressionMethod = this.getInt(fileOffset + 10, 2); 94 if (compressionMethod != 0 && compressionMethod != 8) 95 return { 'status' : false, 'error' : 'Unsupported compression method' }; 96 97 var lastModFileTime = this.getInt(fileOffset + 12, 2); 98 var lastModFileDate = this.getInt(fileOffset + 14, 2); 99 var lastModifiedDate = this.getDOSDate(lastModFileDate, lastModFileTime); 100 101 var crc = this.getInt(fileOffset + 16, 4); 102 // TODO: crc 103 104 var compressedSize = this.getInt(fileOffset + 20, 4); 105 var uncompressedSize = this.getInt(fileOffset + 24, 4); 106 107 var fileNameLength = this.getInt(fileOffset + 28, 2); 108 var extraFieldLength = this.getInt(fileOffset + 30, 2); 109 var fileCommentLength = this.getInt(fileOffset + 32, 2); 110 111 var relativeOffsetOfLocalHeader = this.getInt(fileOffset + 42, 4); 112 113 var fileName = this.data.slice(fileOffset + 46, fileOffset + 46 + fileNameLength); 114 var fileComment = this.data.slice(fileOffset + 46 + fileNameLength + extraFieldLength, fileOffset + 46 + fileNameLength + extraFieldLength + fileCommentLength); 115 116 if (this.getInt(relativeOffsetOfLocalHeader + 0, 4) != 0x04034b50) 117 return { 'status' : false, 'error' : 'Invalid data' }; 118 var localFileNameLength = this.getInt(relativeOffsetOfLocalHeader + 26, 2); 119 var localExtraFieldLength = this.getInt(relativeOffsetOfLocalHeader + 28, 2); 120 var localFileContent = relativeOffsetOfLocalHeader + 30 + localFileNameLength + localExtraFieldLength; 121 122 this.files[fileName] = 123 { 124 'fileComment' : fileComment, 125 'compressionMethod' : compressionMethod, 126 'compressedSize' : compressedSize, 127 'uncompressedSize' : uncompressedSize, 128 'localFileContent' : localFileContent, 129 'lastModifiedDate' : lastModifiedDate 130 }; 131 132 fileOffset += 46 + fileNameLength + extraFieldLength + fileCommentLength; 133 } 134 return { 'status' : true } 135 }; 136 137 138 this.read = function(fileName) { 139 var fileInfo = this.files[fileName]; 140 if (fileInfo) { 141 if (fileInfo.compressionMethod == 8) { 142 if (!tinf) { 143 tinf = new TINF(); 144 tinf.init(); 145 } 146 var result = tinf.uncompress(this.data, fileInfo.localFileContent); 147 if (result.status == tinf.OK) 148 return { 'status' : true, 'data' : result.data }; 149 else 150 return { 'status' : false, 'error' : result.error }; 151 } else { 152 return { 'status' : true, 'data' : this.data.slice(fileInfo.localFileContent, fileInfo.localFileContent + fileInfo.uncompressedSize) }; 153 } 154 } 155 return { 'status' : false, 'error' : "File '" + fileName + "' doesn't exist in zip" }; 156 }; 157 158}; 159 160 161 162/* 163 * tinflate - tiny inflate 164 * 165 * Copyright (c) 2003 by Joergen Ibsen / Jibz 166 * All Rights Reserved 167 * 168 * http://www.ibsensoftware.com/ 169 * 170 * This software is provided 'as-is', without any express 171 * or implied warranty. In no event will the authors be 172 * held liable for any damages arising from the use of 173 * this software. 174 * 175 * Permission is granted to anyone to use this software 176 * for any purpose, including commercial applications, 177 * and to alter it and redistribute it freely, subject to 178 * the following restrictions: 179 * 180 * 1. The origin of this software must not be 181 * misrepresented; you must not claim that you 182 * wrote the original software. If you use this 183 * software in a product, an acknowledgment in 184 * the product documentation would be appreciated 185 * but is not required. 186 * 187 * 2. Altered source versions must be plainly marked 188 * as such, and must not be misrepresented as 189 * being the original software. 190 * 191 * 3. This notice may not be removed or altered from 192 * any source distribution. 193 */ 194 195/* 196 * tinflate javascript port by Erik Moller in May 2011. 197 * emoller@opera.com 198 * 199 * read_bits() patched by mike@imidio.com to allow 200 * reading more then 8 bits (needed in some zlib streams) 201 */ 202 203"use strict"; 204 205function TINF() { 206 207this.OK = 0; 208this.DATA_ERROR = (-3); 209this.WINDOW_SIZE = 32768; 210 211/* ------------------------------ * 212 * -- internal data structures -- * 213 * ------------------------------ */ 214 215this.TREE = function() { 216 this.table = new Array(16); /* table of code length counts */ 217 this.trans = new Array(288); /* code -> symbol translation table */ 218}; 219 220this.DATA = function(that) { 221 this.source = ''; 222 this.sourceIndex = 0; 223 this.tag = 0; 224 this.bitcount = 0; 225 226 this.dest = []; 227 228 this.history = []; 229 230 this.ltree = new that.TREE(); /* dynamic length/symbol tree */ 231 this.dtree = new that.TREE(); /* dynamic distance tree */ 232}; 233 234/* --------------------------------------------------- * 235 * -- uninitialized global data (static structures) -- * 236 * --------------------------------------------------- */ 237 238this.sltree = new this.TREE(); /* fixed length/symbol tree */ 239this.sdtree = new this.TREE(); /* fixed distance tree */ 240 241/* extra bits and base tables for length codes */ 242this.length_bits = new Array(30); 243this.length_base = new Array(30); 244 245/* extra bits and base tables for distance codes */ 246this.dist_bits = new Array(30); 247this.dist_base = new Array(30); 248 249/* special ordering of code length codes */ 250this.clcidx = [ 251 16, 17, 18, 0, 8, 7, 9, 6, 252 10, 5, 11, 4, 12, 3, 13, 2, 253 14, 1, 15 254]; 255 256/* ----------------------- * 257 * -- utility functions -- * 258 * ----------------------- */ 259 260/* build extra bits and base tables */ 261this.build_bits_base = function(bits, base, delta, first) 262{ 263 var i, sum; 264 265 /* build bits table */ 266 for (i = 0; i < delta; ++i) bits[i] = 0; 267 for (i = 0; i < 30 - delta; ++i) bits[i + delta] = Math.floor(i / delta); 268 269 /* build base table */ 270 for (sum = first, i = 0; i < 30; ++i) 271 { 272 base[i] = sum; 273 sum += 1 << bits[i]; 274 } 275} 276 277/* build the fixed huffman trees */ 278this.build_fixed_trees = function(lt, dt) 279{ 280 var i; 281 282 /* build fixed length tree */ 283 for (i = 0; i < 7; ++i) lt.table[i] = 0; 284 285 lt.table[7] = 24; 286 lt.table[8] = 152; 287 lt.table[9] = 112; 288 289 for (i = 0; i < 24; ++i) lt.trans[i] = 256 + i; 290 for (i = 0; i < 144; ++i) lt.trans[24 + i] = i; 291 for (i = 0; i < 8; ++i) lt.trans[24 + 144 + i] = 280 + i; 292 for (i = 0; i < 112; ++i) lt.trans[24 + 144 + 8 + i] = 144 + i; 293 294 /* build fixed distance tree */ 295 for (i = 0; i < 5; ++i) dt.table[i] = 0; 296 297 dt.table[5] = 32; 298 299 for (i = 0; i < 32; ++i) dt.trans[i] = i; 300} 301 302/* given an array of code lengths, build a tree */ 303this.build_tree = function(t, lengths, loffset, num) 304{ 305 var offs = new Array(16); 306 var i, sum; 307 308 /* clear code length count table */ 309 for (i = 0; i < 16; ++i) t.table[i] = 0; 310 311 /* scan symbol lengths, and sum code length counts */ 312 for (i = 0; i < num; ++i) t.table[lengths[loffset + i]]++; 313 314 t.table[0] = 0; 315 316 /* compute offset table for distribution sort */ 317 for (sum = 0, i = 0; i < 16; ++i) 318 { 319 offs[i] = sum; 320 sum += t.table[i]; 321 } 322 323 /* create code->symbol translation table (symbols sorted by code) */ 324 for (i = 0; i < num; ++i) 325 { 326 if (lengths[loffset + i]) t.trans[offs[lengths[loffset + i]]++] = i; 327 } 328} 329 330/* ---------------------- * 331 * -- decode functions -- * 332 * ---------------------- */ 333 334/* get one bit from source stream */ 335this.getbit = function(d) 336{ 337 var bit; 338 339 /* check if tag is empty */ 340 if (!d.bitcount--) 341 { 342 /* load next tag */ 343 d.tag = d.source[d.sourceIndex++] & 0xff; 344 d.bitcount = 7; 345 } 346 347 /* shift bit out of tag */ 348 bit = d.tag & 0x01; 349 d.tag >>= 1; 350 351 return bit; 352} 353 354/* read a num bit value from a stream and add base */ 355function read_bits_direct(source, bitcount, tag, idx, num) 356{ 357 var val = 0; 358 while (bitcount < 24) { 359 tag = tag | (source[idx++] & 0xff) << bitcount; 360 bitcount += 8; 361 } 362 val = tag & (0xffff >> (16 - num)); 363 tag >>= num; 364 bitcount -= num; 365 return [bitcount, tag, idx, val]; 366} 367this.read_bits = function(d, num, base) 368{ 369 if (!num) 370 return base; 371 372 var ret = read_bits_direct(d.source, d.bitcount, d.tag, d.sourceIndex, num); 373 d.bitcount = ret[0]; 374 d.tag = ret[1]; 375 d.sourceIndex = ret[2]; 376 return ret[3] + base; 377} 378 379/* given a data stream and a tree, decode a symbol */ 380this.decode_symbol = function(d, t) 381{ 382 while (d.bitcount < 16) { 383 d.tag = d.tag | (d.source[d.sourceIndex++] & 0xff) << d.bitcount; 384 d.bitcount += 8; 385 } 386 387 var sum = 0, cur = 0, len = 0; 388 do { 389 cur = 2 * cur + ((d.tag & (1 << len)) >> len); 390 391 ++len; 392 393 sum += t.table[len]; 394 cur -= t.table[len]; 395 396 } while (cur >= 0); 397 398 d.tag >>= len; 399 d.bitcount -= len; 400 401 return t.trans[sum + cur]; 402} 403 404/* given a data stream, decode dynamic trees from it */ 405this.decode_trees = function(d, lt, dt) 406{ 407 var code_tree = new this.TREE(); 408 var lengths = new Array(288+32); 409 var hlit, hdist, hclen; 410 var i, num, length; 411 412 /* get 5 bits HLIT (257-286) */ 413 hlit = this.read_bits(d, 5, 257); 414 415 /* get 5 bits HDIST (1-32) */ 416 hdist = this.read_bits(d, 5, 1); 417 418 /* get 4 bits HCLEN (4-19) */ 419 hclen = this.read_bits(d, 4, 4); 420 421 for (i = 0; i < 19; ++i) lengths[i] = 0; 422 423 /* read code lengths for code length alphabet */ 424 for (i = 0; i < hclen; ++i) 425 { 426 /* get 3 bits code length (0-7) */ 427 var clen = this.read_bits(d, 3, 0); 428 429 lengths[this.clcidx[i]] = clen; 430 } 431 432 /* build code length tree */ 433 this.build_tree(code_tree, lengths, 0, 19); 434 435 /* decode code lengths for the dynamic trees */ 436 for (num = 0; num < hlit + hdist; ) 437 { 438 var sym = this.decode_symbol(d, code_tree); 439 440 switch (sym) 441 { 442 case 16: 443 /* copy previous code length 3-6 times (read 2 bits) */ 444 { 445 var prev = lengths[num - 1]; 446 for (length = this.read_bits(d, 2, 3); length; --length) 447 { 448 lengths[num++] = prev; 449 } 450 } 451 break; 452 case 17: 453 /* repeat code length 0 for 3-10 times (read 3 bits) */ 454 for (length = this.read_bits(d, 3, 3); length; --length) 455 { 456 lengths[num++] = 0; 457 } 458 break; 459 case 18: 460 /* repeat code length 0 for 11-138 times (read 7 bits) */ 461 for (length = this.read_bits(d, 7, 11); length; --length) 462 { 463 lengths[num++] = 0; 464 } 465 break; 466 default: 467 /* values 0-15 represent the actual code lengths */ 468 lengths[num++] = sym; 469 break; 470 } 471 } 472 473 /* build dynamic trees */ 474 this.build_tree(lt, lengths, 0, hlit); 475 this.build_tree(dt, lengths, hlit, hdist); 476} 477 478/* ----------------------------- * 479 * -- block inflate functions -- * 480 * ----------------------------- */ 481 482/* given a stream and two trees, inflate a block of data */ 483this.inflate_block_data = function(d, lt, dt) 484{ 485 // js optimization. 486 var ddest = d.dest; 487 var ddestlength = ddest.length; 488 489 while (1) 490 { 491 var sym = this.decode_symbol(d, lt); 492 493 /* check for end of block */ 494 if (sym == 256) 495 { 496 return this.OK; 497 } 498 499 if (sym < 256) 500 { 501 ddest[ddestlength++] = sym; // ? String.fromCharCode(sym); 502 d.history.push(sym); 503 } else { 504 505 var length, dist, offs; 506 var i; 507 508 sym -= 257; 509 510 /* possibly get more bits from length code */ 511 length = this.read_bits(d, this.length_bits[sym], this.length_base[sym]); 512 513 dist = this.decode_symbol(d, dt); 514 515 /* possibly get more bits from distance code */ 516 offs = d.history.length - this.read_bits(d, this.dist_bits[dist], this.dist_base[dist]); 517 518 if (offs < 0) 519 throw ("Invalid zlib offset " + offs); 520 521 /* copy match */ 522 for (i = offs; i < offs + length; ++i) { 523 //ddest[ddestlength++] = ddest[i]; 524 ddest[ddestlength++] = d.history[i]; 525 d.history.push(d.history[i]); 526 } 527 } 528 } 529} 530 531/* inflate an uncompressed block of data */ 532this.inflate_uncompressed_block = function(d) 533{ 534 var length, invlength; 535 var i; 536 537 if (d.bitcount > 7) { 538 var overflow = Math.floor(d.bitcount / 8); 539 d.sourceIndex -= overflow; 540 d.bitcount = 0; 541 d.tag = 0; 542 } 543 544 /* get length */ 545 length = d.source[d.sourceIndex+1]; 546 length = 256*length + d.source[d.sourceIndex]; 547 548 /* get one's complement of length */ 549 invlength = d.source[d.sourceIndex+3]; 550 invlength = 256*invlength + d.source[d.sourceIndex+2]; 551 552 /* check length */ 553 if (length != (~invlength & 0x0000ffff)) return this.DATA_ERROR; 554 555 d.sourceIndex += 4; 556 557 /* copy block */ 558 for (i = length; i; --i) { 559 d.history.push(d.source[d.sourceIndex]); 560 d.dest[d.dest.length] = d.source[d.sourceIndex++]; 561 } 562 563 /* make sure we start next block on a byte boundary */ 564 d.bitcount = 0; 565 566 return this.OK; 567} 568 569/* inflate a block of data compressed with fixed huffman trees */ 570this.inflate_fixed_block = function(d) 571{ 572 /* decode block using fixed trees */ 573 return this.inflate_block_data(d, this.sltree, this.sdtree); 574} 575 576/* inflate a block of data compressed with dynamic huffman trees */ 577this.inflate_dynamic_block = function(d) 578{ 579 /* decode trees from stream */ 580 this.decode_trees(d, d.ltree, d.dtree); 581 582 /* decode block using decoded trees */ 583 return this.inflate_block_data(d, d.ltree, d.dtree); 584} 585 586/* ---------------------- * 587 * -- public functions -- * 588 * ---------------------- */ 589 590/* initialize global (static) data */ 591this.init = function() 592{ 593 /* build fixed huffman trees */ 594 this.build_fixed_trees(this.sltree, this.sdtree); 595 596 /* build extra bits and base tables */ 597 this.build_bits_base(this.length_bits, this.length_base, 4, 3); 598 this.build_bits_base(this.dist_bits, this.dist_base, 2, 1); 599 600 /* fix a special case */ 601 this.length_bits[28] = 0; 602 this.length_base[28] = 258; 603 604 this.reset(); 605} 606 607this.reset = function() 608{ 609 this.d = new this.DATA(this); 610 delete this.header; 611} 612 613/* inflate stream from source to dest */ 614this.uncompress = function(source, offset) 615{ 616 617 var d = this.d; 618 var bfinal; 619 620 /* initialise data */ 621 d.source = source; 622 d.sourceIndex = offset; 623 d.bitcount = 0; 624 625 d.dest = []; 626 627 // Skip zlib header at start of stream 628 if (typeof this.header == 'undefined') { 629 this.header = this.read_bits(d, 16, 0); 630 /* byte 0: 0x78, 7 = 32k window size, 8 = deflate */ 631 /* byte 1: check bits for header and other flags */ 632 } 633 634 var blocks = 0; 635 636 do { 637 638 var btype; 639 var res; 640 641 /* read final block flag */ 642 bfinal = this.getbit(d); 643 644 /* read block type (2 bits) */ 645 btype = this.read_bits(d, 2, 0); 646 647 /* decompress block */ 648 switch (btype) 649 { 650 case 0: 651 /* decompress uncompressed block */ 652 res = this.inflate_uncompressed_block(d); 653 break; 654 case 1: 655 /* decompress block with fixed huffman trees */ 656 res = this.inflate_fixed_block(d); 657 break; 658 case 2: 659 /* decompress block with dynamic huffman trees */ 660 res = this.inflate_dynamic_block(d); 661 break; 662 default: 663 return { 'status' : this.DATA_ERROR }; 664 } 665 666 if (res != this.OK) return { 'status' : this.DATA_ERROR }; 667 blocks++; 668 669 } while (!bfinal && d.sourceIndex < d.source.length); 670 671 d.history = d.history.slice(-this.WINDOW_SIZE); 672 673 return { 'status' : this.OK, 'data' : d.dest }; 674} 675 676}; 677