├── FileSaver.min.js ├── README.md ├── create_texture.js ├── index.html ├── index_backup.html ├── jszip.min.js ├── script.js └── styles.css /FileSaver.min.js: -------------------------------------------------------------------------------- 1 | (function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open("GET",a),d.responseType="blob",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error("could not download file")},d.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open("","_blank"),g&&(g.document.title=g.document.body.innerText="downloading..."),"string"==typeof b)return c(b,d,e);var h="application/octet-stream"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\/[\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&"undefined"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,"undefined"!=typeof module&&(module.exports=g)}); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | You can see the wad-editor in action at http://j0e.io/tools/wad-editor 2 | -------------------------------------------------------------------------------- /create_texture.js: -------------------------------------------------------------------------------- 1 | function createPalette(size, buffer) { 2 | 3 | //Create a frequency list of all colors in the buffer 4 | var colors = []; 5 | 6 | // For every pixel 7 | for (var i = 0; i < buffer.length; i += 4) { 8 | 9 | // Get the rgb values of the current pixel 10 | var r = buffer[i+0]; 11 | var g = buffer[i+1]; 12 | var b = buffer[i+2]; 13 | 14 | // Compare against the already catalogued colors list 15 | var found = false; 16 | for (var j = 0; j < colors.length; j++) { 17 | 18 | // If color is already noted, add to its frequency count 19 | if (colors[j].r == r && colors[j].g == g && colors[j].b == b) { 20 | colors[j].frequency++; 21 | found = true; 22 | break; 23 | } 24 | } 25 | 26 | // If color not found in the loop, this is a new color, add it 27 | if (!found) 28 | colors.push({r:r, g:g, b:b, frequency:0}); 29 | 30 | } 31 | 32 | // Sory by frequency 33 | colors.sort( function(a, b) {return b.frequency - a.frequency;}); 34 | 35 | // Get ready to form the palette 36 | var palette = []; 37 | 38 | // If the number of colors is <= the palette size, just add all the colors 39 | if (colors.length < size) { 40 | for (var i = 0; i < colors.length; i++) 41 | palette.push(colors[i]); 42 | } 43 | else { 44 | // How far to step in between in colors to get the widest possible range of the frequencies 45 | var step = Math.floor(colors.length / size); 46 | 47 | // Add the colors to the palette 48 | for (var i = 0, color = 0; i < size; i++, color += step) { 49 | palette.push(colors[color]); 50 | 51 | // Make sure the colors we skip over arent 0,0,255 BLUE, if so, add it 52 | for (var j = 1; j < step; j++) { 53 | 54 | // Get the current skipped color 55 | var skipped = colors[color+j]; 56 | 57 | // Check if skipped color is 0,0,255 blue 58 | if (skipped.r == 0 && skipped.g == 0 && skipped.b == 255) { 59 | 60 | // Make sure were not at the end of the palette before pushing blue 61 | if (i < 255) { 62 | // Add blue to palette 63 | palette.push({r:0, g:0, b:255}); 64 | } 65 | else { 66 | // Replace last index with blue, who cares what that final low-freq color was anyway 67 | palette[255] = {r:0, g:0, b:255}; 68 | } 69 | 70 | // update the palette index so we don't add extra colors beyond index 255 71 | i++; 72 | } 73 | } 74 | } 75 | } 76 | 77 | // Check for blue 0 0 255 (Half-Life's transparency color) and put it in position 255 78 | var blueIndex = -1; 79 | for (var i = 0; i < palette.length; i++) { 80 | if (palette[i].r == 0 && palette[i].g == 0 && palette[i].b == 255) { 81 | blueIndex = i; 82 | break; 83 | } 84 | } 85 | 86 | // If blue was found 87 | if (blueIndex != -1) { 88 | 89 | // Is the palette is not full, just push blue in the back (after some padding). 90 | if (palette.length < 256) { 91 | 92 | // If the palette is not the full 256 length, fill it out. Blue must be in position 255. 93 | for (var i = palette.length - 1; i < 255; i++) 94 | palette.push({r:0, g:0, b:255}); 95 | } 96 | // If the palette is full 97 | else { 98 | // Swap color at index 255 with blue 99 | var swapColor = palette[255]; 100 | palette[255] = palette[blueIndex]; 101 | palette[blueIndex] = swapColor; 102 | } 103 | } 104 | 105 | return palette; 106 | } 107 | 108 | 109 | function pixelsToPalette(buffer, palette) { 110 | var newBuffer = []; 111 | // For every pixel 112 | for (var bufIndex = 0; bufIndex < buffer.length; bufIndex+=4) { 113 | 114 | // Collect current pixel 115 | var r = buffer[bufIndex + 0]; 116 | var g = buffer[bufIndex + 1]; 117 | var b = buffer[bufIndex + 2]; 118 | 119 | // Find the closest color within the palette 120 | var closest = 16777216; // Start with max possible cubed distance 121 | var closestIndex = -1; 122 | for (var i = 0; i < palette.length; i++) { 123 | // Get the current color 124 | var color = palette[i]; 125 | 126 | // Calculate the distance between the image pixel's color and the current entry in the palette 127 | var dist = (r - color.r)*(r - color.r) + (g - color.g)*(g - color.g) + (b - color.b)*(b - color.b); 128 | 129 | // If this is the closest distance yet, save it 130 | if (dist < closest) { 131 | closest = dist; 132 | closestIndex = i; 133 | } 134 | } 135 | 136 | // Store closest color 137 | newBuffer.push(closestIndex); 138 | } 139 | 140 | return newBuffer; 141 | } 142 | 143 | 144 | 145 | function generateMipTexture(buffer, imgWidth) { 146 | var mipBuffer = []; 147 | 148 | var width = imgWidth * 4; // The buffer is made of RGBA qaudruplets, so the true buffer width is 4 times the image width 149 | 150 | // For every other pixel in the buffer 151 | for (var i = 0; i < buffer.length-width; i+=8) { 152 | 153 | // Only visit every other row 154 | if (i % (width) == 0 && i != 0) 155 | i += width; 156 | 157 | // Average the surrounding colors (2 right and 2 down) 158 | var r = Math.round((buffer[i+0] + buffer[i+4+0] + buffer[i+width+0] + buffer[i+4+width+0]) / 4); 159 | var g = Math.round((buffer[i+1] + buffer[i+4+1] + buffer[i+width+1] + buffer[i+4+width+1]) / 4); 160 | var b = Math.round((buffer[i+2] + buffer[i+4+2] + buffer[i+width+2] + buffer[i+4+width+2]) / 4); 161 | 162 | // Add new RGBA quadruplet to MIP buffer 163 | mipBuffer.push(r); 164 | mipBuffer.push(g); 165 | mipBuffer.push(b); 166 | mipBuffer.push(255); 167 | } 168 | return mipBuffer; 169 | } 170 | 171 | 172 | 173 | self.onmessage = function(event) { 174 | self.postMessage({type: "status", msg: "Beginning"}); 175 | 176 | // Get the incoming pixel buffer 177 | var rgbBuffer = event.data.buffer; 178 | 179 | // Create the palette 180 | self.postMessage({type: "status", msg: "Creating palette"}); 181 | var palette = createPalette(256, rgbBuffer); 182 | 183 | // Create the MIP Textures 184 | self.postMessage({type: "status", msg: "Generating MIP Textures"}); 185 | var mipLevel1 = generateMipTexture(rgbBuffer, event.data.width); 186 | var mipLevel2 = generateMipTexture(mipLevel1, event.data.width/2); 187 | var mipLevel3 = generateMipTexture(mipLevel2, event.data.width/4); 188 | 189 | // Convert the pixel buffer to palette-reference form 190 | self.postMessage({type: "status", msg: "Palettizing image"}); 191 | var refBuffer = pixelsToPalette(rgbBuffer, palette); 192 | 193 | // Convert the MIP Textures to palette-reference form 194 | self.postMessage({type: "status", msg: "Palettizing MIP Textures"}); 195 | mipLevel1 = pixelsToPalette(mipLevel1, palette); 196 | mipLevel2 = pixelsToPalette(mipLevel2, palette); 197 | mipLevel3 = pixelsToPalette(mipLevel3, palette); 198 | 199 | // Create the Texture object 200 | var texture = {}; 201 | texture.palette = palette; 202 | texture.height = event.data.height; 203 | texture.width = event.data.width; 204 | texture.mipLevel = new Array(4); 205 | texture.mipLevel[0] = refBuffer; 206 | texture.mipLevel[1] = mipLevel1; 207 | texture.mipLevel[2] = mipLevel2; 208 | texture.mipLevel[3] = mipLevel3; 209 | 210 | // Make sure the filename/texturename isnt too long 211 | var name = event.data.name; 212 | if (name.length > 11) 213 | texture.name = name.slice(0, 12); 214 | else 215 | texture.name = name; 216 | 217 | self.postMessage({type: "status", msg: "Complete"}); 218 | self.postMessage({type:"data", texture:texture}); 219 | } 220 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Joe's Wad Editor 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Joe's WAD Editor

12 |

Create or modify Half-Life WAD3 files

13 | 14 | 15 | 16 | 35 | 36 | 39 | 42 | 43 | 44 | 45 | 48 | 49 |
17 | 18 |

19 | Open WAD 20 | 21 |

22 | Add Image 23 | 24 |

25 | Rename (12 chars MAX) 26 | 27 |

28 |
29 | 30 | 31 | 32 |
33 |
34 |
37 | 38 | 40 | 41 |
46 | 47 |
50 | You can contribute to this project via github here. 51 | 52 | 53 | -------------------------------------------------------------------------------- /index_backup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Joe's Wad Editor 4 | 5 | 6 | 7 | 8 | 9 |

Joe's WAD Editor

10 |

Create or modify Half-Life WAD3 files

11 | 12 |
13 | 14 |
15 | Upload WAD 16 | 17 |
18 | 19 |
20 | Add Image 21 | 22 |
23 | 24 |
25 | 26 | 27 |
28 | 29 | 30 |
31 | 32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /jszip.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZip v3.7.1 - A JavaScript class for generating and reading zip files 4 | 5 | 6 | (c) 2009-2016 Stuart Knightley 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown. 8 | 9 | JSZip uses the library pako released under the MIT license : 10 | https://github.com/nodeca/pako/blob/master/LICENSE 11 | */ 12 | 13 | !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JSZip=t()}}(function(){return function s(a,o,h){function u(r,t){if(!o[r]){if(!a[r]){var e="function"==typeof require&&require;if(!t&&e)return e(r,!0);if(l)return l(r,!0);var i=new Error("Cannot find module '"+r+"'");throw i.code="MODULE_NOT_FOUND",i}var n=o[r]={exports:{}};a[r][0].call(n.exports,function(t){var e=a[r][1][t];return u(e||t)},n,n.exports,s,a,o,h)}return o[r].exports}for(var l="function"==typeof require&&require,t=0;t>2,s=(3&e)<<4|r>>4,a=1>6:64,o=2>4,r=(15&n)<<4|(s=p.indexOf(t.charAt(o++)))>>2,i=(3&s)<<6|(a=p.indexOf(t.charAt(o++))),l[h++]=e,64!==s&&(l[h++]=r),64!==a&&(l[h++]=i);return l}},{"./support":30,"./utils":32}],2:[function(t,e,r){"use strict";var i=t("./external"),n=t("./stream/DataWorker"),s=t("./stream/Crc32Probe"),a=t("./stream/DataLengthProbe");function o(t,e,r,i,n){this.compressedSize=t,this.uncompressedSize=e,this.crc32=r,this.compression=i,this.compressedContent=n}o.prototype={getContentWorker:function(){var t=new n(i.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),e=this;return t.on("end",function(){if(this.streamInfo.data_length!==e.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),t},getCompressedWorker:function(){return new n(i.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(t,e,r){return t.pipe(new s).pipe(new a("uncompressedSize")).pipe(e.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",e)},e.exports=o},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(t,e,r){"use strict";var i=t("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(t){return new i("STORE compression")},uncompressWorker:function(){return new i("STORE decompression")}},r.DEFLATE=t("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(t,e,r){"use strict";var i=t("./utils");var o=function(){for(var t,e=[],r=0;r<256;r++){t=r;for(var i=0;i<8;i++)t=1&t?3988292384^t>>>1:t>>>1;e[r]=t}return e}();e.exports=function(t,e){return void 0!==t&&t.length?"string"!==i.getTypeOf(t)?function(t,e,r,i){var n=o,s=i+r;t^=-1;for(var a=i;a>>8^n[255&(t^e[a])];return-1^t}(0|e,t,t.length,0):function(t,e,r,i){var n=o,s=i+r;t^=-1;for(var a=i;a>>8^n[255&(t^e.charCodeAt(a))];return-1^t}(0|e,t,t.length,0):0}},{"./utils":32}],5:[function(t,e,r){"use strict";r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null},{}],6:[function(t,e,r){"use strict";var i=null;i="undefined"!=typeof Promise?Promise:t("lie"),e.exports={Promise:i}},{lie:37}],7:[function(t,e,r){"use strict";var i="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,n=t("pako"),s=t("./utils"),a=t("./stream/GenericWorker"),o=i?"uint8array":"array";function h(t,e){a.call(this,"FlateWorker/"+t),this._pako=null,this._pakoAction=t,this._pakoOptions=e,this.meta={}}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(t){this.meta=t.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,t.data),!1)},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0)},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null},h.prototype._createPako=function(){this._pako=new n[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var e=this;this._pako.onData=function(t){e.push({data:t,meta:e.meta})}},r.compressWorker=function(t){return new h("Deflate",t)},r.uncompressWorker=function(){return new h("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(t,e,r){"use strict";function A(t,e){var r,i="";for(r=0;r>>=8;return i}function i(t,e,r,i,n,s){var a,o,h=t.file,u=t.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),d=I.transformTo("string",O.utf8encode(h.name)),c=h.comment,p=I.transformTo("string",s(c)),m=I.transformTo("string",O.utf8encode(c)),_=d.length!==h.name.length,g=m.length!==c.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};e&&!r||(x.crc32=t.crc32,x.compressedSize=t.compressedSize,x.uncompressedSize=t.uncompressedSize);var S=0;e&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===n?(C=798,z|=function(t,e){var r=t;return t||(r=e?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(t){return 63&(t||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+d,b+="up"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+="uc"+A(y.length,2)+y);var E="";return E+="\n\0",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+"\0\0\0\0"+A(z,4)+A(i,4)+f+b+p}}var I=t("../utils"),n=t("../stream/GenericWorker"),O=t("../utf8"),B=t("../crc32"),R=t("../signature");function s(t,e,r,i){n.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=e,this.zipPlatform=r,this.encodeFileName=i,this.streamFiles=t,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}I.inherits(s,n),s.prototype.push=function(t){var e=t.meta.percent||0,r=this.entriesCount,i=this._sources.length;this.accumulate?this.contentBuffer.push(t):(this.bytesWritten+=t.data.length,n.prototype.push.call(this,{data:t.data,meta:{currentFile:this.currentFile,percent:r?(e+100*(r-i-1))/r:100}}))},s.prototype.openedSource=function(t){this.currentSourceOffset=this.bytesWritten,this.currentFile=t.file.name;var e=this.streamFiles&&!t.file.dir;if(e){var r=i(t,e,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}})}else this.accumulate=!0},s.prototype.closedSource=function(t){this.accumulate=!1;var e=this.streamFiles&&!t.file.dir,r=i(t,e,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),e)this.push({data:function(t){return R.DATA_DESCRIPTOR+A(t.crc32,4)+A(t.compressedSize,4)+A(t.uncompressedSize,4)}(t),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},s.prototype.flush=function(){for(var t=this.bytesWritten,e=0;e=this.index;e--)r=(r<<8)+this.byteAt(e);return this.index+=t,r},readString:function(t){return i.transformTo("string",this.readData(t))},readData:function(t){},lastIndexOfSignature:function(t){},readAndCheckSignature:function(t){},readDate:function(){var t=this.readInt(4);return new Date(Date.UTC(1980+(t>>25&127),(t>>21&15)-1,t>>16&31,t>>11&31,t>>5&63,(31&t)<<1))}},e.exports=n},{"../utils":32}],19:[function(t,e,r){"use strict";var i=t("./Uint8ArrayReader");function n(t){i.call(this,t)}t("../utils").inherits(n,i),n.prototype.readData=function(t){this.checkOffset(t);var e=this.data.slice(this.zero+this.index,this.zero+this.index+t);return this.index+=t,e},e.exports=n},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(t,e,r){"use strict";var i=t("./DataReader");function n(t){i.call(this,t)}t("../utils").inherits(n,i),n.prototype.byteAt=function(t){return this.data.charCodeAt(this.zero+t)},n.prototype.lastIndexOfSignature=function(t){return this.data.lastIndexOf(t)-this.zero},n.prototype.readAndCheckSignature=function(t){return t===this.readData(4)},n.prototype.readData=function(t){this.checkOffset(t);var e=this.data.slice(this.zero+this.index,this.zero+this.index+t);return this.index+=t,e},e.exports=n},{"../utils":32,"./DataReader":18}],21:[function(t,e,r){"use strict";var i=t("./ArrayReader");function n(t){i.call(this,t)}t("../utils").inherits(n,i),n.prototype.readData=function(t){if(this.checkOffset(t),0===t)return new Uint8Array(0);var e=this.data.subarray(this.zero+this.index,this.zero+this.index+t);return this.index+=t,e},e.exports=n},{"../utils":32,"./ArrayReader":17}],22:[function(t,e,r){"use strict";var i=t("../utils"),n=t("../support"),s=t("./ArrayReader"),a=t("./StringReader"),o=t("./NodeBufferReader"),h=t("./Uint8ArrayReader");e.exports=function(t){var e=i.getTypeOf(t);return i.checkSupport(e),"string"!==e||n.uint8array?"nodebuffer"===e?new o(t):n.uint8array?new h(i.transformTo("uint8array",t)):new s(i.transformTo("array",t)):new a(t)}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(t,e,r){"use strict";r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\b"},{}],24:[function(t,e,r){"use strict";var i=t("./GenericWorker"),n=t("../utils");function s(t){i.call(this,"ConvertWorker to "+t),this.destType=t}n.inherits(s,i),s.prototype.processChunk=function(t){this.push({data:n.transformTo(this.destType,t.data),meta:t.meta})},e.exports=s},{"../utils":32,"./GenericWorker":28}],25:[function(t,e,r){"use strict";var i=t("./GenericWorker"),n=t("../crc32");function s(){i.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}t("../utils").inherits(s,i),s.prototype.processChunk=function(t){this.streamInfo.crc32=n(t.data,this.streamInfo.crc32||0),this.push(t)},e.exports=s},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(t,e,r){"use strict";var i=t("../utils"),n=t("./GenericWorker");function s(t){n.call(this,"DataLengthProbe for "+t),this.propName=t,this.withStreamInfo(t,0)}i.inherits(s,n),s.prototype.processChunk=function(t){if(t){var e=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=e+t.data.length}n.prototype.processChunk.call(this,t)},e.exports=s},{"../utils":32,"./GenericWorker":28}],27:[function(t,e,r){"use strict";var i=t("../utils"),n=t("./GenericWorker");function s(t){n.call(this,"DataWorker");var e=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,t.then(function(t){e.dataIsReady=!0,e.data=t,e.max=t&&t.length||0,e.type=i.getTypeOf(t),e.isPaused||e._tickAndRepeat()},function(t){e.error(t)})}i.inherits(s,n),s.prototype.cleanUp=function(){n.prototype.cleanUp.call(this),this.data=null},s.prototype.resume=function(){return!!n.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,i.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(i.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var t=null,e=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":t=this.data.substring(this.index,e);break;case"uint8array":t=this.data.subarray(this.index,e);break;case"array":case"nodebuffer":t=this.data.slice(this.index,e)}return this.index=e,this.push({data:t,meta:{percent:this.max?this.index/this.max*100:0}})},e.exports=s},{"../utils":32,"./GenericWorker":28}],28:[function(t,e,r){"use strict";function i(t){this.name=t||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}i.prototype={push:function(t){this.emit("data",t)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(t){this.emit("error",t)}return!0},error:function(t){return!this.isFinished&&(this.isPaused?this.generatedError=t:(this.isFinished=!0,this.emit("error",t),this.previous&&this.previous.error(t),this.cleanUp()),!0)},on:function(t,e){return this._listeners[t].push(e),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(t,e){if(this._listeners[t])for(var r=0;r "+t:t}},e.exports=i},{}],29:[function(t,e,r){"use strict";var h=t("../utils"),n=t("./ConvertWorker"),s=t("./GenericWorker"),u=t("../base64"),i=t("../support"),a=t("../external"),o=null;if(i.nodestream)try{o=t("../nodejs/NodejsStreamOutputAdapter")}catch(t){}function l(t,o){return new a.Promise(function(e,r){var i=[],n=t._internalType,s=t._outputType,a=t._mimeType;t.on("data",function(t,e){i.push(t),o&&o(e)}).on("error",function(t){i=[],r(t)}).on("end",function(){try{var t=function(t,e,r){switch(t){case"blob":return h.newBlob(h.transformTo("arraybuffer",e),r);case"base64":return u.encode(e);default:return h.transformTo(t,e)}}(s,function(t,e){var r,i=0,n=null,s=0;for(r=0;r>>6:(r<65536?e[s++]=224|r>>>12:(e[s++]=240|r>>>18,e[s++]=128|r>>>12&63),e[s++]=128|r>>>6&63),e[s++]=128|63&r);return e}(t)},s.utf8decode=function(t){return h.nodebuffer?o.transformTo("nodebuffer",t).toString("utf-8"):function(t){var e,r,i,n,s=t.length,a=new Array(2*s);for(e=r=0;e>10&1023,a[r++]=56320|1023&i)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(t=o.transformTo(h.uint8array?"uint8array":"array",t))},o.inherits(a,i),a.prototype.processChunk=function(t){var e=o.transformTo(h.uint8array?"uint8array":"array",t.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=e;(e=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),e.set(r,this.leftOver.length)}else e=this.leftOver.concat(e);this.leftOver=null}var i=function(t,e){var r;for((e=e||t.length)>t.length&&(e=t.length),r=e-1;0<=r&&128==(192&t[r]);)r--;return r<0?e:0===r?e:r+u[t[r]]>e?r:e}(e),n=e;i!==e.length&&(h.uint8array?(n=e.subarray(0,i),this.leftOver=e.subarray(i,e.length)):(n=e.slice(0,i),this.leftOver=e.slice(i,e.length))),this.push({data:s.utf8decode(n),meta:t.meta})},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},s.Utf8DecodeWorker=a,o.inherits(l,i),l.prototype.processChunk=function(t){this.push({data:s.utf8encode(t.data),meta:t.meta})},s.Utf8EncodeWorker=l},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(t,e,a){"use strict";var o=t("./support"),h=t("./base64"),r=t("./nodejsUtils"),i=t("set-immediate-shim"),u=t("./external");function n(t){return t}function l(t,e){for(var r=0;r>8;this.dir=!!(16&this.externalFileAttributes),0==t&&(this.dosPermissions=63&this.externalFileAttributes),3==t&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(t){if(this.extraFields[1]){var e=i(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=e.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=e.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=e.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=e.readInt(4))}},readExtraFields:function(t){var e,r,i,n=t.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});t.index+4>>6:(r<65536?e[s++]=224|r>>>12:(e[s++]=240|r>>>18,e[s++]=128|r>>>12&63),e[s++]=128|r>>>6&63),e[s++]=128|63&r);return e},r.buf2binstring=function(t){return l(t,t.length)},r.binstring2buf=function(t){for(var e=new h.Buf8(t.length),r=0,i=e.length;r>10&1023,o[i++]=56320|1023&n)}return l(o,i)},r.utf8border=function(t,e){var r;for((e=e||t.length)>t.length&&(e=t.length),r=e-1;0<=r&&128==(192&t[r]);)r--;return r<0?e:0===r?e:r+u[t[r]]>e?r:e}},{"./common":41}],43:[function(t,e,r){"use strict";e.exports=function(t,e,r,i){for(var n=65535&t|0,s=t>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3>>1:t>>>1;e[r]=t}return e}();e.exports=function(t,e,r,i){var n=o,s=i+r;t^=-1;for(var a=i;a>>8^n[255&(t^e[a])];return-1^t}},{}],46:[function(t,e,r){"use strict";var h,d=t("../utils/common"),u=t("./trees"),c=t("./adler32"),p=t("./crc32"),i=t("./messages"),l=0,f=4,m=0,_=-2,g=-1,b=4,n=2,v=8,y=9,s=286,a=30,o=19,w=2*s+1,k=15,x=3,S=258,z=S+x+1,C=42,E=113,A=1,I=2,O=3,B=4;function R(t,e){return t.msg=i[e],e}function T(t){return(t<<1)-(4t.avail_out&&(r=t.avail_out),0!==r&&(d.arraySet(t.output,e.pending_buf,e.pending_out,r,t.next_out),t.next_out+=r,e.pending_out+=r,t.total_out+=r,t.avail_out-=r,e.pending-=r,0===e.pending&&(e.pending_out=0))}function N(t,e){u._tr_flush_block(t,0<=t.block_start?t.block_start:-1,t.strstart-t.block_start,e),t.block_start=t.strstart,F(t.strm)}function U(t,e){t.pending_buf[t.pending++]=e}function P(t,e){t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pending++]=255&e}function L(t,e){var r,i,n=t.max_chain_length,s=t.strstart,a=t.prev_length,o=t.nice_match,h=t.strstart>t.w_size-z?t.strstart-(t.w_size-z):0,u=t.window,l=t.w_mask,f=t.prev,d=t.strstart+S,c=u[s+a-1],p=u[s+a];t.prev_length>=t.good_match&&(n>>=2),o>t.lookahead&&(o=t.lookahead);do{if(u[(r=e)+a]===p&&u[r+a-1]===c&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do{}while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&sh&&0!=--n);return a<=t.lookahead?a:t.lookahead}function j(t){var e,r,i,n,s,a,o,h,u,l,f=t.w_size;do{if(n=t.window_size-t.lookahead-t.strstart,t.strstart>=f+(f-z)){for(d.arraySet(t.window,t.window,f,f,0),t.match_start-=f,t.strstart-=f,t.block_start-=f,e=r=t.hash_size;i=t.head[--e],t.head[e]=f<=i?i-f:0,--r;);for(e=r=f;i=t.prev[--e],t.prev[e]=f<=i?i-f:0,--r;);n+=f}if(0===t.strm.avail_in)break;if(a=t.strm,o=t.window,h=t.strstart+t.lookahead,u=n,l=void 0,l=a.avail_in,u=x)for(s=t.strstart-t.insert,t.ins_h=t.window[s],t.ins_h=(t.ins_h<=x&&(t.ins_h=(t.ins_h<=x)if(i=u._tr_tally(t,t.strstart-t.match_start,t.match_length-x),t.lookahead-=t.match_length,t.match_length<=t.max_lazy_match&&t.lookahead>=x){for(t.match_length--;t.strstart++,t.ins_h=(t.ins_h<=x&&(t.ins_h=(t.ins_h<=x&&t.match_length<=t.prev_length){for(n=t.strstart+t.lookahead-x,i=u._tr_tally(t,t.strstart-1-t.prev_match,t.prev_length-x),t.lookahead-=t.prev_length-1,t.prev_length-=2;++t.strstart<=n&&(t.ins_h=(t.ins_h<t.pending_buf_size-5&&(r=t.pending_buf_size-5);;){if(t.lookahead<=1){if(j(t),0===t.lookahead&&e===l)return A;if(0===t.lookahead)break}t.strstart+=t.lookahead,t.lookahead=0;var i=t.block_start+r;if((0===t.strstart||t.strstart>=i)&&(t.lookahead=t.strstart-i,t.strstart=i,N(t,!1),0===t.strm.avail_out))return A;if(t.strstart-t.block_start>=t.w_size-z&&(N(t,!1),0===t.strm.avail_out))return A}return t.insert=0,e===f?(N(t,!0),0===t.strm.avail_out?O:B):(t.strstart>t.block_start&&(N(t,!1),t.strm.avail_out),A)}),new M(4,4,8,4,Z),new M(4,5,16,8,Z),new M(4,6,32,32,Z),new M(4,4,16,16,W),new M(8,16,32,32,W),new M(8,16,128,128,W),new M(8,32,128,256,W),new M(32,128,258,1024,W),new M(32,258,258,4096,W)],r.deflateInit=function(t,e){return Y(t,e,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(t,e){return t&&t.state?2!==t.state.wrap?_:(t.state.gzhead=e,m):_},r.deflate=function(t,e){var r,i,n,s;if(!t||!t.state||5>8&255),U(i,i.gzhead.time>>16&255),U(i,i.gzhead.time>>24&255),U(i,9===i.level?2:2<=i.strategy||i.level<2?4:0),U(i,255&i.gzhead.os),i.gzhead.extra&&i.gzhead.extra.length&&(U(i,255&i.gzhead.extra.length),U(i,i.gzhead.extra.length>>8&255)),i.gzhead.hcrc&&(t.adler=p(t.adler,i.pending_buf,i.pending,0)),i.gzindex=0,i.status=69):(U(i,0),U(i,0),U(i,0),U(i,0),U(i,0),U(i,9===i.level?2:2<=i.strategy||i.level<2?4:0),U(i,3),i.status=E);else{var a=v+(i.w_bits-8<<4)<<8;a|=(2<=i.strategy||i.level<2?0:i.level<6?1:6===i.level?2:3)<<6,0!==i.strstart&&(a|=32),a+=31-a%31,i.status=E,P(i,a),0!==i.strstart&&(P(i,t.adler>>>16),P(i,65535&t.adler)),t.adler=1}if(69===i.status)if(i.gzhead.extra){for(n=i.pending;i.gzindex<(65535&i.gzhead.extra.length)&&(i.pending!==i.pending_buf_size||(i.gzhead.hcrc&&i.pending>n&&(t.adler=p(t.adler,i.pending_buf,i.pending-n,n)),F(t),n=i.pending,i.pending!==i.pending_buf_size));)U(i,255&i.gzhead.extra[i.gzindex]),i.gzindex++;i.gzhead.hcrc&&i.pending>n&&(t.adler=p(t.adler,i.pending_buf,i.pending-n,n)),i.gzindex===i.gzhead.extra.length&&(i.gzindex=0,i.status=73)}else i.status=73;if(73===i.status)if(i.gzhead.name){n=i.pending;do{if(i.pending===i.pending_buf_size&&(i.gzhead.hcrc&&i.pending>n&&(t.adler=p(t.adler,i.pending_buf,i.pending-n,n)),F(t),n=i.pending,i.pending===i.pending_buf_size)){s=1;break}s=i.gzindexn&&(t.adler=p(t.adler,i.pending_buf,i.pending-n,n)),0===s&&(i.gzindex=0,i.status=91)}else i.status=91;if(91===i.status)if(i.gzhead.comment){n=i.pending;do{if(i.pending===i.pending_buf_size&&(i.gzhead.hcrc&&i.pending>n&&(t.adler=p(t.adler,i.pending_buf,i.pending-n,n)),F(t),n=i.pending,i.pending===i.pending_buf_size)){s=1;break}s=i.gzindexn&&(t.adler=p(t.adler,i.pending_buf,i.pending-n,n)),0===s&&(i.status=103)}else i.status=103;if(103===i.status&&(i.gzhead.hcrc?(i.pending+2>i.pending_buf_size&&F(t),i.pending+2<=i.pending_buf_size&&(U(i,255&t.adler),U(i,t.adler>>8&255),t.adler=0,i.status=E)):i.status=E),0!==i.pending){if(F(t),0===t.avail_out)return i.last_flush=-1,m}else if(0===t.avail_in&&T(e)<=T(r)&&e!==f)return R(t,-5);if(666===i.status&&0!==t.avail_in)return R(t,-5);if(0!==t.avail_in||0!==i.lookahead||e!==l&&666!==i.status){var o=2===i.strategy?function(t,e){for(var r;;){if(0===t.lookahead&&(j(t),0===t.lookahead)){if(e===l)return A;break}if(t.match_length=0,r=u._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++,r&&(N(t,!1),0===t.strm.avail_out))return A}return t.insert=0,e===f?(N(t,!0),0===t.strm.avail_out?O:B):t.last_lit&&(N(t,!1),0===t.strm.avail_out)?A:I}(i,e):3===i.strategy?function(t,e){for(var r,i,n,s,a=t.window;;){if(t.lookahead<=S){if(j(t),t.lookahead<=S&&e===l)return A;if(0===t.lookahead)break}if(t.match_length=0,t.lookahead>=x&&0t.lookahead&&(t.match_length=t.lookahead)}if(t.match_length>=x?(r=u._tr_tally(t,1,t.match_length-x),t.lookahead-=t.match_length,t.strstart+=t.match_length,t.match_length=0):(r=u._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++),r&&(N(t,!1),0===t.strm.avail_out))return A}return t.insert=0,e===f?(N(t,!0),0===t.strm.avail_out?O:B):t.last_lit&&(N(t,!1),0===t.strm.avail_out)?A:I}(i,e):h[i.level].func(i,e);if(o!==O&&o!==B||(i.status=666),o===A||o===O)return 0===t.avail_out&&(i.last_flush=-1),m;if(o===I&&(1===e?u._tr_align(i):5!==e&&(u._tr_stored_block(i,0,0,!1),3===e&&(D(i.head),0===i.lookahead&&(i.strstart=0,i.block_start=0,i.insert=0))),F(t),0===t.avail_out))return i.last_flush=-1,m}return e!==f?m:i.wrap<=0?1:(2===i.wrap?(U(i,255&t.adler),U(i,t.adler>>8&255),U(i,t.adler>>16&255),U(i,t.adler>>24&255),U(i,255&t.total_in),U(i,t.total_in>>8&255),U(i,t.total_in>>16&255),U(i,t.total_in>>24&255)):(P(i,t.adler>>>16),P(i,65535&t.adler)),F(t),0=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new d.Buf8(r.w_size),d.arraySet(u,e,l-r.w_size,r.w_size,0),e=u,l=r.w_size),a=t.avail_in,o=t.next_in,h=t.input,t.avail_in=l,t.next_in=0,t.input=e,j(r);r.lookahead>=x;){for(i=r.strstart,n=r.lookahead-(x-1);r.ins_h=(r.ins_h<>>=y=v>>>24,p-=y,0===(y=v>>>16&255))C[s++]=65535&v;else{if(!(16&y)){if(0==(64&y)){v=m[(65535&v)+(c&(1<>>=y,p-=y),p<15&&(c+=z[i++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(c&(1<>>=y,p-=y,(y=s-a)>3,c&=(1<<(p-=w<<3))-1,t.next_in=i,t.next_out=s,t.avail_in=i>>24&255)+(t>>>8&65280)+((65280&t)<<8)+((255&t)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(t){var e;return t&&t.state?(e=t.state,t.total_in=t.total_out=e.total=0,t.msg="",e.wrap&&(t.adler=1&e.wrap),e.mode=P,e.last=0,e.havedict=0,e.dmax=32768,e.head=null,e.hold=0,e.bits=0,e.lencode=e.lendyn=new I.Buf32(i),e.distcode=e.distdyn=new I.Buf32(n),e.sane=1,e.back=-1,N):U}function o(t){var e;return t&&t.state?((e=t.state).wsize=0,e.whave=0,e.wnext=0,a(t)):U}function h(t,e){var r,i;return t&&t.state?(i=t.state,e<0?(r=0,e=-e):(r=1+(e>>4),e<48&&(e&=15)),e&&(e<8||15=s.wsize?(I.arraySet(s.window,e,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(i<(n=s.wsize-s.wnext)&&(n=i),I.arraySet(s.window,e,r-i,n,s.wnext),(i-=n)?(I.arraySet(s.window,e,r-i,i,0),s.wnext=i,s.whave=s.wsize):(s.wnext+=n,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){t.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){t.msg="unknown compression method",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){t.msg="invalid window size",r.mode=30;break}r.dmax=1<>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break t;o--,u+=i[s++]<>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break t;o--,u+=i[s++]<>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break t;o--,u+=i[s++]<>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(c=r.length)&&(c=o),c&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,i,s,c,k)),512&r.flags&&(r.check=B(r.check,i,c,s)),o-=c,s+=c,r.length-=c),r.length))break t;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break t;for(c=0;k=i[s+c++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&c>9&1,r.head.done=!0),t.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break t;o--,u+=i[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break t;o--,u+=i[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==e)break;u>>>=2,l-=2;break t;case 2:r.mode=17;break;case 3:t.msg="invalid block type",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break t;o--,u+=i[s++]<>>16^65535)){t.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===e)break t;case 15:r.mode=16;case 16:if(c=r.length){if(o>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){t.msg="invalid code lengths set",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break t;o--,u+=i[s++]<>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){t.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],c=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l>>=_)),u>>>=3,l-=3}else{for(z=_+7;l>>=_)),u>>>=7,l-=7}if(r.have+c>r.nlen+r.ndist){t.msg="invalid bit length repeat",r.mode=30;break}for(;c--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){t.msg="invalid code -- missing end-of-block",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){t.msg="invalid literal/lengths set",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){t.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===e)break t;case 20:r.mode=21;case 21:if(6<=o&&258<=h){t.next_out=a,t.avail_out=h,t.next_in=s,t.avail_in=o,r.hold=u,r.bits=l,R(t,d),a=t.next_out,n=t.output,h=t.avail_out,s=t.next_in,i=t.input,o=t.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break t;o--,u+=i[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break t;o--,u+=i[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){t.msg="invalid literal/length code",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break t;o--,u+=i[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break t;o--,u+=i[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){t.msg="invalid distance code",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){t.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break t;if(c=d-h,r.offset>c){if((c=r.offset-c)>r.whave&&r.sane){t.msg="invalid distance too far back",r.mode=30;break}p=c>r.wnext?(c-=r.wnext,r.wsize-c):r.wnext-c,c>r.length&&(c=r.length),m=r.window}else m=n,p=a-r.offset,c=r.length;for(hc?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=e[r+a[v]]}if(k>>7)]}function U(t,e){t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending++]=e>>>8&255}function P(t,e,r){t.bi_valid>c-r?(t.bi_buf|=e<>c-t.bi_valid,t.bi_valid+=r-c):(t.bi_buf|=e<>>=1,r<<=1,0<--e;);return r>>>1}function Z(t,e,r){var i,n,s=new Array(g+1),a=0;for(i=1;i<=g;i++)s[i]=a=a+r[i-1]<<1;for(n=0;n<=e;n++){var o=t[2*n+1];0!==o&&(t[2*n]=j(s[o]++,o))}}function W(t){var e;for(e=0;e>1;1<=r;r--)G(t,s,r);for(n=h;r=t.heap[1],t.heap[1]=t.heap[t.heap_len--],G(t,s,1),i=t.heap[1],t.heap[--t.heap_max]=r,t.heap[--t.heap_max]=i,s[2*n]=s[2*r]+s[2*i],t.depth[n]=(t.depth[r]>=t.depth[i]?t.depth[r]:t.depth[i])+1,s[2*r+1]=s[2*i+1]=n,t.heap[1]=n++,G(t,s,1),2<=t.heap_len;);t.heap[--t.heap_max]=t.heap[1],function(t,e){var r,i,n,s,a,o,h=e.dyn_tree,u=e.max_code,l=e.stat_desc.static_tree,f=e.stat_desc.has_stree,d=e.stat_desc.extra_bits,c=e.stat_desc.extra_base,p=e.stat_desc.max_length,m=0;for(s=0;s<=g;s++)t.bl_count[s]=0;for(h[2*t.heap[t.heap_max]+1]=0,r=t.heap_max+1;r<_;r++)p<(s=h[2*h[2*(i=t.heap[r])+1]+1]+1)&&(s=p,m++),h[2*i+1]=s,u>=7;i>>=1)if(1&r&&0!==t.dyn_ltree[2*e])return o;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return h;for(e=32;e>>3,(s=t.static_len+3+7>>>3)<=n&&(n=s)):n=s=r+5,r+4<=n&&-1!==e?J(t,e,r,i):4===t.strategy||s===n?(P(t,2+(i?1:0),3),K(t,z,C)):(P(t,4+(i?1:0),3),function(t,e,r,i){var n;for(P(t,e-257,5),P(t,r-1,5),P(t,i-4,4),n=0;n>>8&255,t.pending_buf[t.d_buf+2*t.last_lit+1]=255&e,t.pending_buf[t.l_buf+t.last_lit]=255&r,t.last_lit++,0===e?t.dyn_ltree[2*r]++:(t.matches++,e--,t.dyn_ltree[2*(A[r]+u+1)]++,t.dyn_dtree[2*N(e)]++),t.last_lit===t.lit_bufsize-1},r._tr_align=function(t){P(t,2,3),L(t,m,z),function(t){16===t.bi_valid?(U(t,t.bi_buf),t.bi_buf=0,t.bi_valid=0):8<=t.bi_valid&&(t.pending_buf[t.pending++]=255&t.bi_buf,t.bi_buf>>=8,t.bi_valid-=8)}(t)}},{"../utils/common":41}],53:[function(t,e,r){"use strict";e.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(t,e,r){"use strict";e.exports="function"==typeof setImmediate?setImmediate:function(){var t=[].slice.apply(arguments);t.splice(1,0,0),setTimeout.apply(null,t)}},{}]},{},[10])(10)}); -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | /* If you are looking here then I assume you have some interest in understanding how to 2 | * manipulate the WAD3 format. If you are creating a free application for the benefit 3 | * of the community then feel free to sift through and copy any code that you like. 4 | * If you have any questions, contact me at 5 | * joe((at))fortunatelyjoe.com <---- See how I fooled the bots there! 6 | * 7 | * Good luck in your programming endeavors! 8 | * 9 | * -Joe */ 10 | 11 | 12 | 13 | "use strict"; 14 | 15 | var textures = []; // A global array containing all the texture objects 16 | var file_name = "" // store the name of the currently open WAD file 17 | 18 | 19 | /******************************************************** 20 | * upload 21 | * 22 | * Handles the file upload button 23 | ********************************************************/ 24 | function upload() { 25 | // Clear any previous loaded textures 26 | textures = []; 27 | 28 | // Clear any previous loaded textures from document texture list 29 | var select = document.getElementById("texture_list"); 30 | for(var i = select.options.length - 1 ; i >= 0 ; i--) 31 | select.remove(i); 32 | 33 | // Get the file 34 | var file = document.getElementById("upload").files[0]; 35 | file_name = file.name 36 | // Create a FileReader to read file 37 | var reader = new FileReader(); 38 | 39 | // Set up callback function so when the FilReader is finished, it passes the ArrayBuffer 40 | // to the parseWad function 41 | reader.onload = function() { 42 | parseWad(reader.result); 43 | } 44 | 45 | // Start converting file to ArrayBuffer 46 | reader.readAsArrayBuffer(file); 47 | 48 | } 49 | 50 | 51 | 52 | /******************************************************** 53 | * onSelectTexture 54 | * 55 | * Event handler for when a texture is selected from the 56 | * texture list. 57 | ********************************************************/ 58 | function onSelectTexture() { 59 | var index = document.getElementById("texture_list").selectedIndex; 60 | displayTexture(textures[index]); 61 | 62 | // Update rename box with current texture name 63 | document.getElementById("rename").value = textures[index].name; 64 | } 65 | 66 | 67 | 68 | /******************************************************** 69 | * parseWad 70 | * 71 | * Takes an ArrayBuffer of a WAD file and parses it. 72 | ********************************************************/ 73 | function parseWad(buffer) { 74 | 75 | // Create DataView to view the buffer by types 76 | var dv = new DataView(buffer); 77 | 78 | // Is this a valid WAD3 file? 79 | if (!isValidWad(dv)) { 80 | alert("File is not a valid WAD3 file."); 81 | return; 82 | } 83 | 84 | // Get the WAD header 85 | var header = getWadHeader(dv); 86 | 87 | // Get the WAD Directory Entries (List of texture file details) 88 | var entries = getWadEntries(dv, header.dirOffset, header.nEntries); // Global 89 | 90 | // Create the texture objects 91 | for (var i = 0; i < entries.length; i++) { 92 | textures[i] = retrieveTexture(dv, entries[i]); 93 | } 94 | 95 | // Populate texture list 96 | for (var i = 0; i < entries.length; i++) { 97 | var option = document.createElement("OPTION"); 98 | option.text = entries[i].name; 99 | option.value = entries[i].name; 100 | document.getElementById("texture_list").options.add(option); 101 | } 102 | 103 | // Display first texture as default 104 | var texture = retrieveTexture(dv, entries[0]); 105 | document.getElementById("texture_list").selectedIndex = 0 106 | displayTexture(texture); 107 | } 108 | 109 | 110 | 111 | /******************************************************** 112 | * getWadHeader 113 | * 114 | * Takes a DataView object of the WAD file and parses the 115 | * WAD header into an object. 116 | * 117 | *Header Specification (12 bytes) 118 | * 4 Magic Number "WAD3" 119 | * 4 nDir (The number of directory entries) 120 | * 4 nDirOffset Offset into file where entries start 121 | ********************************************************/ 122 | function getWadHeader(dv) { 123 | var header = {}; 124 | 125 | // Get the number of entries 126 | header.nEntries = dv.getUint32(4, true); 127 | 128 | // Get directory offset (directory contains all the file entries) 129 | header.dirOffset = dv.getUint32(8, true); 130 | 131 | return header; 132 | } 133 | 134 | 135 | 136 | /******************************************************** 137 | * isWadValid 138 | * 139 | * Takes a DataView object of the WAD file and checks for 140 | * the magic string "WAD3". Returns true if valid, false 141 | * if not. 142 | ********************************************************/ 143 | function isValidWad(dv) { 144 | // Read magic string and make sure this is a valid WAD3 File 145 | if (dv.getUint8(0) != 0x57 || dv.getUint8(1) != 0x41 || dv.getUint8(2) != 0x44 || dv.getUint8(3) != 0x33) // "WAD3" 146 | return false; 147 | 148 | return true; 149 | } 150 | 151 | 152 | 153 | /******************************************************** 154 | * getWadEntries 155 | * 156 | * Takes a DataView object of the WAD file and parses the 157 | * collection of WAD entries into an array of objects. 158 | * 159 | * Directory Entry Specification (32 bytes) 160 | * 4 nFilePos Absolute offset to file's location 161 | * 4 nDiskSize Size of the file 162 | * 4 nSize Uncompressed size 163 | * 1 nType Type of entry 164 | * 1 bCompression 0 if not compressed 165 | * 2 nDUmmy Unused 166 | * 16 szName Name of file (null terminated) 167 | ********************************************************/ 168 | function getWadEntries(dv, dirOffset, nEntries) { 169 | var entrySize = 32; 170 | var entries = []; 171 | 172 | for (var i = 0; i < nEntries; i++) { 173 | 174 | // Object to hold entry 175 | var currEntry = {}; 176 | 177 | // Offset to start of current entry 178 | var entryPos = dirOffset + i * entrySize; 179 | 180 | // Offset property 181 | currEntry.offset = dv.getUint32(entryPos, true); 182 | 183 | // Size property 184 | currEntry.size = dv.getUint32(entryPos + 4, true); 185 | 186 | // Uncompressed Size property 187 | currEntry.uSize = dv.getUint32(entryPos + 8, true); 188 | 189 | // Type property 190 | currEntry.type = dv.getUint8(entryPos + 12); 191 | 192 | // Compressed State property 193 | currEntry.isCompressed = dv.getUint8(entryPos + 13); 194 | 195 | // Name String property 196 | currEntry.name = dataViewToString(dv, entryPos + 16, 16); 197 | 198 | // Add entry to entries array 199 | entries.push(currEntry); 200 | 201 | } 202 | 203 | return entries; 204 | } 205 | 206 | 207 | 208 | /******************************************************** 209 | * dataViewToString 210 | * 211 | * Takes a DataView object of the WAD file, a starting 212 | * offset, and a maximum length and converts that portion 213 | * of the dataView to a string. 214 | ********************************************************/ 215 | function dataViewToString(dv, start, len) { 216 | var str = ""; 217 | for (var i = 0; i < len; i++) { 218 | // Get the ASCII code 219 | var charCode = dv.getUint8(start + i); 220 | 221 | // End loop if NULL-terminator 222 | if (charCode == 0) break; 223 | 224 | // Add character to name string 225 | str += String.fromCharCode(charCode); 226 | } 227 | return str; 228 | } 229 | 230 | 231 | 232 | /******************************************************** 233 | * retrieveTexture 234 | * 235 | * Takes a DataView object of the WAD file, and the 236 | * the texture's directory entry object and creates 237 | * a texture object. 238 | * 239 | * Texture File Specification (file header is 40 bytes) 240 | * 16 szName Name of texture file (null terminated) 241 | * 4 nWidth Width of texture in pixels 242 | * 4 nHeight Height of texture in pixels 243 | * 4 offset0 relative Offset to level 0 MIP texture 244 | * 4 offset1 relative Offset to level 1 MIP texture 245 | * 4 offset2 relative Offset to level 2 MIP texture 246 | * 4 offset3 relative offset to level 3 MIP texture 247 | *---OFFSETS ARE RELATIVE TO BEGINNING OF FILE HEADER)--- 248 | * VAR tex0 MIP texture level 0 249 | * VAR tex1 MIP texture level 1 250 | * VAR tex2 MIP texture level 2 251 | * VAR tex3 MIP texture level 3 252 | * 2 nColors Number of colors in palette (Max 256) 253 | * 768 palette Color table, 256 triplets of (R, G, B) 254 | * 2 padding 255 | ********************************************************/ 256 | function retrieveTexture(dv, dirEntry) { 257 | var texture = {}; 258 | var offset = dirEntry.offset; // Offset of actual texture within file 259 | 260 | // Name 261 | texture.name = dataViewToString(dv, offset, 16); 262 | 263 | // Width/Height 264 | texture.width = dv.getUint32(offset + 16, true); 265 | texture.height = dv.getUint32(offset + 20, true); 266 | 267 | // MIP Texture Offsets by level 268 | var mipOffset = []; 269 | mipOffset[0] = dv.getUint32(offset + 24, true); 270 | mipOffset[1] = dv.getUint32(offset + 28, true); 271 | mipOffset[2] = dv.getUint32(offset + 32, true); 272 | mipOffset[3] = dv.getUint32(offset + 36, true); 273 | 274 | // Read in MIP Textures by level 275 | texture.mipLevel = []; 276 | for (var level = 0; level < 4; level++) { 277 | texture.mipLevel[level] = []; 278 | 279 | // Read the pixels (Note, these are not RGB values, they are references to the palette) 280 | // The texture dimensions are divided by a power of 4 for each additional MIP level /4 /16 /64 281 | var nPixels = (texture.width * texture.height) / Math.pow(4, level); 282 | for (var i = 0; i < nPixels; i++) { 283 | texture.mipLevel[level][i] = dv.getUint8(offset + mipOffset[level] + i, true); 284 | } 285 | } 286 | 287 | // Read in palette 288 | texture.palette = []; 289 | 290 | // Palette is at the end of the file. We find the palette by starting at the file offset, fast-forward 291 | // to end of file, then back off by the size of the palette 768 (256 * 3) 292 | var paletteOffset = offset + dirEntry.size - 768 - 2; 293 | for (var i = 0; i < 768; i += 3) { 294 | var r = dv.getUint8(paletteOffset + i, true); 295 | var g = dv.getUint8(paletteOffset + i + 1, true); 296 | var b = dv.getUint8(paletteOffset + i + 2, true); 297 | 298 | // Add the RGB object to the palette array 299 | texture.palette.push({r:r, g:g, b:b}); 300 | } 301 | return texture; 302 | } 303 | 304 | 305 | 306 | /******************************************************** 307 | * displayTexture 308 | * 309 | * Takes a texture object and displays it on the 310 | * "viewport" canvas 311 | ********************************************************/ 312 | function displayTexture(texture) { 313 | 314 | // Get canvas context 315 | var canvas = document.getElementById("viewport"); 316 | var ctx = canvas.getContext("2d"); 317 | 318 | // Resize canvas to image 319 | canvas.height = texture.height; 320 | canvas.width = texture.width; 321 | 322 | // Get access to the canvas pixel buffer 323 | var imgData = ctx.createImageData(texture.width, texture.height); 324 | 325 | // Clear screen 326 | ctx.clearRect(0, 0, canvas.width, canvas.height); 327 | 328 | // Draw pixels to pixel buffer 329 | var nPixels = texture.width * texture.height; 330 | var imgDataIndex = 0; 331 | for (var i = 0; i < nPixels; i++) { 332 | var palIndex = texture.mipLevel[0][i]; 333 | imgData.data[imgDataIndex + 0] = texture.palette[palIndex].r; // Red 334 | imgData.data[imgDataIndex + 1] = texture.palette[palIndex].g; // Green 335 | imgData.data[imgDataIndex + 2] = texture.palette[palIndex].b; // Blue 336 | imgData.data[imgDataIndex + 3] = 255; // Alpha 337 | imgDataIndex += 4; 338 | } 339 | 340 | // Send pixel buffer back to canvas 341 | ctx.putImageData(imgData, 0, 0); 342 | } 343 | 344 | 345 | 346 | /******************************************************** 347 | * buildWad() 348 | * 349 | * Builds a binary WAD file using textures stored in the 350 | * global "textures" array variable and then initiates 351 | * the download. 352 | ********************************************************/ 353 | function buildWad() { 354 | // Keep track of file offsets for later when we build directory entries 355 | var fileOffsets = []; 356 | 357 | // Keep track of the individual file lengths for later when we build directory entries 358 | var fileSizes = []; 359 | 360 | // Header size 361 | var headerSize = 12; 362 | 363 | // Calculate the size of the directory entries section 364 | var entriesSize = textures.length * 32; 365 | 366 | // Calculate size of the file/data section 367 | var fileSectionSize = 0; 368 | for (var i = 0; i < textures.length; i++) { 369 | var nPixels = textures[i].width * textures[i].height; 370 | fileSectionSize += 40 + nPixels + nPixels/4 + nPixels/16 + nPixels/64 + 2 + 768 + 2; 371 | } 372 | 373 | // Create a buffer to hold our file 374 | var buffer = new ArrayBuffer(headerSize + entriesSize + fileSectionSize); 375 | 376 | // Create a dataview so we can populate the buffer with specific data types 377 | var dv = new DataView(buffer); 378 | 379 | // File position 380 | var pos = 0; 381 | 382 | // Build header 383 | pos = putByte(dv, pos, 0x57); // W 384 | pos = putByte(dv, pos, 0x41); // A 385 | pos = putByte(dv, pos, 0x44); // D 386 | pos = putByte(dv, pos, 0x33); // 3 387 | pos = put32(dv, pos, textures.length); // nDirs 388 | pos = put32(dv, pos, headerSize + fileSectionSize); // nDirOffset (entries start after file/data section) 389 | 390 | 391 | // Build File/Data section 392 | for (var i = 0; i < textures.length; i++) { 393 | fileOffsets.push(pos); // Note the current file position (used in directory entry later) 394 | 395 | pos = putStr16(dv, pos, textures[i].name); // Name string 396 | pos = put32(dv, pos, textures[i].width); // Width 397 | pos = put32(dv, pos, textures[i].height); // Height 398 | 399 | // Calculate MIP texture offsets 400 | var nPixels = textures[i].height * textures[i].width; 401 | var mipOffset0 = 40; 402 | var mipOffset1 = mipOffset0 + nPixels; 403 | var mipOffset2 = mipOffset1 + nPixels/4; 404 | var mipOffset3 = mipOffset2 + nPixels/16; 405 | 406 | // Write the MIP offsets 407 | pos = put32(dv, pos, mipOffset0); // MIP Level 0 offset 408 | pos = put32(dv, pos, mipOffset1); // MIP Level 1 offset 409 | pos = put32(dv, pos, mipOffset2); // MIP Level 2 offset 410 | pos = put32(dv, pos, mipOffset3); // MIP Level 3 offset 411 | 412 | // Write the MIP texture data by level 413 | for (var level = 0; level < 4; level++) { 414 | 415 | // Write all pixels within that layer 416 | var currLevel = textures[i].mipLevel[level]; 417 | var currLength = currLevel.length; 418 | for (var pixel = 0; pixel < currLength; pixel++) { 419 | 420 | // Write pixel 421 | pos = putByte(dv, pos, currLevel[pixel]); 422 | } 423 | } 424 | 425 | // Write the palette 426 | pos = put16(dv, pos, 256); // Number of colors used 427 | var palette = textures[i].palette; 428 | for (var palIndex = 0; palIndex < 256; palIndex++) { 429 | 430 | // Write palette entry 431 | pos = putByte(dv, pos, palette[palIndex].r); // Red 432 | pos = putByte(dv, pos, palette[palIndex].g); // Green 433 | pos = putByte(dv, pos, palette[palIndex].b); // Blue 434 | } 435 | 436 | // 2 bytes of padding following palette 437 | pos = put16(dv, pos, 0); 438 | 439 | // Record the file size (current position - starting position) 440 | fileSizes[i] = pos - fileOffsets[i]; 441 | } 442 | 443 | // Now build the directory entries 444 | for (var i = 0; i < textures.length; i++) { 445 | pos = put32(dv, pos, fileOffsets[i]); // offset of file in WAD 446 | pos = put32(dv, pos, fileSizes[i]); // file size 447 | pos = put32(dv, pos, fileSizes[i]); // uncompressed size (same, we don't support compression) 448 | pos = putByte(dv, pos, 67); // type (67 is what Wally uses, so it must be a good choice) 449 | pos = putByte(dv, pos, 0); // compression (0 because we don't support it) 450 | pos = put16(dv, pos, 0); // 2 dummy bytes 451 | pos = putStr16(dv, pos, textures[i].name); // texture name (16 bytes, null terminated) 452 | } 453 | 454 | saveData(buffer, "download.wad"); 455 | } 456 | 457 | 458 | 459 | /******************************************************** 460 | * binary put functions 461 | * 462 | * Takes a dataview object, a position (in bytes), and 463 | * a variable to write. 464 | ********************************************************/ 465 | function putByte(dv, pos, data) { 466 | dv.setUint8(pos, data); 467 | return pos + 1; 468 | } 469 | 470 | function put16(dv, pos, data) { 471 | dv.setUint16(pos, data, true); 472 | return pos += 2; 473 | } 474 | 475 | function put32(dv, pos, data) { 476 | dv.setUint32(pos, data, true); 477 | return pos += 4; 478 | } 479 | 480 | function putStr16(dv, pos, str) { 481 | if (str.length > 15) { 482 | console.error("putStr16: Attempted to use string greater than length 15"); 483 | return null; 484 | } 485 | 486 | var charLoop = str.length; // How many characters to add 487 | var nullLoop = 16 - str.length; // How many null terminators to add 488 | 489 | // Loop to add the string characters 490 | for (var i = 0; i < charLoop; i++) { 491 | var charCode = str.charCodeAt(i); 492 | dv.setUint8(pos + i, charCode); 493 | } 494 | 495 | // Loop to fill the any remaining bytes within the 16 length with null terminators 496 | for (var i = 0; i < nullLoop; i++) { 497 | dv.setUint8(pos + charLoop + i, 0); 498 | } 499 | 500 | return pos += 16; 501 | } 502 | 503 | 504 | 505 | /******************************************************** 506 | * saveData 507 | * 508 | * Takes a data buffer (assumed to be a binary file) and 509 | * initiates the download. 510 | ********************************************************/ 511 | function saveData(data, fileName) { 512 | var a = document.createElement("a"); 513 | document.body.appendChild(a); 514 | a.style = "display: none"; 515 | 516 | var blob = new Blob([data], {type: "octet/stream"}); 517 | var url = window.URL.createObjectURL(blob); 518 | a.href = url; 519 | a.download = fileName; 520 | a.click(); 521 | window.URL.revokeObjectURL(url); 522 | } 523 | 524 | /******************************************************** 525 | * exportZip() 526 | * 527 | * Build a zip from all textures and save it 528 | ********************************************************/ 529 | function exportZip() { 530 | var zip = new JSZip(); 531 | // create a folder with the name of the file to prevent a mess on "extract here" 532 | var img = zip.folder(file_name.toLowerCase().replace(".wad", "")); 533 | var canvas = document.getElementById("viewport"); 534 | for (var i = 0; i < textures.length; i++) { 535 | // display each texture to get it's data URL 536 | displayTexture(textures[i]) 537 | // convert the dataURL to base64 and save it to a file 538 | img.file(`${textures[i].name}.png`, canvas.toDataURL().replace(/^data:image\/(png|jpg);base64,/, ""), {base64: true}); 539 | } 540 | zip.generateAsync({type:"blob"}) 541 | .then(function(content) { 542 | // see FileSaver.js 543 | saveAs(content, `${file_name}.zip`); 544 | }); 545 | // re-display the currently selected texture 546 | displayTexture(textures[document.getElementById("texture_list").selectedIndex]) 547 | } 548 | 549 | /******************************************************** 550 | * add_img_handler() 551 | * 552 | * Handles the actions for adding an image to the WAD 553 | * collection. 554 | ********************************************************/ 555 | function add_img_handler(){ 556 | // Get the image file 557 | var imgFile = document.getElementById("add_img").files[0]; 558 | 559 | // Create a FileReader and onload handler 560 | var reader = new FileReader(); 561 | reader.onload = function(event){ 562 | 563 | // Create image and onload handler 564 | var img = new Image(); 565 | img.onload = function(){ 566 | 567 | // Status Window 568 | var status = document.getElementById("status"); 569 | status.value = "Status: Starting"; 570 | 571 | // Make sure img dimensions are multiples of 16 572 | if (img.width % 16 || img.height % 16) { 573 | status.value = "Status: Error - Image dimensions must be multiples of 16." 574 | return; 575 | } 576 | 577 | // Disable interface 578 | setDisableInterface(true); 579 | 580 | // Set-up canvas 581 | var canvas = document.getElementById("viewport"); 582 | var ctx = canvas.getContext("2d"); 583 | canvas.width = img.width; 584 | canvas.height = img.height; 585 | 586 | // Draw image to canvas 587 | ctx.drawImage(img,0,0); 588 | 589 | // Get the pixel buffer from the canvas 590 | var buffer = ctx.getImageData(0, 0, canvas.width, canvas.height).data; 591 | 592 | // Create web worker to handle the intense image processing routines 593 | var textureWorker = new Worker('create_texture.js'); 594 | 595 | // Worker output variable 596 | var texture; 597 | 598 | // Message handler for textureWorker 599 | textureWorker.onmessage = function(event) { 600 | 601 | // Is incoming message a status update? 602 | if (event.data.type == "status") { 603 | status.value = "Status: " + event.data.msg; 604 | } 605 | 606 | // Otherwise its the processed texture 607 | else { 608 | texture = event.data.texture; 609 | 610 | // Display the image from palette form 611 | displayImg(texture.mipLevel[0], texture.palette); 612 | 613 | // Add the texture (handles document stuff) 614 | addTexture(texture); 615 | 616 | // Reenable interface 617 | setDisableInterface(false); 618 | 619 | console.log(texture.palette); 620 | } 621 | } 622 | 623 | // Start textureWorker thread 624 | textureWorker.postMessage({height:img.height, width:img.width, name:imgFile.name, buffer:buffer}); 625 | } 626 | img.src = event.target.result; 627 | } 628 | // Invoke FileReader with the image file 629 | reader.readAsDataURL(imgFile); 630 | } 631 | 632 | 633 | 634 | /******************************************************** 635 | * displayImg() 636 | * 637 | * Takes an array of pixels (which are not RGB values, but 638 | * references into the palette), and the palette itself 639 | * and displays the image in the viewport canvas. 640 | ********************************************************/ 641 | function displayImg(refs, palette) { 642 | // Get image data to swap out 643 | var canvas = document.getElementById("viewport"); 644 | var ctx = canvas.getContext("2d"); 645 | var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); 646 | var buffer = imgData.data; 647 | 648 | // For all the palette references in the buffer 649 | var pixelIndex = 0; 650 | for (var i = 0; i < refs.length; i++) { 651 | // Grab color from palette 652 | var color = palette[refs[i]]; 653 | 654 | // Build the pixel in the pixel buffer 655 | buffer[pixelIndex++] = color.r; 656 | buffer[pixelIndex++] = color.g; 657 | buffer[pixelIndex++] = color.b; 658 | buffer[pixelIndex++] = 255; 659 | } 660 | 661 | // Reinsert image data to canvas 662 | ctx.putImageData(imgData, 0, 0); 663 | } 664 | 665 | 666 | 667 | /******************************************************** 668 | * addTexture() 669 | * 670 | * Takes a texture object handles integrating it into the 671 | * WAD's collection. Also handles updating document with 672 | * new texture's information. 673 | ********************************************************/ 674 | function addTexture(texture) { 675 | // TODO ----------------- Generate mip textures 676 | 677 | // Add texture object 678 | textures.push(texture); 679 | 680 | // Add to list 681 | var list = document.getElementById("texture_list"); 682 | var option = document.createElement("option"); 683 | option.text = texture.name; 684 | list.add(option); 685 | 686 | } 687 | 688 | 689 | 690 | /******************************************************** 691 | * removeTexture() 692 | * 693 | * removes the texture that is selected in the texture 694 | * list form, both from the form and from the internal 695 | * texture array. 696 | ********************************************************/ 697 | function removeTexture() { 698 | // Make sure there are actually textures available to remove 699 | if (textures.length == 0) 700 | return; 701 | 702 | // Get list from document 703 | var list = document.getElementById("texture_list"); 704 | 705 | // Get selected index from list 706 | var index = list.selectedIndex; 707 | 708 | // Make sure something is selected in list 709 | if (index == -1) 710 | return; 711 | 712 | // Remove from texture array without leaving gap 713 | textures.splice(index, 1); 714 | 715 | // Remove from list in document 716 | list.remove(index); 717 | 718 | // Clear the canvas of current image 719 | var canvas = document.getElementById("viewport"); 720 | var ctx = canvas.getContext("2d"); 721 | ctx.clearRect(0, 0, canvas.width, canvas.height); 722 | 723 | } 724 | 725 | 726 | 727 | /******************************************************** 728 | * setDisableInterface() 729 | * 730 | * Enables/Disables the form elements depending on the 731 | * boolean argument. 732 | ********************************************************/ 733 | function setDisableInterface(boolean) { 734 | document.getElementById("upload").disabled = boolean; 735 | document.getElementById("add_img").disabled = boolean; 736 | document.getElementById("download").disabled = boolean; 737 | document.getElementById("remove").disabled = boolean; 738 | document.getElementById("texture_list").disabled = boolean; 739 | document.getElementById("rename").disabled = boolean; 740 | } 741 | 742 | 743 | 744 | /******************************************************** 745 | * rename() 746 | * 747 | * Renames a texture to whatever is in the rename form 748 | * element and updates the texture name in both the form 749 | * list and its internal object in the texture array. 750 | ********************************************************/ 751 | function rename() { 752 | var newName = document.getElementById("rename").value; 753 | var status = document.getElementById("status"); 754 | if (newName.length > 12) { 755 | status.value = "Status: Error - Name must be no longer than 12 characters"; 756 | return; 757 | } 758 | 759 | // Make sure there are actually textures in the list 760 | if (textures.length == 0) 761 | return; 762 | 763 | // Get list from document 764 | var list = document.getElementById("texture_list"); 765 | 766 | // Get selected index from list 767 | var index = list.selectedIndex; 768 | 769 | // Make sure something is selected in list 770 | if (index == -1) 771 | return; 772 | 773 | textures[index].name = newName; 774 | 775 | list.options[index].text = newName; 776 | 777 | status.value = "Status: Texture renamed."; 778 | 779 | } 780 | 781 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | text-align:center; 3 | background-color: #4d4d4d; 4 | font-family: Consolas,monaco,monospace; 5 | color: white; 6 | } 7 | 8 | button, input { 9 | font-family: Consolas,monaco,monospace; 10 | margin: 4px; 11 | background-color: #333333; 12 | border: none; 13 | color: #F5F5F5; 14 | padding: 15px 32px; 15 | text-align: center; 16 | text-decoration: none; 17 | display: inline-block; 18 | font-size: 16px; 19 | } 20 | 21 | input { 22 | width: 400px; 23 | } 24 | 25 | button { 26 | width: 150px; 27 | border: 1px solid white; 28 | } 29 | 30 | select { 31 | background-color: white; 32 | width: 200px; 33 | } 34 | 35 | canvas { 36 | background-color: black; 37 | margin: 10px; 38 | } 39 | 40 | table { 41 | width: 80%; 42 | margin: auto; 43 | } 44 | 45 | td { 46 | width: 30%; 47 | } 48 | 49 | #status { 50 | background-color: #333333; 51 | width: 100%; 52 | } 53 | 54 | #rename { 55 | background-color: white; 56 | color: #333333; 57 | } 58 | 59 | .center { 60 | text-align: center; 61 | } 62 | 63 | --------------------------------------------------------------------------------