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 */ 355this.read_bits = function(d, num, base) 356{ 357 if (!num) 358 return base; 359 360 var val = 0; 361 while (d.bitcount < 24) { 362 d.tag = d.tag | (d.source[d.sourceIndex++] & 0xff) << d.bitcount; 363 d.bitcount += 8; 364 } 365 val = d.tag & (0xffff >> (16 - num)); 366 d.tag >>= num; 367 d.bitcount -= num; 368 return val + base; 369} 370 371/* given a data stream and a tree, decode a symbol */ 372this.decode_symbol = function(d, t) 373{ 374 while (d.bitcount < 16) { 375 d.tag = d.tag | (d.source[d.sourceIndex++] & 0xff) << d.bitcount; 376 d.bitcount += 8; 377 } 378 379 var sum = 0, cur = 0, len = 0; 380 do { 381 cur = 2 * cur + ((d.tag & (1 << len)) >> len); 382 383 ++len; 384 385 sum += t.table[len]; 386 cur -= t.table[len]; 387 388 } while (cur >= 0); 389 390 d.tag >>= len; 391 d.bitcount -= len; 392 393 return t.trans[sum + cur]; 394} 395 396/* given a data stream, decode dynamic trees from it */ 397this.decode_trees = function(d, lt, dt) 398{ 399 var code_tree = new this.TREE(); 400 var lengths = new Array(288+32); 401 var hlit, hdist, hclen; 402 var i, num, length; 403 404 /* get 5 bits HLIT (257-286) */ 405 hlit = this.read_bits(d, 5, 257); 406 407 /* get 5 bits HDIST (1-32) */ 408 hdist = this.read_bits(d, 5, 1); 409 410 /* get 4 bits HCLEN (4-19) */ 411 hclen = this.read_bits(d, 4, 4); 412 413 for (i = 0; i < 19; ++i) lengths[i] = 0; 414 415 /* read code lengths for code length alphabet */ 416 for (i = 0; i < hclen; ++i) 417 { 418 /* get 3 bits code length (0-7) */ 419 var clen = this.read_bits(d, 3, 0); 420 421 lengths[this.clcidx[i]] = clen; 422 } 423 424 /* build code length tree */ 425 this.build_tree(code_tree, lengths, 0, 19); 426 427 /* decode code lengths for the dynamic trees */ 428 for (num = 0; num < hlit + hdist; ) 429 { 430 var sym = this.decode_symbol(d, code_tree); 431 432 switch (sym) 433 { 434 case 16: 435 /* copy previous code length 3-6 times (read 2 bits) */ 436 { 437 var prev = lengths[num - 1]; 438 for (length = this.read_bits(d, 2, 3); length; --length) 439 { 440 lengths[num++] = prev; 441 } 442 } 443 break; 444 case 17: 445 /* repeat code length 0 for 3-10 times (read 3 bits) */ 446 for (length = this.read_bits(d, 3, 3); length; --length) 447 { 448 lengths[num++] = 0; 449 } 450 break; 451 case 18: 452 /* repeat code length 0 for 11-138 times (read 7 bits) */ 453 for (length = this.read_bits(d, 7, 11); length; --length) 454 { 455 lengths[num++] = 0; 456 } 457 break; 458 default: 459 /* values 0-15 represent the actual code lengths */ 460 lengths[num++] = sym; 461 break; 462 } 463 } 464 465 /* build dynamic trees */ 466 this.build_tree(lt, lengths, 0, hlit); 467 this.build_tree(dt, lengths, hlit, hdist); 468} 469 470/* ----------------------------- * 471 * -- block inflate functions -- * 472 * ----------------------------- */ 473 474/* given a stream and two trees, inflate a block of data */ 475this.inflate_block_data = function(d, lt, dt) 476{ 477 // js optimization. 478 var ddest = d.dest; 479 var ddestlength = ddest.length; 480 481 while (1) 482 { 483 var sym = this.decode_symbol(d, lt); 484 485 /* check for end of block */ 486 if (sym == 256) 487 { 488 return this.OK; 489 } 490 491 if (sym < 256) 492 { 493 ddest[ddestlength++] = sym; // ? String.fromCharCode(sym); 494 d.history.push(sym); 495 } else { 496 497 var length, dist, offs; 498 var i; 499 500 sym -= 257; 501 502 /* possibly get more bits from length code */ 503 length = this.read_bits(d, this.length_bits[sym], this.length_base[sym]); 504 505 dist = this.decode_symbol(d, dt); 506 507 /* possibly get more bits from distance code */ 508 offs = d.history.length - this.read_bits(d, this.dist_bits[dist], this.dist_base[dist]); 509 510 if (offs < 0) 511 throw ("Invalid zlib offset " + offs); 512 513 /* copy match */ 514 for (i = offs; i < offs + length; ++i) { 515 //ddest[ddestlength++] = ddest[i]; 516 ddest[ddestlength++] = d.history[i]; 517 d.history.push(d.history[i]); 518 } 519 } 520 } 521} 522 523/* inflate an uncompressed block of data */ 524this.inflate_uncompressed_block = function(d) 525{ 526 var length, invlength; 527 var i; 528 529 if (d.bitcount > 7) { 530 var overflow = Math.floor(d.bitcount / 8); 531 d.sourceIndex -= overflow; 532 d.bitcount = 0; 533 d.tag = 0; 534 } 535 536 /* get length */ 537 length = d.source[d.sourceIndex+1]; 538 length = 256*length + d.source[d.sourceIndex]; 539 540 /* get one's complement of length */ 541 invlength = d.source[d.sourceIndex+3]; 542 invlength = 256*invlength + d.source[d.sourceIndex+2]; 543 544 /* check length */ 545 if (length != (~invlength & 0x0000ffff)) return this.DATA_ERROR; 546 547 d.sourceIndex += 4; 548 549 /* copy block */ 550 for (i = length; i; --i) { 551 d.history.push(d.source[d.sourceIndex]); 552 d.dest[d.dest.length] = d.source[d.sourceIndex++]; 553 } 554 555 /* make sure we start next block on a byte boundary */ 556 d.bitcount = 0; 557 558 return this.OK; 559} 560 561/* inflate a block of data compressed with fixed huffman trees */ 562this.inflate_fixed_block = function(d) 563{ 564 /* decode block using fixed trees */ 565 return this.inflate_block_data(d, this.sltree, this.sdtree); 566} 567 568/* inflate a block of data compressed with dynamic huffman trees */ 569this.inflate_dynamic_block = function(d) 570{ 571 /* decode trees from stream */ 572 this.decode_trees(d, d.ltree, d.dtree); 573 574 /* decode block using decoded trees */ 575 return this.inflate_block_data(d, d.ltree, d.dtree); 576} 577 578/* ---------------------- * 579 * -- public functions -- * 580 * ---------------------- */ 581 582/* initialize global (static) data */ 583this.init = function() 584{ 585 /* build fixed huffman trees */ 586 this.build_fixed_trees(this.sltree, this.sdtree); 587 588 /* build extra bits and base tables */ 589 this.build_bits_base(this.length_bits, this.length_base, 4, 3); 590 this.build_bits_base(this.dist_bits, this.dist_base, 2, 1); 591 592 /* fix a special case */ 593 this.length_bits[28] = 0; 594 this.length_base[28] = 258; 595 596 this.reset(); 597} 598 599this.reset = function() 600{ 601 this.d = new this.DATA(this); 602 delete this.header; 603} 604 605/* inflate stream from source to dest */ 606this.uncompress = function(source, offset) 607{ 608 609 var d = this.d; 610 var bfinal; 611 612 /* initialise data */ 613 d.source = source; 614 d.sourceIndex = offset; 615 d.bitcount = 0; 616 617 d.dest = []; 618 619 // Skip zlib header at start of stream 620 if (typeof this.header == 'undefined') { 621 this.header = this.read_bits(d, 16, 0); 622 /* byte 0: 0x78, 7 = 32k window size, 8 = deflate */ 623 /* byte 1: check bits for header and other flags */ 624 } 625 626 var blocks = 0; 627 628 do { 629 630 var btype; 631 var res; 632 633 /* read final block flag */ 634 bfinal = this.getbit(d); 635 636 /* read block type (2 bits) */ 637 btype = this.read_bits(d, 2, 0); 638 639 /* decompress block */ 640 switch (btype) 641 { 642 case 0: 643 /* decompress uncompressed block */ 644 res = this.inflate_uncompressed_block(d); 645 break; 646 case 1: 647 /* decompress block with fixed huffman trees */ 648 res = this.inflate_fixed_block(d); 649 break; 650 case 2: 651 /* decompress block with dynamic huffman trees */ 652 res = this.inflate_dynamic_block(d); 653 break; 654 default: 655 return { 'status' : this.DATA_ERROR }; 656 } 657 658 if (res != this.OK) return { 'status' : this.DATA_ERROR }; 659 blocks++; 660 661 } while (!bfinal && d.sourceIndex < d.source.length); 662 663 d.history = d.history.slice(-this.WINDOW_SIZE); 664 665 return { 'status' : this.OK, 'data' : d.dest }; 666} 667 668}; 669