├── LICENSE ├── README.md ├── icon.png ├── index.js ├── jszip.min.js ├── manifest.json ├── options.html ├── options.js └── webglripper.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Rilshrink 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebGLRipper 2 | A tool used to rip models & textures from WebGL applets 3 | 4 | # Issues? 5 | If you encounter any issues after installing this extension, I kindly ask you to follow these steps: 6 | 7 | 1. First, try disabling my extension and see if the issue persists. 8 | 2. Check for any conflicting extensions or browser settings that might be causing the problem. 9 | 3. If the issue persists only when my extension is enabled, please report it here. 10 | 11 | When submitting a bug report, please include as much detail as possible, such as your browser version, operating system, website you were testing, and any error messages encountered. 12 | 13 | Thank you in advanced! 14 | 15 | > If you are using shadercalc, please note that it is currently a Work-In-Progress and doesn't currently work. 16 | 17 | # How to use? 18 | 19 | > You will **NOT** find this extension on the Chrome Webstore or any other extension platform! If you do find it there, it wasn't published by me and may contain malicious code. 20 | 21 | ## Chrome 22 | 23 | Click on the green <> Code Button -> Download the repo as a zip -> Unzip it somewhere safe -> Goto ```chrome://extensions/``` -> Enable developer mode -> Load unpacked -> Load unzipped directory 24 | 25 | You can then access options by clicking on the icon, and clicking on the 'options' button, which will bring you to the page. 26 | 27 | ## Firefox 28 | 29 | Click on the green <> Code Button -> Download the repo as a zip -> Unzip it somewhere safe -> Goto ```about:addons``` -> Debug Add-ons -> Load Temporary Add-on -> Load unzipped directory 30 | 31 | You can then access options by clicking on the extensions icon in the top right, clicking the cog wheel and go 'Manage Extension' then click on the 'Options' tab. 32 | 33 | # How to rip? 34 | 35 | Go to a compatible WebGL website, then press the Insert key on your keyboard and it should then begin downloading. 36 | 37 | > The downloaded textures may not be the correct size and you'll have to manually resize them, this is a current known limitation. 38 | 39 | > The website may request permission to download multiple files; be sure to allow this, or alternatively, navigate to the extension options and enable Download as ZIP. -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rilshrink/WebGLRipper/9ef0b3aedfb5bf99590082d2d05fee0151f3fe38/icon.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | ; (function () { // https://stackoverflow.com/questions/70474845/inject-javascript-from-content-script-with-a-chrome-extension-v3 2 | 3 | // Extremely hacky way to make this work, since you can't access chrome.storage 4 | (chrome || browser).storage.sync.get({ 5 | default_texture_res: '4096x4096', 6 | do_shader_calc: false, 7 | is_debug_mode: true, 8 | unflip_textures: true, 9 | should_download_zip: false, 10 | minimum_clears: 1 11 | }, function(items) { 12 | 13 | let hiddenSettings = document.createElement("div"); 14 | hiddenSettings.id = "webgl_ripper_settings"; 15 | hiddenSettings.textContent = JSON.stringify(items); 16 | hiddenSettings.hidden = true; 17 | (document.head || document.documentElement).appendChild(hiddenSettings); 18 | 19 | if(items.should_download_zip) { 20 | let s = document.createElement('script'); 21 | s.src = (chrome || browser).runtime.getURL('jszip.min.js'); 22 | s.onload = function () { this.remove(); }; 23 | (document.head || document.documentElement).appendChild(s); 24 | } 25 | 26 | }); // Add settings before WebGL Ripper Script 27 | 28 | let s = document.createElement('script'); 29 | s.src = (chrome || browser).runtime.getURL('webglripper.js'); 30 | s.onload = function () { this.remove(); }; 31 | (document.head || document.documentElement).appendChild(s); 32 | })(); -------------------------------------------------------------------------------- /jszip.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZip v3.10.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/main/LICENSE.markdown. 8 | 9 | JSZip uses the library pako released under the MIT license : 10 | https://github.com/nodeca/pako/blob/main/LICENSE 11 | */ 12 | 13 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JSZip=e()}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof require&&require;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h)}return o[r].exports}for(var l="function"==typeof require&&require,e=0;e>2,s=(3&t)<<4|r>>4,a=1>6:64,o=2>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l}},{"./support":30,"./utils":32}],2:[function(e,t,r){"use strict";var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){"use strict";var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){"use strict";var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t.charCodeAt(a))];return-1^e}(0|t,e,e.length,0):0}},{"./utils":32}],5:[function(e,t,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(e,t,r){"use strict";var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n}},{lie:37}],7:[function(e,t,r){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e,t){a.call(this,"FlateWorker/"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={}}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.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 i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta})}},r.compressWorker=function(e){return new h("Deflate",e)},r.uncompressWorker=function(){return new h("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(e,t,r){"use strict";function A(e,t){var r,n="";for(r=0;r>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),c=I.transformTo("string",O.utf8encode(h.name)),d=h.comment,p=I.transformTo("string",s(d)),m=I.transformTo("string",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||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)+c,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(n,4)+f+b+p}}var I=e("../utils"),i=e("../stream/GenericWorker"),O=e("../utf8"),B=e("../crc32"),R=e("../signature");function s(e,t,r,n){i.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}))},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}})}else this.accumulate=!0},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),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 e=this.bytesWritten,t=0;t=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo("string",this.readData(e))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i},{"../utils":32}],19:[function(e,t,r){"use strict";var n=e("./Uint8ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./DataReader":18}],21:[function(e,t,r){"use strict";var n=e("./ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./ArrayReader":17}],22:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../support"),s=e("./ArrayReader"),a=e("./StringReader"),o=e("./NodeBufferReader"),h=e("./Uint8ArrayReader");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),"string"!==t||i.uint8array?"nodebuffer"===t?new o(e):i.uint8array?new h(n.transformTo("uint8array",e)):new s(n.transformTo("array",e)):new a(e)}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(e,t,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(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../utils");function s(e){n.call(this,"ConvertWorker to "+e),this.destType=e}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta})},t.exports=s},{"../utils":32,"./GenericWorker":28}],25:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../crc32");function s(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}e("../utils").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e)},t.exports=s},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataLengthProbe for "+e),this.propName=e,this.withStreamInfo(e,0)}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length}i.prototype.processChunk.call(this,e)},t.exports=s},{"../utils":32,"./GenericWorker":28}],27:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataWorker");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat()},function(e){t.error(e)})}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":e=this.data.substring(this.index,t);break;case"uint8array":e=this.data.subarray(this.index,t);break;case"array":case"nodebuffer":e=this.data.slice(this.index,t)}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s},{"../utils":32,"./GenericWorker":28}],28:[function(e,t,r){"use strict";function n(e){this.name=e||"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}n.prototype={push:function(e){this.emit("data",e)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(e){this.emit("error",e)}return!0},error:function(e){return!this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit("error",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(e,t){if(this._listeners[e])for(var r=0;r "+e:e}},t.exports=n},{}],29:[function(e,t,r){"use strict";var h=e("../utils"),i=e("./ConvertWorker"),s=e("./GenericWorker"),u=e("../base64"),n=e("../support"),a=e("../external"),o=null;if(n.nodestream)try{o=e("../nodejs/NodejsStreamOutputAdapter")}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on("data",function(e,t){n.push(e),o&&o(t)}).on("error",function(e){n=[],r(e)}).on("end",function(){try{var e=function(e,t,r){switch(e){case"blob":return h.newBlob(h.transformTo("arraybuffer",t),r);case"base64":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t}(e)},s.utf8decode=function(e){return h.nodebuffer?o.transformTo("nodebuffer",e).toString("utf-8"):function(e){var t,r,n,i,s=e.length,a=new Array(2*s);for(t=r=0;t>10&1023,a[r++]=56320|1023&n)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(e=o.transformTo(h.uint8array?"uint8array":"array",e))},o.inherits(a,n),a.prototype.processChunk=function(e){var t=o.transformTo(h.uint8array?"uint8array":"array",e.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=t;(t=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),t.set(r,this.leftOver.length)}else t=this.leftOver.concat(t);this.leftOver=null}var n=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}(t),i=t;n!==t.length&&(h.uint8array?(i=t.subarray(0,n),this.leftOver=t.subarray(n,t.length)):(i=t.slice(0,n),this.leftOver=t.slice(n,t.length))),this.push({data:s.utf8decode(i),meta:e.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,n),l.prototype.processChunk=function(e){this.push({data:s.utf8encode(e.data),meta:e.meta})},s.Utf8EncodeWorker=l},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(e,t,a){"use strict";var o=e("./support"),h=e("./base64"),r=e("./nodejsUtils"),u=e("./external");function n(e){return e}function l(e,t){for(var r=0;r>8;this.dir=!!(16&this.externalFileAttributes),0==e&&(this.dosPermissions=63&this.externalFileAttributes),3==e&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var e=n(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(e){var t,r,n,i=e.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});e.index+4>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t},r.buf2binstring=function(e){return l(e,e.length)},r.binstring2buf=function(e){for(var t=new h.Buf8(e.length),r=0,n=t.length;r>10&1023,o[n++]=56320|1023&i)}return l(o,n)},r.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}},{"./common":41}],43:[function(e,t,r){"use strict";t.exports=function(e,t,r,n){for(var i=65535&e|0,s=e>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}},{}],46:[function(e,t,r){"use strict";var h,c=e("../utils/common"),u=e("./trees"),d=e("./adler32"),p=e("./crc32"),n=e("./messages"),l=0,f=4,m=0,_=-2,g=-1,b=4,i=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(e,t){return e.msg=n[t],t}function T(e){return(e<<1)-(4e.avail_out&&(r=e.avail_out),0!==r&&(c.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0))}function N(e,t){u._tr_flush_block(e,0<=e.block_start?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,F(e.strm)}function U(e,t){e.pending_buf[e.pending++]=t}function P(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t}function L(e,t){var r,n,i=e.max_chain_length,s=e.strstart,a=e.prev_length,o=e.nice_match,h=e.strstart>e.w_size-z?e.strstart-(e.w_size-z):0,u=e.window,l=e.w_mask,f=e.prev,c=e.strstart+S,d=u[s+a-1],p=u[s+a];e.prev_length>=e.good_match&&(i>>=2),o>e.lookahead&&(o=e.lookahead);do{if(u[(r=t)+a]===p&&u[r+a-1]===d&&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!=--i);return a<=e.lookahead?a:e.lookahead}function j(e){var t,r,n,i,s,a,o,h,u,l,f=e.w_size;do{if(i=e.window_size-e.lookahead-e.strstart,e.strstart>=f+(f-z)){for(c.arraySet(e.window,e.window,f,f,0),e.match_start-=f,e.strstart-=f,e.block_start-=f,t=r=e.hash_size;n=e.head[--t],e.head[t]=f<=n?n-f:0,--r;);for(t=r=f;n=e.prev[--t],e.prev[t]=f<=n?n-f:0,--r;);i+=f}if(0===e.strm.avail_in)break;if(a=e.strm,o=e.window,h=e.strstart+e.lookahead,u=i,l=void 0,l=a.avail_in,u=x)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x)if(n=u._tr_tally(e,e.strstart-e.match_start,e.match_length-x),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=x){for(e.match_length--;e.strstart++,e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x&&e.match_length<=e.prev_length){for(i=e.strstart+e.lookahead-x,n=u._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-x),e.lookahead-=e.prev_length-1,e.prev_length-=2;++e.strstart<=i&&(e.ins_h=(e.ins_h<e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(j(e),0===e.lookahead&&t===l)return A;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,N(e,!1),0===e.strm.avail_out))return A;if(e.strstart-e.block_start>=e.w_size-z&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):(e.strstart>e.block_start&&(N(e,!1),e.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(e,t){return Y(e,t,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?_:(e.state.gzhead=t,m):_},r.deflate=function(e,t){var r,n,i,s;if(!e||!e.state||5>8&255),U(n,n.gzhead.time>>16&255),U(n,n.gzhead.time>>24&255),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,255&n.gzhead.os),n.gzhead.extra&&n.gzhead.extra.length&&(U(n,255&n.gzhead.extra.length),U(n,n.gzhead.extra.length>>8&255)),n.gzhead.hcrc&&(e.adler=p(e.adler,n.pending_buf,n.pending,0)),n.gzindex=0,n.status=69):(U(n,0),U(n,0),U(n,0),U(n,0),U(n,0),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,3),n.status=E);else{var a=v+(n.w_bits-8<<4)<<8;a|=(2<=n.strategy||n.level<2?0:n.level<6?1:6===n.level?2:3)<<6,0!==n.strstart&&(a|=32),a+=31-a%31,n.status=E,P(n,a),0!==n.strstart&&(P(n,e.adler>>>16),P(n,65535&e.adler)),e.adler=1}if(69===n.status)if(n.gzhead.extra){for(i=n.pending;n.gzindex<(65535&n.gzhead.extra.length)&&(n.pending!==n.pending_buf_size||(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending!==n.pending_buf_size));)U(n,255&n.gzhead.extra[n.gzindex]),n.gzindex++;n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),n.gzindex===n.gzhead.extra.length&&(n.gzindex=0,n.status=73)}else n.status=73;if(73===n.status)if(n.gzhead.name){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.gzindex=0,n.status=91)}else n.status=91;if(91===n.status)if(n.gzhead.comment){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.status=103)}else n.status=103;if(103===n.status&&(n.gzhead.hcrc?(n.pending+2>n.pending_buf_size&&F(e),n.pending+2<=n.pending_buf_size&&(U(n,255&e.adler),U(n,e.adler>>8&255),e.adler=0,n.status=E)):n.status=E),0!==n.pending){if(F(e),0===e.avail_out)return n.last_flush=-1,m}else if(0===e.avail_in&&T(t)<=T(r)&&t!==f)return R(e,-5);if(666===n.status&&0!==e.avail_in)return R(e,-5);if(0!==e.avail_in||0!==n.lookahead||t!==l&&666!==n.status){var o=2===n.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(j(e),0===e.lookahead)){if(t===l)return A;break}if(e.match_length=0,r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):3===n.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=S){if(j(e),e.lookahead<=S&&t===l)return A;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=x&&0e.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=x?(r=u._tr_tally(e,1,e.match_length-x),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):h[n.level].func(n,t);if(o!==O&&o!==B||(n.status=666),o===A||o===O)return 0===e.avail_out&&(n.last_flush=-1),m;if(o===I&&(1===t?u._tr_align(n):5!==t&&(u._tr_stored_block(n,0,0,!1),3===t&&(D(n.head),0===n.lookahead&&(n.strstart=0,n.block_start=0,n.insert=0))),F(e),0===e.avail_out))return n.last_flush=-1,m}return t!==f?m:n.wrap<=0?1:(2===n.wrap?(U(n,255&e.adler),U(n,e.adler>>8&255),U(n,e.adler>>16&255),U(n,e.adler>>24&255),U(n,255&e.total_in),U(n,e.total_in>>8&255),U(n,e.total_in>>16&255),U(n,e.total_in>>24&255)):(P(n,e.adler>>>16),P(n,65535&e.adler)),F(e),0=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new c.Buf8(r.w_size),c.arraySet(u,t,l-r.w_size,r.w_size,0),t=u,l=r.w_size),a=e.avail_in,o=e.next_in,h=e.input,e.avail_in=l,e.next_in=0,e.input=t,j(r);r.lookahead>=x;){for(n=r.strstart,i=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)+(d&(1<>>=y,p-=y),p<15&&(d+=z[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<>>=y,p-=y,(y=s-a)>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<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(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,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){e.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){e.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){e.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 e;o--,u+=n[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 e;o--,u+=n[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 e;o--,u+=n[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<(d=r.length)&&(d=o),d&&(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,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.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 e;o--,u+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=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){e.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 e;o--,u+=n[s++]<>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){e.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],d=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+d>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.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){e.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){e.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.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 e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[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){e.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 e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.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){e.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(hd?(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=t[r+a[v]]}if(k>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u>=7;n>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{"../utils/common":41}],53:[function(e,t,r){"use strict";t.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(e,t,r){(function(e){!function(r,n){"use strict";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i="[object process]"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage("","*"),r.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",r.addEventListener?r.addEventListener("message",d,!1):r.attachEvent("onmessage",d),function(e){r.postMessage(a+e,"*")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&"onreadystatechange"in l.createElement("script")?(s=l.documentElement,function(e){var t=l.createElement("script");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r"], 10 | "js": ["index.js"], 11 | "run_at": "document_start", 12 | "all_frames": true 13 | } 14 | ], 15 | "web_accessible_resources": [{ 16 | "resources": ["webglripper.js", "jszip.min.js"], 17 | "matches": [""] 18 | }], 19 | "options_ui": { 20 | "page": "options.html", 21 | "browser_style": true 22 | }, 23 | "icons": { 24 | "16": "icon.png", 25 | "48": "icon.png", 26 | "128": "icon.png" 27 | }, 28 | "permissions": [ 29 | "", 30 | "contextMenus", 31 | "tabs", 32 | "storage" 33 | ], 34 | "browser_specific_settings": { 35 | "gecko": { 36 | "id": "rilshrink@webglripper" 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WebGL Ripper Options 5 | 6 | 34 | 35 | 36 | 37 |
38 | 50 |
51 | 56 | 57 |
58 | 63 | 64 |
65 | 70 | 71 |
72 | 77 | 78 |
79 | 84 | 85 |
86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /options.js: -------------------------------------------------------------------------------- 1 | // Saves options to chrome.storage 2 | function save_options() { 3 | let defaultTextureRes = document.getElementById('ripper_defaulttexres').value; 4 | let minimumClears = document.getElementById('ripper_minimumclears').value; 5 | let shadercalc = document.getElementById('ripper_shadercalc').checked; 6 | let debugmode = document.getElementById('ripper_debug').checked; 7 | let unfliptex = document.getElementById('ripper_shouldfliptextures').checked; 8 | let downloadzip = document.getElementById('ripper_downloadzip').checked; 9 | (chrome || browser).storage.sync.set({ 10 | default_texture_res: defaultTextureRes, 11 | do_shader_calc: shadercalc, 12 | is_debug_mode: debugmode, 13 | unflip_textures: unfliptex, 14 | should_download_zip: downloadzip, 15 | minimum_clears: minimumClears 16 | }, function() { 17 | // Update status to let user know options were saved. 18 | var status = document.getElementById('status'); 19 | status.textContent = 'Options saved.'; 20 | setTimeout(function() { 21 | status.textContent = ''; 22 | }, 750); 23 | }); 24 | } 25 | 26 | // Restores select box and checkbox state using the preferences 27 | // stored in chrome.storage. 28 | function restore_options() { 29 | (chrome || browser).storage.sync.get({ 30 | default_texture_res: '4096x4096', 31 | do_shader_calc: false, 32 | is_debug_mode: false, 33 | unflip_textures: true, 34 | should_download_zip: false, 35 | minimum_clears: 1 36 | }, function(items) { 37 | document.getElementById('ripper_defaulttexres').value = items.default_texture_res; 38 | document.getElementById('ripper_shadercalc').checked = items.do_shader_calc; 39 | document.getElementById('ripper_debug').checked = items.is_debug_mode; 40 | document.getElementById('ripper_shouldfliptextures').checked = items.unflip_textures; 41 | document.getElementById('ripper_downloadzip').checked = items.should_download_zip; 42 | document.getElementById('ripper_minimumclears').value = items.minimum_clears; 43 | }); 44 | } 45 | 46 | document.addEventListener('keydown', function (event) { 47 | 48 | }); 49 | 50 | document.addEventListener('DOMContentLoaded', restore_options); 51 | document.getElementById('save').addEventListener('click', save_options); -------------------------------------------------------------------------------- /webglripper.js: -------------------------------------------------------------------------------- 1 | const OBJUtils = { 2 | DrawModes: { 3 | "POINTS": 0, 4 | "LINES": 1, 5 | "LINE_STRIP": 2, 6 | "LINE_LOOP": 3, 7 | "TRIANGLES": 4, 8 | "TRIANGLE_STRIP": 5, 9 | "TRIANGE_FAN": 6 10 | }, 11 | 12 | /* Used to store a texture for use in OBJModel */ 13 | OBJTexture: class { 14 | constructor(filename, url, type = "map_Kd") { 15 | this._URL = url; 16 | this._FILENAME = filename; 17 | this._TYPE = type; 18 | } 19 | 20 | getTexString() { 21 | return `${this._TYPE} ${this._FILENAME}.png`; 22 | } 23 | }, 24 | 25 | OBJColor: class { 26 | r = 0; 27 | g = 0; 28 | b = 0; 29 | constructor(r, g, b, a) { 30 | this.r = r; 31 | this.g = g; 32 | this.b = b; 33 | } 34 | 35 | toString() { 36 | return `${this.r} ${this.g} ${this.b}`; 37 | } 38 | }, 39 | 40 | /* Used to store color info about the primitive */ 41 | OBJColTexture: class { 42 | constructor(filename, color) { 43 | this._COLOR = color; 44 | this._FILENAME = filename; 45 | } 46 | 47 | getTexString() { 48 | return `Kd ${this._COLOR.toString()}`; 49 | } 50 | }, 51 | 52 | /* Used to store indices and how to draw them */ 53 | OBJPrimitive: class { 54 | constructor(mode, indices) { 55 | this._MODE = mode; 56 | this._INDICES = indices; 57 | } 58 | }, 59 | 60 | /* OBJ Creation */ 61 | OBJModel: class { 62 | // Primitives = [] // Array of OBJPrimitive's 63 | // Vertices = [] // Array of Vertices 64 | // Normals = [] // Array of Normals 65 | // UVs = [] // Array of UVs 66 | // Textures = [] // Array of OBJTexture or OBJColTexture for the obj 67 | constructor(_Primitives, _Vertices, _Normals, _UVs, _Textures, _Name) { 68 | this.vertex = _Vertices; 69 | this.normal = _Normals; 70 | this.uv = _UVs; 71 | this.primitives = _Primitives; 72 | this.textures = _Textures; 73 | this.name = _Name || `rip${Math.random()}`; 74 | } 75 | 76 | transform(matrix) { 77 | LogToParent("Transforming vertices: ", this.vertex.length/3); 78 | for (let vI = 0; vI < this.vertex.length; vI += 3) { 79 | let v = [this.vertex[vI + 0], this.vertex[vI + 1], this.vertex[vI + 2]]; 80 | let transformed = multiplyMatrixAndPoint(matrix, v); 81 | this.vertex[vI + 0] = transformed[0]; 82 | this.vertex[vI + 1] = transformed[1]; 83 | this.vertex[vI + 2] = transformed[2]; 84 | } 85 | } 86 | 87 | BuildOBJ() { 88 | let obj = ''; 89 | obj += `mtllib ${this.name}.mtl\n`; // Set model library to use 90 | obj += `o ${this.name}\n`; // Define model name 91 | 92 | for (let vI = 0; vI < this.vertex.length; vI += 3) { 93 | obj += 'v '; 94 | for (let vJ = 0; vJ < 3; ++vJ) 95 | obj += this.vertex[vI + vJ] + ' '; 96 | obj += '\n'; 97 | } // Write all vertex positions into the obj file 98 | 99 | for (let nI = 0; nI < this.normal.length; nI += 3) { 100 | obj += 'vn '; 101 | for (let nJ = 0; nJ < 3; ++nJ) 102 | obj += this.normal[nI + nJ] + ' '; 103 | obj += '\n'; 104 | } // Write all normal positions into the obj file 105 | 106 | for (let uI = 0; uI < this.uv.length; uI += 2) { 107 | obj += 'vt '; 108 | for (let uJ = 0; uJ < 2; ++uJ) 109 | obj += this.uv[uI + uJ] + ' '; 110 | obj += '\n'; 111 | } // Write all Texture Coords into the obj file 112 | 113 | obj += `usemtl ${this.name}\n`; // Specify to start using the mtl lib for the indices below 114 | obj += 's on \n'; // Enable Smooth Shading 115 | 116 | let hasNormals = this.normal.length != 0; 117 | let hasUVs = this.uv.length != 0; 118 | 119 | let primitive = this.primitives; 120 | switch (primitive._MODE) { 121 | case OBJUtils.DrawModes["TRIANGLES"]: 122 | case OBJUtils.DrawModes["TRIANGLE_STRIP"]: 123 | let isStrip = (primitive._MODE == OBJUtils.DrawModes["TRIANGLE_STRIP"]); 124 | for (let j = 0; j + 2 < primitive._INDICES.length; !isStrip ? j += 3 : j++) { 125 | obj += 'f '; 126 | let order = [0, 1, 2]; 127 | if (isStrip && (j % 2 == 1)) { 128 | order = [0, 2, 1]; 129 | } 130 | for (let k = 0; k < 3; ++k) { 131 | let faceNumber = primitive._INDICES[j + order[k]] + 1; 132 | obj += faceNumber; 133 | if (hasNormals || hasUVs) { 134 | obj += '/'; 135 | if (hasUVs) 136 | obj += faceNumber; 137 | if (hasNormals) 138 | obj += `/${faceNumber}`; 139 | } 140 | obj += ' '; 141 | } 142 | obj += '\n'; 143 | } 144 | break; 145 | } // Write indices into obj file 146 | return obj; 147 | } 148 | 149 | BuildMTL() { 150 | let mtl = ''; 151 | mtl += `newmtl ${this.name}\n`; 152 | this.textures.forEach(function (texture) { 153 | mtl += `${texture.getTexString()}\n`; 154 | }); 155 | return mtl; 156 | } 157 | } 158 | } // OBJ File Namespace 159 | 160 | class Downloader { 161 | static async DownloadImage(filename, url) { 162 | const a = document.createElement("a"); 163 | a.href = await this.toDataURL(url); 164 | a.download = filename; 165 | document.body.appendChild(a); 166 | a.click(); 167 | document.body.removeChild(a); 168 | } 169 | 170 | static async DownloadBlob(filename, blob) { 171 | const a = document.createElement("a"); 172 | a.href = URL.createObjectURL(blob); 173 | a.download = filename; 174 | document.body.appendChild(a); 175 | a.click(); 176 | document.body.removeChild(a); 177 | } 178 | 179 | static DownloadString(filename, str) { 180 | var textblob = new Blob([str], { type: 'text/plain' }); 181 | var link = document.createElement('a'); 182 | link.download = filename; 183 | link.innerHTML = 'Download File'; 184 | link.href = window.URL.createObjectURL(textblob); 185 | link.onclick = function (e) { document.body.removeChild(e.target); }; 186 | link.style.display = 'none'; 187 | document.body.appendChild(link); 188 | link.click(); 189 | } 190 | 191 | static toDataURL(url) { 192 | return fetch(url).then((response) => { 193 | return response.blob(); 194 | }).then(blob => { 195 | return URL.createObjectURL(blob); 196 | }); 197 | } 198 | } // Downloader Class 199 | 200 | let _window = window; 201 | 202 | // Create config object 203 | _window.WEBGLRipperSettings = { 204 | // Settings 205 | hasLoadedSettings: false, 206 | CaptureSceneKeyCode: 45, // Insert Key 207 | CaptureTexturesKeyCode: 45, // Insert Key 208 | defaultTexWidth: 4096, 209 | defaultTexHeight: 4096, // As we can't always retrieve the width and height of a texture, we must have a default size in that case. 210 | shouldUnFlipTex: true, // If we should unflip the textures 211 | isDebug: true, // Debug Printing 212 | isDoShaderCalc: false, // Force the shader to do calculations, useful for grabbing specific frames of vertex animations. 213 | shouldDownloadZip: false, // Download all the model assets into a zip file. 214 | minimumClears: 1, // Minimum amount of clears 215 | }; 216 | 217 | let LogToParent = function () { 218 | if (!_window.WEBGLRipperSettings.isDebug) 219 | return; 220 | _window.console.log('[WebGLRipper]', ...arguments); 221 | }; 222 | 223 | // https://stackoverflow.com/questions/5629684/how-can-i-check-if-an-element-exists-in-the-visible-dom 224 | let doesElementExist = function(element) { 225 | return typeof(element) != 'undefined' && element != null; 226 | }; 227 | 228 | let loadWebGLRipperSettings = function() { 229 | if(!doesElementExist(document.getElementById("webgl_ripper_settings"))) { 230 | LogToParent("Settings failed to load!"); 231 | return; 232 | } 233 | 234 | if(_window.WEBGLRipperSettings.hasLoadedSettings) { 235 | LogToParent("Settings already loaded!"); 236 | return; 237 | } 238 | 239 | let hiddenSettings = document.getElementById("webgl_ripper_settings"); 240 | let settings = JSON.parse(hiddenSettings.textContent); 241 | LogToParent("Loaded Settings: ", settings); 242 | 243 | _window.WEBGLRipperSettings.defaultTexWidth = parseInt(settings.default_texture_res.split("x")[0]) || 4096; 244 | _window.WEBGLRipperSettings.defaultTexHeight = parseInt(settings.default_texture_res.split("x")[1]) || 4096; 245 | _window.WEBGLRipperSettings.isDoShaderCalc = settings.do_shader_calc; 246 | _window.WEBGLRipperSettings.isDebug = settings.is_debug_mode; 247 | _window.WEBGLRipperSettings.shouldUnFlipTex = settings.unflip_textures; 248 | _window.WEBGLRipperSettings.shouldDownloadZip = settings.should_download_zip; 249 | _window.WEBGLRipperSettings.minimumClears = parseInt(settings.minimum_clears); 250 | _window.WEBGLRipperSettings.hasLoadedSettings = true; 251 | }; 252 | 253 | _window.RIPPERS = []; 254 | _window.MODELS = []; 255 | 256 | document.addEventListener('keydown', function (event) { 257 | if (event.keyCode == _window.WEBGLRipperSettings.CaptureSceneKeyCode && !event.shiftKey) { 258 | LogToParent("Starting capturing..."); 259 | for(let i = 0; i < _window.RIPPERS.length; i++) { 260 | LogToParent("Started capture on: ", _window.RIPPERS[ i ]); 261 | _window.RIPPERS[ i ]._StartCapturing = true; 262 | } 263 | } 264 | }); 265 | 266 | function resizeArrayBuffer(originalBuffer, newByteSize) { 267 | if(originalBuffer.byteLength > newByteSize){ 268 | throw new Error("Can't resize to a smaller array"); 269 | } 270 | 271 | if(originalBuffer.detached) 272 | return; // TODO: Fix 273 | 274 | const resizedBuffer = new ArrayBuffer(newByteSize); 275 | const originalView = new Uint8Array(originalBuffer); 276 | const resizedView = new Uint8Array(resizedBuffer); 277 | resizedView.set(originalView); 278 | return resizedBuffer; 279 | } 280 | 281 | // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Matrix_math_for_the_web 282 | function multiplyMatrixAndPoint(matrix, point) { 283 | let c0r0 = matrix[0], c1r0 = matrix[1], c2r0 = matrix[2], c3r0 = matrix[3]; 284 | let c0r1 = matrix[4], c1r1 = matrix[5], c2r1 = matrix[6], c3r1 = matrix[7]; 285 | let c0r2 = matrix[8], c1r2 = matrix[9], c2r2 = matrix[10], c3r2 = matrix[11]; 286 | let c0r3 = matrix[12], c1r3 = matrix[13], c2r3 = matrix[14], c3r3 = matrix[15]; 287 | 288 | let x = point[0]; 289 | let y = point[1]; 290 | let z = point[2]; 291 | let w = 1; 292 | 293 | let resultX = x * c0r0 + y * c0r1 + z * c0r2 + w * c0r3; 294 | let resultY = x * c1r0 + y * c1r1 + z * c1r2 + w * c1r3; 295 | let resultZ = x * c2r0 + y * c2r1 + z * c2r2 + w * c2r3; 296 | return [resultX, resultY, resultZ]; 297 | } 298 | 299 | class WebGLRipperWrapper { 300 | _StartCapturing = false; 301 | 302 | _IsEnabled = true; 303 | _IsWebGL2 = false; 304 | _GLViewport = { x: 0, y: 0, width: 0, height: 0 }; 305 | _GLContext = null; 306 | _GLState = new Map(); 307 | _GLBuffers = new Map(); 308 | _GLActiveTextureIndex = 0; 309 | _GLCurrentBoundTexture = null; 310 | _GLTextures = new Map(); 311 | _GLAllTextures = []; 312 | 313 | _GLCurrentUVS = []; 314 | _GLCurrentUVIndex = -1; 315 | _GLCurrentNormals = []; 316 | _GLCurrentNormalIndex = -1; 317 | _GLCurrentVertices = []; 318 | _GLCurrentVertexIndex = -1; 319 | 320 | _GLCurrentAttribIndex = 0; 321 | _GLCurrentAttrib = []; 322 | _GLCurrentAttribEnabled = []; 323 | 324 | _ClearCount = 0; 325 | _CurrentModels = []; 326 | _TextureCache = new Map(); 327 | _isCapturing = false; 328 | _needsToReset = false; 329 | 330 | _AttribTypeEnum = { 331 | VERTEX: 0, 332 | NORMAL: 1, 333 | UVS: 2 334 | }; 335 | 336 | constructor(gl) { 337 | this._GLContext = gl; 338 | } 339 | 340 | HelperFunc_GetDataURIFromWebGLTexture(gl, webglTexture, texWidth, texHeight, flip = true) { 341 | let fb = gl.createFramebuffer(); 342 | 343 | // make this the current frame buffer 344 | gl.bindFramebuffer(gl.FRAMEBUFFER, fb); 345 | 346 | // attach the texture to the framebuffer. 347 | gl.framebufferTexture2D( 348 | gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 349 | gl.TEXTURE_2D, webglTexture, 0); 350 | 351 | // check if you can read from this type of texture. 352 | let canRead = (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE); 353 | 354 | if (!canRead) { 355 | // Unbind the framebuffer 356 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 357 | return null; 358 | } 359 | 360 | var pixels = new Uint8Array(texWidth * texHeight * 4); 361 | // read the pixels 362 | gl.readPixels(0, 0, texWidth, texHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); 363 | 364 | // Unbind the framebuffer 365 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 366 | 367 | if (flip) { 368 | // Now do pixel manipulation to make it right side up -- https://stackoverflow.com/questions/41969562/how-can-i-flip-the-result-of-webglrenderingcontext-readpixels 369 | var halfHeight = texHeight / 2 | 0; // the | 0 keeps the result an int 370 | var bytesPerRow = texWidth * 4; 371 | 372 | // make a temp buffer to hold one row 373 | var temp = new Uint8Array(texWidth * 4); 374 | for (var y = 0; y < halfHeight; ++y) { 375 | var topOffset = y * bytesPerRow; 376 | var bottomOffset = (texHeight - y - 1) * bytesPerRow; 377 | 378 | // make copy of a row on the top half 379 | temp.set(pixels.subarray(topOffset, topOffset + bytesPerRow)); 380 | 381 | // copy a row from the bottom half to the top 382 | pixels.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow); 383 | 384 | // copy the copy of the top half row to the bottom half 385 | pixels.set(temp, bottomOffset); 386 | } 387 | } 388 | 389 | let canvas = document.createElement("canvas"); 390 | canvas.width = texWidth; 391 | canvas.height = texHeight; 392 | 393 | let ctx = canvas.getContext('2d'); 394 | 395 | let arr = new Uint8ClampedArray(pixels); 396 | 397 | let imgData = new ImageData(arr, texWidth, texHeight); 398 | ctx.putImageData(imgData, 0, 0); 399 | return canvas.toDataURL("image/png"); 400 | } 401 | 402 | HelperFunc_GetAttribValueType(attrib) { 403 | let vertexNames = [ 404 | 'position', 405 | 'vertex', 406 | 'avertexposition', 407 | 's_attribute_0', 408 | 'avertex', 409 | 'vertex_position', // Playcanvas.js 410 | 'aposition', // Playcanvas.js 411 | 'vposition', // imvu.com 412 | 'vertexposition', // Raylib 413 | 'a_pos' // nick.com 414 | ]; 415 | 416 | let normalNames = [ 417 | 'avertexnormal', 418 | 'normal', 419 | 's_attribute_1', 420 | 'vertex_normal', // Playcanvas.js 421 | 'vnormal', // imvu.com 422 | 'vertexnormal', // Raylib 423 | ]; 424 | 425 | let uvNames = [ 426 | 'uv', 427 | 'texcoord', 428 | 'texcoords', 429 | 'texcoord0', 430 | 'atexturecoord', 431 | 'vertex_texcoord0', // Playcanvas.js 432 | 'vtexcoord', // imvu.com 433 | 'vertextexcoord', // Raylib 434 | 'a_uv', // nick.com 435 | ]; 436 | 437 | let attribName = attrib.name.toLowerCase(); 438 | if (vertexNames.includes(attribName)) 439 | return this._AttribTypeEnum.VERTEX; 440 | if (normalNames.includes(attribName)) 441 | return this._AttribTypeEnum.NORMAL; 442 | if (uvNames.includes(attribName)) 443 | return this._AttribTypeEnum.UVS; 444 | return -1; 445 | } 446 | 447 | HelperFunc_IsPossibleTextureUniform(texname) { 448 | const textureMap = new Map(); 449 | 450 | // Diffuse / Ambient 451 | textureMap.set('map' , 'map_Kd'); 452 | textureMap.set('usampler' , 'map_Kd'); 453 | textureMap.set('texture' , 'map_Kd'); 454 | textureMap.set('bonesampler' , 'map_Kd'); 455 | textureMap.set('bonetexture' , 'map_Kd'); 456 | textureMap.set('albedosampler' , 'map_Kd'); 457 | textureMap.set('source' , 'map_Kd'); 458 | textureMap.set('u_texture' , 'map_Kd'); 459 | textureMap.set('texture_envatlas', 'map_Kd'); 460 | // classic.minecraft.net 461 | textureMap.set("diffusesampler", 'map_Kd'); 462 | textureMap.set("ambientsampler", 'map_Kd'); 463 | for (let t = 0; t < 32; t++) 464 | textureMap.set(`texture${t}`,'map_Kd'); 465 | 466 | // Normals 467 | textureMap.set('normalmap', 'norm'); /* PBR rendering */ 468 | 469 | if(!textureMap.has(texname.toLowerCase())) { 470 | LogToParent("Not a known texture type: ", texname); 471 | return null; 472 | } 473 | 474 | return textureMap.get(texname.toLowerCase()); 475 | } 476 | 477 | HelperFunc_GetCurrentProgram(self, gl) { 478 | return gl.getParameter(gl.CURRENT_PROGRAM); 479 | } 480 | 481 | /* Used for debugging */ 482 | HelperFunc_DownloadTextureAtLoc(self, gl, loc) { 483 | /* See if a texture is bound to that slot */ 484 | let tex = self._GLTextures.get(loc); 485 | 486 | if (tex == undefined || !tex) { 487 | LogToParent("No Texture found in slot: ", loc); 488 | return; 489 | } 490 | 491 | let texWidth = tex.width || _window.WEBGLRipperSettings.defaultTexWidth; 492 | let texHeight = tex.height || _window.WEBGLRipperSettings.defaultTexHeight; 493 | let uri = self.HelperFunc_GetDataURIFromWebGLTexture(gl, tex, texWidth, texHeight, _window.WEBGLRipperSettings.shouldUnFlipTex); 494 | 495 | if (uri == null) { 496 | LogToParent("Recieved null texture!"); 497 | return; 498 | } 499 | 500 | let objTexture = new OBJUtils.OBJTexture("tex_" + self._CurrentModels.length, uri); 501 | Downloader.DownloadImage(objTexture._FILENAME, objTexture._URL); 502 | LogToParent("Recieved texture, Sampler Location: ", loc, ", WebGL Texture Object: ", tex); 503 | } 504 | 505 | HelperFunc_GetModelMatrix(self, gl) { 506 | let uniformData = self.readUniformData(gl); 507 | LogToParent("Current Uniform Data: ", uniformData); 508 | 509 | let _CurrentProgram = self.HelperFunc_GetCurrentProgram(self, gl); 510 | let modelMatrix = null; 511 | 512 | let modelMatrixs = [ 513 | "modelviewmatrix", // three.js 514 | "modelmatrix", 515 | "world", 516 | "matrix_model" 517 | ]; 518 | 519 | uniformData.forEach(uniform => { 520 | if (modelMatrixs.includes(uniform.name.toLowerCase())) { 521 | var loc = gl.getUniformLocation(_CurrentProgram, uniform.name); 522 | modelMatrix = gl.getUniform(_CurrentProgram, loc); 523 | LogToParent("Recieved modelMatrix: ", modelMatrix); 524 | } 525 | }); 526 | return modelMatrix; 527 | } 528 | 529 | HelperFunc_GetAllTextures(self, gl) { 530 | let textures = []; 531 | let uniformData = self.readUniformData(gl); 532 | LogToParent("Current Uniform Data: ", uniformData); 533 | 534 | let _CurrentProgram = self.HelperFunc_GetCurrentProgram(self, gl); 535 | 536 | uniformData.forEach(uniform => { 537 | if (uniform.type != gl.SAMPLER_2D) 538 | return; 539 | 540 | let texType = self.HelperFunc_IsPossibleTextureUniform(uniform.name); 541 | if (texType === null) 542 | return; 543 | 544 | /* Get the sampler location of the texture */ 545 | var loc = gl.getUniformLocation(_CurrentProgram, uniform.name); 546 | 547 | /* Read the location of the webgl texture slot from the sampler */ 548 | let samplerLocation = gl.getUniform(_CurrentProgram, loc); 549 | 550 | /* Make sure it's a texture slot */ 551 | if (samplerLocation < 0 || samplerLocation > 31) { 552 | LogToParent("Sampler location out of bounds: ", samplerLocation); 553 | return; 554 | } 555 | 556 | /* See if a texture is bound to that slot */ 557 | let tex = self._GLTextures.get(samplerLocation); 558 | 559 | if (tex == undefined || !tex) { 560 | LogToParent("No Texture found in slot: ", samplerLocation); 561 | return; 562 | } 563 | 564 | if (self._TextureCache.get(tex)) { 565 | textures.push(self._TextureCache.get(tex)); 566 | LogToParent("Texture already in cache"); 567 | return; 568 | } 569 | 570 | let texWidth = tex.width || _window.WEBGLRipperSettings.defaultTexWidth; 571 | let texHeight = tex.height || _window.WEBGLRipperSettings.defaultTexHeight; 572 | let uri = self.HelperFunc_GetDataURIFromWebGLTexture(gl, tex, texWidth, texHeight, _window.WEBGLRipperSettings.shouldUnFlipTex); 573 | 574 | if (uri == null) { 575 | LogToParent("Recieved null texture!"); 576 | return; 577 | } 578 | 579 | // Will not work on http servers :( 580 | let texName = crypto.randomUUID(); 581 | 582 | let objTexture = new OBJUtils.OBJTexture(texName, uri, texType); 583 | self._TextureCache.set(tex, objTexture); 584 | textures.push(objTexture); 585 | LogToParent("Recieved texture, Sampler Location: ", samplerLocation, ", WebGL Texture Object: ", tex, ", With texture type: ", texType); 586 | }); 587 | 588 | return textures; 589 | } 590 | 591 | HelperFunc_SizeOfType(self, gl, glType) { 592 | 593 | switch (glType) { 594 | case gl.BYTE: 595 | case gl.UNSIGNED_BYTE: 596 | return 1; 597 | case gl.SHORT: 598 | case gl.UNSIGNED_SHORT: 599 | return 2; 600 | default: 601 | case gl.FLOAT: 602 | return 4; 603 | } 604 | 605 | return 1; 606 | } 607 | 608 | HelperFunc_UpdateAllAttributes(self, gl) { // Got help from: https://github.com/benvanik/WebGL-Inspector/blob/c5f961dba261cbd94d9b3ff3ddbaf8b7d3bf5ef9/core/ui/shared/BufferPreview.js#L191 609 | let attribData = self.readAttribData(gl); 610 | 611 | attribData.forEach(function (attr) { 612 | 613 | if (!self._GLCurrentAttribEnabled[attr.loc]) 614 | return; 615 | 616 | let attribType = self.HelperFunc_GetAttribValueType(attr); 617 | if(attribType < 0) { 618 | LogToParent("Unknown Attrib Type: ", attr); 619 | return; 620 | } 621 | 622 | let _bufferData = self.getBufferDataFromBuffer(self, gl.getVertexAttrib(attr.loc, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)); 623 | if (!_bufferData || _bufferData.byteLength <= 0) 624 | return; 625 | 626 | let bufferData = []; 627 | 628 | let vAttribData = self._GLCurrentAttrib[attr.loc]; 629 | 630 | if(vAttribData == null) { 631 | LogToParent("Missing attrib data at location: ", attr.loc); 632 | return; 633 | } 634 | 635 | LogToParent("Got vAttribData: ", vAttribData, "Along with attr: ", attr); 636 | 637 | const SizeArrayMap = { 638 | [gl.BYTE]: 1 * vAttribData.size, 639 | [gl.UNSIGNED_BYTE]: 1 * vAttribData.size, 640 | [gl.SHORT]: 2 * vAttribData.size, 641 | [gl.UNSIGNED_SHORT]: 2 * vAttribData.size, 642 | [gl.FLOAT]: 4 * vAttribData.size 643 | }; 644 | 645 | let byteAdvance = SizeArrayMap[vAttribData.type]; 646 | let typeSize = byteAdvance / vAttribData.size; 647 | 648 | let fStride = vAttribData.stride ? vAttribData.stride : byteAdvance; 649 | 650 | const TypedArrayMap = { 651 | [gl.BYTE]: Int8Array, 652 | [gl.UNSIGNED_BYTE]: Uint8Array, 653 | [gl.SHORT]: Int16Array, 654 | [gl.UNSIGNED_SHORT]: Uint16Array, 655 | [gl.FLOAT]: Float32Array 656 | }; 657 | 658 | const TypedArrayConstructor = TypedArrayMap[vAttribData.type]; 659 | _bufferData = new TypedArrayConstructor(_bufferData, vAttribData.offset); 660 | 661 | let byteOffset = vAttribData.offset; 662 | while (byteOffset <= _bufferData.byteLength) { 663 | var readView = new TypedArrayConstructor(_bufferData.buffer.slice(byteOffset, byteOffset + fStride)); 664 | 665 | for (let i = 0; i < vAttribData.size; i++) { 666 | bufferData.push(readView[i]); 667 | } 668 | 669 | byteOffset += fStride; 670 | } 671 | 672 | if (!bufferData) { 673 | LogToParent("Couldn't get bufferData: ", attr); 674 | return; 675 | } 676 | 677 | bufferData = new TypedArrayConstructor(bufferData); 678 | 679 | LogToParent("Final Stride is: ", fStride, ", Buffer byte length: ", _bufferData.byteLength, "Original Buffer Data: ", _bufferData, "Got New Buffer Data: ", bufferData); 680 | 681 | switch (attribType) { 682 | case self._AttribTypeEnum.VERTEX: 683 | self._GLCurrentVertices = bufferData; 684 | self._GLCurrentVertexIndex = attr.loc; 685 | break; 686 | case self._AttribTypeEnum.NORMAL: 687 | self._GLCurrentNormals = bufferData; 688 | self._GLCurrentNormalIndex = attr.loc; 689 | break; 690 | case self._AttribTypeEnum.UVS: 691 | self._GLCurrentUVS = bufferData; 692 | self._GLCurrentUVIndex = attr.loc; 693 | break; 694 | default: 695 | LogToParent("Unknown Attrib Type: ", attr); 696 | break; 697 | } 698 | }); 699 | } 700 | 701 | HelperFunc_PerformRIP(self, gl) { 702 | LogToParent(`Downloading ${self._CurrentModels.length}`); 703 | let models = self._CurrentModels.slice(); // Create a copy 704 | 705 | // Download each model 706 | 707 | if(_window.WEBGLRipperSettings.shouldDownloadZip && JSZip) { 708 | const zip = new JSZip(); 709 | 710 | models.forEach(async function (obj) { 711 | zip.file(`${obj.name}.obj`, obj.BuildOBJ()); 712 | if (obj.textures.length <= 0) 713 | return; 714 | zip.file(`${obj.name}.mtl`, obj.BuildMTL()); 715 | }); 716 | 717 | let textures = []; 718 | let texcache = []; 719 | models.forEach(function (obj) { 720 | obj.textures.forEach(function (texture) { 721 | if (!texture._URL) 722 | return; 723 | if (texcache[texture._URL]) 724 | return; 725 | textures.push(texture); 726 | texcache[texture._URL] = true; 727 | }); 728 | }); 729 | 730 | textures.forEach(async function (texture) { 731 | zip.file(`${texture._FILENAME}.png`, texture._URL.replace("data:image/png;base64,", ""), {base64: true}); 732 | }); 733 | 734 | zip.generateAsync({type:"blob"}).then(function(content) { 735 | let dateString = new Date().toISOString(); 736 | Downloader.DownloadBlob(`${dateString}-rip.zip`, content); 737 | }); 738 | 739 | } else { 740 | function pause(msec) { 741 | return new Promise((resolve) => { 742 | setTimeout(resolve, msec || 1000); 743 | }); 744 | } 745 | 746 | async function downloadModel(obj) { 747 | await Downloader.DownloadString(`${obj.name}.obj`, obj.BuildOBJ()); 748 | if (obj.textures.length > 0) { 749 | await Downloader.DownloadString(`${obj.name}.mtl`, obj.BuildMTL()); 750 | } 751 | } 752 | 753 | async function downloadTexture(texture) { 754 | await Downloader.DownloadImage(texture._FILENAME, texture._URL); 755 | } 756 | 757 | async function downloadAll(elements, isModel = true) { 758 | var count = 0; 759 | for (const e of elements) { 760 | if (isModel) { 761 | await downloadModel(e); 762 | } else { 763 | await downloadTexture(e); 764 | } 765 | 766 | await pause(500); // Pause to not overload the browser in case of big scenes 767 | } 768 | } 769 | 770 | downloadAll(models).catch((error) => { 771 | console.error("An error occurred while downloading models:", error); 772 | }); 773 | 774 | // Download each texture 775 | 776 | let textures = []; 777 | let texcache = []; 778 | models.forEach(function (obj) { 779 | obj.textures.forEach(function (texture) { 780 | if (!texture._URL) return; 781 | if (texcache[texture._URL]) return; 782 | textures.push(texture); 783 | texcache[texture._URL] = true; 784 | }); 785 | }); 786 | 787 | downloadAll(textures, false).catch((error) => { 788 | console.error("An error occurred while downloading textures:", error); 789 | }); 790 | } 791 | 792 | // Reset vars 793 | 794 | self._isCapturing = false; 795 | self._needsToReset = true; 796 | self._CurrentModels = []; 797 | } 798 | 799 | HelperFunc_Flush(self, gl) { 800 | self._GLCurrentUVS = []; 801 | self._GLCurrentNormals = []; 802 | self._GLCurrentVertices = []; 803 | } 804 | 805 | HelperFunc_ResetAll(self, gl) { 806 | self._TextureCache = new Map(); 807 | self._ClearCount = 0; 808 | } 809 | 810 | hooked_viewport(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/viewport 811 | let _x = args[0]; 812 | let _y = args[1]; 813 | let _width = args[2]; 814 | let _height = args[3]; 815 | self._GLViewport = { x: _x, y: _y, width: _width, height: _height }; 816 | } 817 | 818 | hooked_activeTexture(self, gl, args, oFunc) { 819 | self._GLActiveTextureIndex = args[0] - gl.TEXTURE0; 820 | } 821 | 822 | hooked_texImage2D(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D 823 | let target = args[0]; 824 | if (target != gl.TEXTURE_2D) 825 | return; 826 | self._GLAllTextures.forEach(glTex => { 827 | if(glTex == self._GLCurrentBoundTexture) 828 | glTex.is2DTexture = true; 829 | }); 830 | 831 | // Attempt to get width and height of texture 832 | let pixels = null; 833 | switch (args.length) { 834 | case 9: 835 | pixels = args[8]; 836 | break; 837 | case 6: 838 | pixels = args[5]; 839 | break; 840 | } 841 | 842 | if (pixels == null) 843 | return; 844 | 845 | let _ArrayBufferView = (new Uint16Array()).constructor.prototype.__proto__.constructor; 846 | if ((pixels instanceof _ArrayBufferView)) 847 | return; 848 | 849 | if (pixels instanceof ImageData || 850 | pixels instanceof HTMLImageElement || 851 | pixels instanceof HTMLCanvasElement || 852 | pixels instanceof HTMLVideoElement || 853 | pixels instanceof ImageBitmap) { 854 | self._GLCurrentBoundTexture.width = pixels.width; 855 | self._GLCurrentBoundTexture.height = pixels.height; 856 | } 857 | } 858 | 859 | hooked_shaderSource(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/shaderSource 860 | let shader = args[0]; 861 | let source = args[1]; 862 | if (!shader || !source) 863 | return; 864 | //LogToParent("Got shader source: ", source); 865 | } 866 | 867 | hooked_linkProgram(self, gl, args, oFunc) { 868 | let program = args[0]; 869 | 870 | if (!_window.WEBGLRipperSettings.isDoShaderCalc) { 871 | return; 872 | } 873 | 874 | gl.transformFeedbackVaryings(program, ["gl_Position"], gl.SEPARATE_ATTRIBS); 875 | LogToParent("[ShaderCalc] Added Transform Feedback for gl_Position"); 876 | } 877 | 878 | hooked_bindTexture(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bindTexture 879 | let target = args[0]; 880 | let texture = args[1]; 881 | if (target != gl.TEXTURE_2D) 882 | return; 883 | if (texture == null) 884 | return; 885 | self._GLTextures.set(self._GLActiveTextureIndex, texture); 886 | self._GLCurrentBoundTexture = texture; 887 | } 888 | 889 | hooked_drawArrays(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/drawArrays 890 | if (!self._isCapturing) 891 | return; 892 | 893 | LogToParent("Captured 'drawArrays' call: ", args); 894 | 895 | let drawMode = args[0]; 896 | let indFirst = args[1]; 897 | let indCount = args[2]; 898 | 899 | switch (drawMode) { 900 | case OBJUtils.DrawModes.TRIANGLES: 901 | case OBJUtils.DrawModes.TRIANGLE_STRIP: 902 | break; 903 | default: 904 | LogToParent("Unsupported draw mode: ", drawMode); 905 | return; 906 | } 907 | 908 | self.HelperFunc_UpdateAllAttributes(self, gl); 909 | 910 | if (self._GLCurrentVertices.length <= 0) { 911 | LogToParent("Got no vertices in drawArrays call"); 912 | return; 913 | } 914 | 915 | if(_window.WEBGLRipperSettings.isDoShaderCalc) { 916 | /* Setup Area */ 917 | 918 | } 919 | 920 | let textures = self.HelperFunc_GetAllTextures(self, gl); 921 | let modelMatrix = self.HelperFunc_GetModelMatrix(self, gl); 922 | 923 | let indices = []; 924 | 925 | // Go through each position, see if it exists or not and add an indice to it! 926 | let indice = 0; 927 | for (let i = 0; i < self._GLCurrentVertices.length; i += 3) { 928 | indices.push(indice++); 929 | } 930 | 931 | LogToParent("Using indices, size: ", indices.length, ", to cut out from ", indFirst, " to ", indFirst, " + ", indCount); 932 | indices = indices.slice(indFirst, indFirst + indCount); 933 | LogToParent("New indices size: ", indices.length); 934 | 935 | let objPrimitives = new OBJUtils.OBJPrimitive(drawMode, indices); 936 | let objID = self._CurrentModels.length; 937 | let builtOBJ = new OBJUtils.OBJModel(objPrimitives, self._GLCurrentVertices, self._GLCurrentNormals, self._GLCurrentUVS, textures, `RIP${objID}`); 938 | if (modelMatrix){ 939 | builtOBJ.transform(modelMatrix); 940 | } 941 | self._CurrentModels.push(builtOBJ); 942 | LogToParent("Finished Building OBJ: ", builtOBJ); 943 | 944 | // Cleanup 945 | self.HelperFunc_Flush(self, gl); 946 | 947 | if(_window.WEBGLRipperSettings.isDoShaderCalc) 948 | return true; 949 | } 950 | 951 | hooked_drawElements(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/drawElements 952 | if (!self._isCapturing) 953 | return; 954 | 955 | LogToParent("Captured 'drawElements' call: ", args); 956 | 957 | let drawMode = args[0]; 958 | let indCount = args[1]; 959 | let indType = args[2]; 960 | let indOffset = args[3]; 961 | 962 | switch (drawMode) { 963 | case OBJUtils.DrawModes.TRIANGLES: 964 | case OBJUtils.DrawModes.TRIANGLE_STRIP: 965 | break; 966 | default: 967 | LogToParent("Unsupported draw mode: ", drawMode); 968 | return; 969 | } 970 | 971 | let oIndices = self.getBufferedIndices(self); 972 | if (!oIndices || oIndices == undefined) { 973 | LogToParent("No indices found in 'drawElements' call"); 974 | return; 975 | } 976 | 977 | self.HelperFunc_UpdateAllAttributes(self, gl); 978 | 979 | if(self._GLCurrentVertices.length <= 0) { 980 | LogToParent("Got no vertices in drawElements call"); 981 | return; 982 | } 983 | 984 | const indTypeMap = { 985 | [gl.UNSIGNED_BYTE]: 1, 986 | [gl.UNSIGNED_SHORT]: 2, 987 | [gl.UNSIGNED_INT]: 4 988 | }; 989 | 990 | let indTypeSize = indTypeMap[indType] || 2; // Usually indices are unsigned shorts so it makes sense to just make it the default 991 | 992 | let _indOffset = indOffset / indTypeSize; 993 | 994 | const indTypedArrayMap = { 995 | [gl.UNSIGNED_BYTE]: Uint8Array, 996 | [gl.UNSIGNED_SHORT]: Uint16Array, 997 | [gl.UNSIGNED_INT]: Uint32Array 998 | }; 999 | 1000 | let IndTypedArray = indTypedArrayMap[indType] || Uint16Array; 1001 | 1002 | if (oIndices instanceof ArrayBuffer) { 1003 | oIndices = new IndTypedArray(oIndices); 1004 | } 1005 | 1006 | LogToParent("Using indices, size: ", oIndices.length, ", to cut out from ", _indOffset, " to ", _indOffset, " + ", indCount); 1007 | let indices = new IndTypedArray(indCount); 1008 | 1009 | for (let ind = 0; ind < indCount; ind++) 1010 | indices[ind] = oIndices[_indOffset + ind]; 1011 | 1012 | LogToParent("New indices size: ", indices.length); 1013 | 1014 | if (_window.WEBGLRipperSettings.isDoShaderCalc) { 1015 | /* 1016 | TODO: Fix 1017 | - Convert drawElements call to drawArrays call 1018 | - Must sort vertices inline with indices to pass through 1019 | */ 1020 | 1021 | /* 1022 | 1023 | let drawArraysVertices = []; 1024 | 1025 | for(let ind = 0; ind < indCount; ind++) { 1026 | drawArraysVertices[ind] = self._GLCurrentVertices[indices[ind]]; 1027 | } 1028 | 1029 | const drawArraysBuffer = gl.createBuffer(); 1030 | gl.bindBuffer(gl.ARRAY_BUFFER, drawArraysBuffer); 1031 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(drawArraysVertices), gl.STATIC_DRAW); 1032 | 1033 | LogToParent("Converting drawElements to drawArray: ", drawArraysVertices); 1034 | 1035 | const numComponents = 1; // pull out 2 values per iteration 1036 | const type = gl.FLOAT; // the data in the buffer is 32bit floats 1037 | const normalize = false; // don't normalize 1038 | const stride = 0; // how many bytes to get from one set of values to the next 1039 | // 0 = use type and numComponents above 1040 | const offset = 0; // how many bytes inside the buffer to start from 1041 | gl.vertexAttribPointer(self._GLCurrentVertexIndex, numComponents, type, normalize, stride, offset); 1042 | gl.enableVertexAttribArray(self._GLCurrentVertexIndex); 1043 | 1044 | self.HelperFunc_Flush(self, gl); 1045 | gl.drawArrays(drawMode, 0, indCount); 1046 | */ 1047 | 1048 | return true; 1049 | } 1050 | 1051 | let textures = self.HelperFunc_GetAllTextures(self, gl); 1052 | let modelMatrix = self.HelperFunc_GetModelMatrix(self, gl); 1053 | 1054 | let objPrimitives = new OBJUtils.OBJPrimitive(drawMode, indices); 1055 | let objID = self._CurrentModels.length; 1056 | let builtOBJ = new OBJUtils.OBJModel(objPrimitives, self._GLCurrentVertices, self._GLCurrentNormals, self._GLCurrentUVS, textures, `RIP${objID}`); 1057 | if (modelMatrix){ 1058 | builtOBJ.transform(modelMatrix); 1059 | } 1060 | self._CurrentModels.push(builtOBJ); 1061 | LogToParent("Finished Building OBJ: ", builtOBJ); 1062 | 1063 | // Cleanup 1064 | self.HelperFunc_Flush(self, gl); 1065 | } 1066 | 1067 | hooked_drawRangeElements(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/drawRangeElements 1068 | if (!self._isCapturing) 1069 | return; 1070 | 1071 | let drawMode = args[0]; 1072 | let start = args[1]; 1073 | let end = args[2]; 1074 | let count = args[3]; 1075 | let type = args[4]; 1076 | let offset = args[5]; 1077 | 1078 | LogToParent("Captured unsupported 'drawRangeElements' call: ", args); 1079 | } 1080 | 1081 | hooked_drawBuffers(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/drawBuffers 1082 | if (!self._isCapturing) 1083 | return; 1084 | 1085 | let buffers = args[0]; 1086 | 1087 | LogToParent("Captured unsupported 'drawBuffers' call: ", args); 1088 | } 1089 | 1090 | hooked_drawElementsInstanced(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/drawElementsInstanced 1091 | if (!self._isCapturing) 1092 | return; 1093 | 1094 | let drawMode = args[0]; 1095 | let count = args[1]; 1096 | let type = args[2]; 1097 | let offset = args[3]; 1098 | let instanceCount = args[4]; 1099 | 1100 | LogToParent("Captured unsupported 'drawElementsInstanced' call: ", args); 1101 | LogToParent("Forwarding to 'drawElements'..."); 1102 | self.hooked_drawElements(self, gl, args, oFunc); 1103 | } 1104 | 1105 | hooked_createTexture(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/createTexture 1106 | let glTexture = oFunc.apply(gl); 1107 | self._GLAllTextures.push(glTexture); 1108 | return glTexture; 1109 | } 1110 | 1111 | hooked_createBuffer(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/createBuffer 1112 | 1113 | let buffer = oFunc.apply(gl, args ? args : []); 1114 | self._GLBuffers.set(buffer, null); 1115 | 1116 | return buffer; 1117 | } 1118 | 1119 | hooked_deleteBuffer(self, gl, args, oFunc) { 1120 | if(!args[0]) 1121 | return; 1122 | self._GLState.delete(args[0]); 1123 | } 1124 | 1125 | hooked_bindBuffer(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bindBuffer 1126 | let target = args[0]; 1127 | let buffer = args[1]; 1128 | self._GLState.set(target, buffer); 1129 | } 1130 | 1131 | hooked_bufferData(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bufferData 1132 | let target = args[0]; 1133 | let data = null; 1134 | switch (args.length) { 1135 | case 2: 1136 | data = null; 1137 | break; 1138 | default: 1139 | let maybeData = args[1]; 1140 | // https://stackoverflow.com/questions/64650119/react-error-sharedarraybuffer-is-not-defined-in-firefox 1141 | let _ArrayBufferView = (new Uint16Array()).constructor.prototype.__proto__.constructor; 1142 | if (maybeData instanceof ArrayBuffer || 1143 | (crossOriginIsolated && maybeData instanceof SharedArrayBuffer) || 1144 | maybeData instanceof _ArrayBufferView) { 1145 | data = maybeData; 1146 | } else if(!isNaN(maybeData)) { 1147 | let newBufSize = maybeData; 1148 | data = new ArrayBuffer(newBufSize); 1149 | } 1150 | break; 1151 | } 1152 | 1153 | let buffer = self._GLState.get(target); 1154 | if (!buffer || !data) { 1155 | LogToParent("Called buffer data without specifying data: ", args); 1156 | return; 1157 | } 1158 | self._GLBuffers.set(buffer, data); 1159 | } 1160 | 1161 | hooked_bufferSubData(self, gl, args, oFunc) { 1162 | //LogToParent("bufferSubData: ", args); 1163 | let target = args[0]; 1164 | let offset = args[1]; 1165 | let srcData = args[2]; 1166 | if (!srcData) { 1167 | LogToParent("No data specified: ", args); 1168 | return; 1169 | } 1170 | if (!(srcData instanceof ArrayBuffer)){ 1171 | srcData = srcData.buffer; 1172 | } 1173 | 1174 | let newLen = offset+srcData.byteLength; 1175 | 1176 | let buffer = self._GLState.get(target); 1177 | if (!buffer) { 1178 | LogToParent("No such buffer: ", args); 1179 | return; 1180 | } 1181 | let dstData = self._GLBuffers.get(buffer); 1182 | if (dstData == null){ 1183 | LogToParent("Creating a new buffer of length ", newLen); 1184 | dstData = new ArrayBuffer(newLen, true); 1185 | self._GLBuffers.set(buffer, dstData); 1186 | } 1187 | if (!(dstData instanceof ArrayBuffer)){ 1188 | dstData = dstData.buffer; 1189 | //LogToParent("Turned dst into buffer ", dstData); 1190 | } 1191 | if (dstData.byteLength < newLen){ 1192 | LogToParent("Resizing buffer to length ", newLen); 1193 | dstData = resizeArrayBuffer(dstData, newLen); 1194 | self._GLBuffers.set(buffer, dstData); 1195 | } 1196 | let dstView = new Uint8Array(dstData); 1197 | let srcView = new Uint8Array(srcData); 1198 | dstView.set(srcView, offset); 1199 | } 1200 | 1201 | hooked_getExtension(self, gl, args, oFunc) { 1202 | 1203 | LogToParent("Registered possible extension: ", args[0]); 1204 | 1205 | } 1206 | 1207 | hooked_clear(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/clear 1208 | 1209 | if (self._CurrentModels.length > 0 && ++self._ClearCount >= _window.WEBGLRipperSettings.minimumClears && self._isCapturing) { 1210 | LogToParent("Performing rip..."); 1211 | self.HelperFunc_PerformRIP(self, gl); 1212 | } 1213 | 1214 | if (self._StartCapturing && self._IsEnabled) { // Fix Race conditions 1215 | LogToParent("Starting capture..."); 1216 | self._isCapturing = true; 1217 | self._StartCapturing = false; 1218 | } 1219 | 1220 | if (self._needsToReset) { 1221 | LogToParent("Resetting..."); 1222 | self.HelperFunc_ResetAll(self, gl); 1223 | self._needsToReset = false; 1224 | } 1225 | 1226 | self.HelperFunc_Flush(self, gl); 1227 | } 1228 | 1229 | hooked_enableVertexAttribArray(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/enableVertexAttribArray 1230 | let index = args[0]; 1231 | 1232 | self._GLCurrentAttribEnabled[index] = true; 1233 | } 1234 | 1235 | hooked_disableVertexAttribArray(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/disableVertexAttribArray 1236 | let index = args[0]; 1237 | 1238 | self._GLCurrentAttribEnabled[index] = false; 1239 | } 1240 | 1241 | hooked_vertexAttribIPointer(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/vertexAttribIPointer 1242 | let index = args[0]; 1243 | let size = args[1]; 1244 | let type = args[2]; 1245 | let stride = args[3]; 1246 | let offset = args[4]; 1247 | 1248 | self._GLCurrentAttrib[index] = { size: size, type: type, stride: stride, offset: offset, normalized: false }; 1249 | self._GLCurrentAttribEnabled[index] = true; 1250 | } 1251 | 1252 | hooked_vertexAttribPointer(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/vertexAttribPointer 1253 | let index = args[0]; 1254 | let size = args[1]; 1255 | let type = args[2]; 1256 | let normalized = args[3]; 1257 | let stride = args[4]; 1258 | let offset = args[5]; 1259 | 1260 | self._GLCurrentAttrib[index] = { size: size, type: type, stride: stride, offset: offset, normalized: normalized }; 1261 | self._GLCurrentAttribEnabled[index] = true; 1262 | } 1263 | 1264 | hooked_vertexAttribDivisor(self, gl, args, oFunc) { // https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/vertexAttribDivisor 1265 | 1266 | } 1267 | 1268 | hooked_vertexAttribDivisorANGLE(self, gl, args, oFunc) { 1269 | } 1270 | 1271 | readUniformData(gl, p) { 1272 | let uniformData = []; 1273 | let program = gl.getParameter(gl.CURRENT_PROGRAM); 1274 | if(!program) 1275 | return; 1276 | 1277 | let uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); 1278 | for (let j = 0; j < uniformCount; j++) { 1279 | let info = gl.getActiveUniform(program, j); 1280 | if(!info) 1281 | continue; 1282 | uniformData.push({ 1283 | index: j, 1284 | name: info.name, 1285 | size: info.size, 1286 | type: info.type 1287 | }); 1288 | } 1289 | return uniformData; 1290 | } 1291 | 1292 | readAttribData(gl, p) { 1293 | let attribData = []; 1294 | let program = gl.getParameter(gl.CURRENT_PROGRAM); 1295 | if(!program) 1296 | return; 1297 | let attribCount = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); 1298 | for (let j = 0; j < attribCount; j++) { 1299 | let info = gl.getActiveAttrib(program, j); 1300 | if(!info) 1301 | continue; 1302 | let loc = gl.getAttribLocation(program, info.name); 1303 | attribData.push({ 1304 | index: j, 1305 | loc: loc, 1306 | name: info.name, 1307 | size: info.size, 1308 | type: info.type 1309 | }); 1310 | } 1311 | return attribData; 1312 | } 1313 | 1314 | getBufferedIndices(self) { 1315 | let index = self._GLContext.getParameter(self._GLContext.ELEMENT_ARRAY_BUFFER_BINDING); 1316 | return index == null ? null : self.getBufferDataFromBuffer(self, index); 1317 | } 1318 | 1319 | getBufferDataFromBuffer(self, buffer) { 1320 | return self._GLBuffers.get(buffer); 1321 | } 1322 | 1323 | } 1324 | 1325 | // Just in case the site checks for function modifications through a string check. 1326 | let hideHook = function (fn, oFn) { fn.toString = oFn.toString.bind(oFn); } 1327 | 1328 | function RegisterGLFunction(_GL, _RipperWrapper, _Method) { 1329 | if (_GL[_Method] == undefined) return; 1330 | let hookFunc = _RipperWrapper[`hooked_${_Method}`]; 1331 | if (!hookFunc) { 1332 | LogToParent(`Wrapper didn't have the method '${_Method}' defined!`); 1333 | _RipperWrapper[`hooked_${_Method}`] = function (self, gl, args) { 1334 | // To prevent errors create a 'fake' method 1335 | LogToParent(`${_Method}: `, args); 1336 | }; 1337 | hookFunc = _RipperWrapper[`hooked_${_Method}`]; 1338 | if (!hookFunc) { 1339 | LogToParent(`Failed to hook: gl.${_Method}, aborting!`); 1340 | return; 1341 | } 1342 | } 1343 | let originalFunc = _GL[_Method]; 1344 | _GL[_Method] = function () { 1345 | let rv = hookFunc(_RipperWrapper, this, arguments, originalFunc); 1346 | if (rv) 1347 | return rv; 1348 | return originalFunc.apply(this, arguments); 1349 | }; 1350 | LogToParent(`Successfully hooked into gl.${_Method}`); 1351 | hideHook(_GL[_Method], originalFunc); 1352 | } 1353 | 1354 | /* Hook into context getter */ 1355 | LogToParent("Attempting to hook into canvas 'getContext' func!"); 1356 | let oGetContext = window.HTMLCanvasElement.prototype.getContext; 1357 | window.HTMLCanvasElement.prototype.getContext = function () { 1358 | 1359 | let contextNames = ["webgl", "webgl2", "experimental-webgl"]; 1360 | let isRequestingWebGL = contextNames.indexOf(arguments[0].toLowerCase()) != -1; 1361 | 1362 | if (!isRequestingWebGL) { 1363 | LogToParent("Got unsupported context: ", arguments[0]); 1364 | return oGetContext.apply(this, arguments); 1365 | } 1366 | 1367 | // Fix https://github.com/Rilshrink/WebGLRipper/issues/21 1368 | loadWebGLRipperSettings(); 1369 | 1370 | if (_window.WEBGLRipperSettings.isDoShaderCalc) { 1371 | arguments[0] = "webgl2"; // Force it so we can use transform feedback, should be safe to do since you can use all webgl functions with webgl2 1372 | } 1373 | 1374 | let gl = oGetContext.apply(this, arguments); 1375 | 1376 | if (!gl) 1377 | return gl; 1378 | 1379 | if (!gl._hooked) { 1380 | let glRipper = new WebGLRipperWrapper(gl); 1381 | glRipper._IsWebGL2 = (arguments[0] == 'webgl2'); 1382 | RegisterGLFunction(gl, glRipper, "clear"); 1383 | RegisterGLFunction(gl, glRipper, "bindBuffer"); 1384 | RegisterGLFunction(gl, glRipper, "bufferData"); 1385 | RegisterGLFunction(gl, glRipper, "bufferSubData"); 1386 | RegisterGLFunction(gl, glRipper, "createBuffer"); 1387 | RegisterGLFunction(gl, glRipper, "drawArrays"); 1388 | RegisterGLFunction(gl, glRipper, "drawArraysInstanced"); 1389 | RegisterGLFunction(gl, glRipper, "drawElements"); 1390 | RegisterGLFunction(gl, glRipper, "drawElementsInstanced"); 1391 | RegisterGLFunction(gl, glRipper, "drawBuffers"); 1392 | RegisterGLFunction(gl, glRipper, "linkProgram"); 1393 | RegisterGLFunction(gl, glRipper, "vertexAttribPointer"); 1394 | RegisterGLFunction(gl, glRipper, "vertexAttribIPointer"); 1395 | RegisterGLFunction(gl, glRipper, "enableVertexAttribArray"); 1396 | RegisterGLFunction(gl, glRipper, "disableVertexAttribArray"); 1397 | RegisterGLFunction(gl, glRipper, "vertexAttribDivisor"); 1398 | RegisterGLFunction(gl, glRipper, "vertexAttribDivisorANGLE"); 1399 | RegisterGLFunction(gl, glRipper, "shaderSource"); 1400 | RegisterGLFunction(gl, glRipper, "activeTexture"); 1401 | RegisterGLFunction(gl, glRipper, "bindTexture"); 1402 | RegisterGLFunction(gl, glRipper, "texImage2D"); 1403 | RegisterGLFunction(gl, glRipper, "createTexture"); 1404 | RegisterGLFunction(gl, glRipper, "getExtension"); 1405 | 1406 | //RegisterGLFunction(gl, glRipper, "drawArraysInstancedANGLE"); 1407 | //RegisterGLFunction(gl, glRipper, "drawElementsInstancedANGLE"); 1408 | //RegisterGLFunction(gl, glRipper, "bindBufferBase"); 1409 | //RegisterGLFunction(gl, glRipper, "bindBufferRange"); 1410 | 1411 | RegisterGLFunction(gl, glRipper, "deleteBuffer"); 1412 | /* 1413 | RegisterGLFunction(gl, glRipper, "flush"); 1414 | RegisterGLFunction(gl, glRipper, "finish"); 1415 | RegisterGLFunction(gl, glRipper, "readPixels"); 1416 | RegisterGLFunction(gl, glRipper, "bindBufferBase"); 1417 | RegisterGLFunction(gl, glRipper, "bindBufferRange"); 1418 | RegisterGLFunction(gl, glRipper, "clearColor"); 1419 | RegisterGLFunction(gl, glRipper, "clearDepth"); 1420 | */ 1421 | 1422 | _window.RIPPERS.push(glRipper); 1423 | gl._hooked = true; 1424 | LogToParent(`Injected into '${arguments[0]}' context!`, _window.RIPPERS); 1425 | } 1426 | 1427 | return gl; 1428 | }; /* Got from 'WebGL-Inspector' https://github.com/benvanik/WebGL-Inspector/blob/master/core/extensions/chrome/contentscript.js#L178 */ 1429 | 1430 | hideHook(_window.HTMLCanvasElement.prototype.getContext, oGetContext); --------------------------------------------------------------------------------