test_ece_crawl_tex

downloadtextures

  1. // ==UserScript==
  2. // @name test_ece_crawl_tex
  3. // @version 6.9
  4. // @description downloadtextures
  5. // @author yo-mama-duqistudio
  6. // @include /^https?://(www\.)?sketchfab\.com/.*
  7. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js
  8. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.0.2/jszip-utils.min.js
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js
  10. // @run-at document-start
  11. // @grant unsafeWindow
  12. // @grant GM_download
  13. // @license MIT
  14. // @namespace https://greasyfork.org/users/1330796
  15. // ==/UserScript==
  16.  
  17. var zip = new JSZip();
  18. let folder = zip.folder("collection");
  19.  
  20. var button_dw = false;
  21. var func_drawGeometry =
  22. /(this\._stateCache\.drawGeometry\(this\._graphicContext,t\))/g;
  23. var fund_drawArrays = /t\.drawArrays\(t\.TRIANGLES,0,6\)/g;
  24. var func_renderInto1 = /A\.renderInto\(n,E,R,n,E,R/g;
  25. var func_renderInto2 = /g\.renderInto=function\(e,i,r/g;
  26. var func_getResourceImage = /getResourceImage:function\(e,t\){/g;
  27.  
  28. var func_test = /apply:function\(e\){var t=e instanceof r\.Geometry;/g;
  29. let resourceReady = false;
  30. let resourceTimer = null;
  31. function getByteLength(str) {
  32. let byteLength = 0;
  33. for (let i = 0; i < str.length; i++) {
  34. const charCode = str.charCodeAt(i);
  35. if (charCode <= 0x7f) {
  36. byteLength += 1;
  37. } else if (charCode <= 0x7ff) {
  38. byteLength += 2;
  39. } else if (charCode <= 0xffff) {
  40. byteLength += 3;
  41. } else {
  42. byteLength += 4;
  43. }
  44. }
  45. return byteLength;
  46. }
  47.  
  48. var addbtnfunc;
  49.  
  50. (function () {
  51. "use strict";
  52. var window = unsafeWindow;
  53. console.log("[UserScript]init", window);
  54.  
  55. window.allmodel = [];
  56. var saveimagecache2 = {};
  57. var objects = {};
  58.  
  59. var saveimage_to_list = function (url, file_name) {
  60. if (!saveimagecache2[url]) {
  61. var mdl = {
  62. name: file_name,
  63. };
  64. saveimagecache2[url] = mdl;
  65. }
  66. };
  67.  
  68. addbtnfunc = function () {
  69. var p = document.evaluate(
  70. "//div[@class='titlebar']",
  71. document,
  72. null,
  73. 9,
  74. null
  75. ).singleNodeValue;
  76. if (p && !button_dw) {
  77. console.log("[UserScript]add btn dwnld");
  78. var btn = document.createElement("a");
  79. btn.setAttribute("class", "control");
  80. btn.innerHTML =
  81. "<pre style='font-family:impact;font-size:16px;text-shadow: 1px 1px black;color:#99ff66;'>下载贴图</pre>";
  82. btn.addEventListener("click", () => {
  83. alert("Zipping files...");
  84. });
  85. btn.addEventListener("click", dodownload, false);
  86. p.appendChild(btn);
  87. button_dw = true;
  88. waitdownload();
  89. } else {
  90. console.log("[UserScript]try add btn later");
  91. setTimeout(addbtnfunc, 3000);
  92. }
  93. };
  94.  
  95. var waitdownload = function () {
  96. if (!resourceReady) {
  97. setTimeout(waitdownload, 500);
  98. } else {
  99. setTimeout(dodownload, 200);
  100. }
  101. };
  102.  
  103. var dodownload = function () {
  104. console.log("[UserScript]download");
  105. var idx = 0;
  106. window.allmodel.forEach(function (obj) {
  107. var mdl = null;
  108. });
  109. PackAll();
  110. };
  111.  
  112. var PackAll = function () {
  113. for (var obj in objects) {
  114. console.log("[UserScript]save file", obj);
  115. folder.file(obj, objects[obj], { binary: true });
  116. }
  117.  
  118. var file_name = document.getElementsByClassName('canvas')[0].getAttribute('data-modeluid');
  119. folder
  120. .generateAsync({ type: "blob" })
  121. .then((content) => saveAs(content, file_name + ".zip"));
  122.  
  123. window.top.close();
  124. };
  125.  
  126. var parseobj = function (obj) {
  127. console.log("[UserScript]: obj", obj);
  128. var list = [];
  129. obj._primitives.forEach(function (p) {
  130. if (p && p.indices) {
  131. list.push({
  132. mode: p.mode,
  133. indices: p.indices._elements,
  134. });
  135. }
  136. });
  137.  
  138. var attr = obj._attributes;
  139. return {
  140. vertex: attr.Vertex._elements,
  141. normal: attr.Normal ? attr.Normal._elements : [],
  142. uv: attr.TexCoord0
  143. ? attr.TexCoord0._elements
  144. : attr.TexCoord1
  145. ? attr.TexCoord1._elements
  146. : attr.TexCoord2
  147. ? attr.TexCoord2._elements
  148. : attr.TexCoord2
  149. ? attr.TexCoord2._elements
  150. : attr.TexCoord3
  151. ? attr.TexCoord3._elements
  152. : attr.TexCoord4
  153. ? attr.TexCoord4._elements
  154. : attr.TexCoord5
  155. ? attr.TexCoord5._elements
  156. : attr.TexCoord6
  157. ? attr.TexCoord6._elements
  158. : attr.TexCoord7
  159. ? attr.TexCoord7._elements
  160. : attr.TexCoord8
  161. ? attr.TexCoord8._elements
  162. : [],
  163. primitives: list,
  164. };
  165. };
  166.  
  167. var dosavefile = function (mdl) {
  168. var obj = mdl.obj;
  169.  
  170. //console.log("TEST");
  171. //console.log(obj);
  172.  
  173. var str = "";
  174. str += "mtllib " + mdl.name + ".mtl\n";
  175. str += "o " + mdl.name + "\n";
  176. for (var i = 0; i < obj.vertex.length; i += 3) {
  177. str += "v ";
  178. for (var j = 0; j < 3; ++j) {
  179. str += obj.vertex[i + j] + " ";
  180. }
  181. str += "\n";
  182. }
  183. for (i = 0; i < obj.normal.length; i += 3) {
  184. str += "vn ";
  185. for (j = 0; j < 3; ++j) {
  186. str += obj.normal[i + j] + " ";
  187. }
  188. str += "\n";
  189. }
  190.  
  191. for (i = 0; i < obj.uv.length; i += 2) {
  192. str += "vt ";
  193. for (j = 0; j < 2; ++j) {
  194. str += obj.uv[i + j] + " ";
  195. }
  196. str += "\n";
  197. }
  198. //str += 'usemtl ' + mdl.name + '\n';
  199. str += "s on \n";
  200.  
  201. var vn = obj.normal.length != 0;
  202. var vt = obj.uv.length != 0;
  203.  
  204. for (i = 0; i < obj.primitives.length; ++i) {
  205. var primitive = obj.primitives[i];
  206. if (primitive.mode == 4 || primitive.mode == 5) {
  207. var strip = primitive.mode == 5;
  208. for (j = 0; j + 2 < primitive.indices.length; !strip ? (j += 3) : j++) {
  209. str += "f ";
  210. var order = [0, 1, 2];
  211. if (strip && j % 2 == 1) {
  212. order = [0, 2, 1];
  213. }
  214. for (var k = 0; k < 3; ++k) {
  215. var faceNum = primitive.indices[j + order[k]] + 1;
  216. str += faceNum;
  217. if (vn || vt) {
  218. str += "/";
  219. if (vt) {
  220. str += faceNum;
  221. }
  222. if (vn) {
  223. str += "/" + faceNum;
  224. }
  225. }
  226. str += " ";
  227. }
  228. str += "\n";
  229. }
  230. } else {
  231. console.log(
  232. "[UserScript]dosavefile: unknown primitive mode",
  233. primitive
  234. );
  235. }
  236. }
  237.  
  238. str += "\n";
  239.  
  240. var objblob = new Blob([str], { type: "text/plain" });
  241.  
  242. objects[mdl.name + ".obj"] = objblob;
  243. };
  244.  
  245. window.attachbody = function (obj) {
  246. if (
  247. obj._faked != true &&
  248. ((obj.stateset && obj.stateset._name) ||
  249. obj._name ||
  250. (obj._parents && obj._parents[0]._name))
  251. ) {
  252. obj._faked = true;
  253. if (obj._name == "composer layer" || obj._name == "Ground - Geometry")
  254. return;
  255.  
  256. // 用计时器判断资源是否准备好。
  257. if (resourceTimer) {
  258. clearTimeout(resourceTimer);
  259. console.log("Resource not ready.");
  260. }
  261. resourceTimer = setTimeout(() => {
  262. resourceReady = true;
  263. console.log("Resource is ready.");
  264. }, 2000);
  265. // 根据_instanceID去重复,不然会下载很多次,导致每次下载大小不一样。
  266. let isDuplicate = window.allmodel.some(
  267. (existingObj) => existingObj._instanceID === obj._instanceID
  268. );
  269. if (!isDuplicate) {
  270. window.allmodel.push(obj);
  271. }
  272. console.log(obj);
  273. }
  274. //console.log(obj);
  275. };
  276.  
  277. var ParseAllImageList = function (imagemodel) {};
  278.  
  279. window.hook_test = function (e, idx) {
  280. console.log("hooked index: " + idx);
  281. console.log(e);
  282. };
  283. window.drawhookcanvas = function (e, imagemodel) {
  284. // if((e.width == 128 && e.height == 128) || (e.width == 32 && e.height == 32) || (e.width == 64 && e.height == 64))
  285. // {
  286. // console.log("texture missed");
  287. // console.log(e);
  288. // return e;
  289. // }
  290.  
  291. if (imagemodel) {
  292. var alpha = e.options.format;
  293. var filename_image = imagemodel.attributes.name;
  294. if (filename_image == "internal_ground_ao_texture.jpeg") return e;
  295. var uid = imagemodel.attributes.uid;
  296. var url_image = e.url;
  297. var max_size = 0;
  298. var obr = e;
  299.  
  300. //let img = imagemodel.attributes.images[imagemodel.attributes.images.length-2]l
  301.  
  302. imagemodel.attributes.images.forEach(function (img) {
  303. var alpha_is_check = alpha == "A" ? img.options.format == alpha : true;
  304.  
  305. var d = img.width;
  306. while (d % 2 == 0) {
  307. d = d / 2;
  308. }
  309.  
  310. if (img.size > max_size && alpha_is_check && d == 1) {
  311. max_size = img.size;
  312. url_image = img.url;
  313. uid = img.uid;
  314. obr = img;
  315. }
  316. });
  317. if (!saveimagecache2[url_image]) {
  318. if (alpha == "A") {
  319. let ext = filename_image.split(".");
  320.  
  321. filename_image = ext[0] + "_alpha." + ext[1];
  322. }
  323.  
  324. saveimage_to_list(url_image, filename_image);
  325. } else {
  326. //console.log(e);
  327. }
  328.  
  329. return obr;
  330. }
  331. return e;
  332. };
  333.  
  334. window.drawhookimg = function (gl, t) {
  335. var url = t[5].currentSrc;
  336. var width = t[5].width;
  337. var height = t[5].height;
  338.  
  339. if (!saveimagecache2[url]) {
  340. //console.log("rejected:"+url);
  341. return;
  342. } else {
  343. //console.log("saved texture:"+url);
  344. }
  345.  
  346. var data = new Uint8Array(width * height * 4);
  347. gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
  348.  
  349. var halfHeight = (height / 2) | 0;
  350. var bytesPerRow = width * 4;
  351.  
  352. var temp = new Uint8Array(width * 4);
  353. for (var y = 0; y < halfHeight; ++y) {
  354. var topOffset = y * bytesPerRow;
  355. var bottomOffset = (height - y - 1) * bytesPerRow;
  356.  
  357. temp.set(data.subarray(topOffset, topOffset + bytesPerRow));
  358.  
  359. data.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
  360.  
  361. data.set(temp, bottomOffset);
  362. }
  363.  
  364. var canvas = document.createElement("canvas");
  365. canvas.width = width;
  366. canvas.height = height;
  367. var context = canvas.getContext("2d");
  368.  
  369. var imageData = context.createImageData(width, height);
  370. imageData.data.set(data);
  371. context.putImageData(imageData, 0, 0);
  372.  
  373. var re = /(?:\.([^.]+))?$/;
  374. var ext = re.exec(saveimagecache2[url].name)[1];
  375. var name = saveimagecache2[url].name + ".png";
  376.  
  377. if (ext == "png" || ext == "jpg" || ext == "jpeg") {
  378. var ret = saveimagecache2[url].name.replace("." + ext, "");
  379. name = ret + ".png";
  380. }
  381. console.log("saved texture to blob " + name);
  382. canvas.toBlob(function (blob) {
  383. objects[name] = blob;
  384. }, "image/png");
  385. };
  386. })();
  387.  
  388. (() => {
  389. "use strict";
  390. const Event = class {
  391. constructor(script, target) {
  392. this.script = script;
  393. this.target = target;
  394.  
  395. this._cancel = false;
  396. this._replace = null;
  397. this._stop = false;
  398. }
  399.  
  400. preventDefault() {
  401. this._cancel = true;
  402. }
  403. stopPropagation() {
  404. this._stop = true;
  405. }
  406. replacePayload(payload) {
  407. this._replace = payload;
  408. }
  409. };
  410.  
  411. let callbacks = [];
  412. window.addBeforeScriptExecuteListener = (f) => {
  413. if (typeof f !== "function") {
  414. throw new Error("Event handler must be a function.");
  415. }
  416. callbacks.push(f);
  417. };
  418. window.removeBeforeScriptExecuteListener = (f) => {
  419. let i = callbacks.length;
  420. while (i--) {
  421. if (callbacks[i] === f) {
  422. callbacks.splice(i, 1);
  423. }
  424. }
  425. };
  426.  
  427. const dispatch = (script, target) => {
  428. if (script.tagName !== "SCRIPT") {
  429. return;
  430. }
  431.  
  432. const e = new Event(script, target);
  433.  
  434. if (typeof window.onbeforescriptexecute === "function") {
  435. try {
  436. window.onbeforescriptexecute(e);
  437. } catch (err) {
  438. console.error(err);
  439. }
  440. }
  441.  
  442. for (const func of callbacks) {
  443. if (e._stop) {
  444. break;
  445. }
  446. try {
  447. func(e);
  448. } catch (err) {
  449. console.error(err);
  450. }
  451. }
  452.  
  453. if (e._cancel) {
  454. script.textContent = "";
  455. script.remove();
  456. } else if (typeof e._replace === "string") {
  457. script.textContent = e._replace;
  458. }
  459. };
  460. const observer = new MutationObserver((mutations) => {
  461. for (const m of mutations) {
  462. for (const n of m.addedNodes) {
  463. dispatch(n, m.target);
  464. }
  465. }
  466. });
  467. observer.observe(document, {
  468. childList: true,
  469. subtree: true,
  470. });
  471. })();
  472.  
  473. (() => {
  474. "use strict";
  475.  
  476. window.onbeforescriptexecute = (e) => {
  477. var links_as_arr = Array.from(e.target.childNodes);
  478.  
  479. links_as_arr.forEach(function (srimgc) {
  480. if (srimgc instanceof HTMLScriptElement) {
  481. if (
  482. srimgc.src.indexOf("web/dist/") >= 0 ||
  483. srimgc.src.indexOf("standaloneViewer") >= 0
  484. ) {
  485. e.preventDefault();
  486. e.stopPropagation();
  487. var req = new XMLHttpRequest();
  488. req.open("GET", srimgc.src, false);
  489. req.send("");
  490. var jstext = req.responseText;
  491. var ret = func_renderInto1.exec(jstext);
  492.  
  493. if (ret) {
  494. let index = ret.index + ret[0].length;
  495. let head = jstext.slice(0, index);
  496. let tail = jstext.slice(index);
  497. jstext = head + ",i" + tail;
  498. console.log(
  499. "[UserScript] Injection: patch_0 injected successful " +
  500. srimgc.src
  501. );
  502. }
  503.  
  504. ret = func_renderInto2.exec(jstext);
  505.  
  506. if (ret) {
  507. let index = ret.index + ret[0].length;
  508. let head = jstext.slice(0, index);
  509. let tail = jstext.slice(index);
  510. jstext = head + ",iin,iir,iid,image_data" + tail;
  511. console.log(
  512. "[UserScript] Injection: patch_1 injected successful " +
  513. srimgc.src
  514. );
  515. }
  516.  
  517. ret = fund_drawArrays.exec(jstext);
  518.  
  519. if (ret) {
  520. let index = ret.index + ret[0].length;
  521. let head = jstext.slice(0, index);
  522. let tail = jstext.slice(index);
  523. jstext = head + ",window.drawhookimg(t,image_data)" + tail;
  524. console.log(
  525. "[UserScript] Injection: patch_2 injected successful " +
  526. srimgc.src
  527. );
  528. }
  529.  
  530. ret = func_getResourceImage.exec(jstext);
  531.  
  532. if (ret) {
  533. let index = ret.index + ret[0].length;
  534. let head = jstext.slice(0, index);
  535. let tail = jstext.slice(index);
  536. jstext =
  537. head + "e = window.drawhookcanvas(e,this._imageModel);" + tail;
  538. console.log(
  539. "[UserScript] Injection: patch_3 injected successful " +
  540. srimgc.src
  541. );
  542. }
  543.  
  544. ret = func_drawGeometry.exec(jstext);
  545.  
  546. if (ret) {
  547. let index1 = ret.index + ret[1].length;
  548. let head1 = jstext.slice(0, index1);
  549. let tail1 = jstext.slice(index1);
  550. jstext = head1 + ";window.attachbody(t);" + tail1;
  551. console.log(
  552. "[UserScript] Injection: patch_4 injected successful" + srimgc.src
  553. );
  554. setTimeout(addbtnfunc, 3000);
  555. }
  556.  
  557. var obj = document.createElement("script");
  558. obj.type = "text/javascript";
  559. obj.text = jstext;
  560. document.getElementsByTagName("head")[0].appendChild(obj);
  561. }
  562. }
  563. });
  564. };
  565. })();