// ==UserScript==
// @name test_ece_crawl_tex
// @version 6.9
// @description downloadtextures
// @author yo-mama-duqistudio
// @include /^https?://(www\.)?sketchfab\.com/.*
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.0.2/jszip-utils.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js
// @run-at document-start
// @grant unsafeWindow
// @grant GM_download
// @license MIT
// @namespace https://greasyfork.org/users/1330796
// ==/UserScript==
var zip = new JSZip();
let folder = zip.folder("collection");
var button_dw = false;
var func_drawGeometry =
/(this\._stateCache\.drawGeometry\(this\._graphicContext,t\))/g;
var fund_drawArrays = /t\.drawArrays\(t\.TRIANGLES,0,6\)/g;
var func_renderInto1 = /A\.renderInto\(n,E,R,n,E,R/g;
var func_renderInto2 = /g\.renderInto=function\(e,i,r/g;
var func_getResourceImage = /getResourceImage:function\(e,t\){/g;
var func_test = /apply:function\(e\){var t=e instanceof r\.Geometry;/g;
let resourceReady = false;
let resourceTimer = null;
function getByteLength(str) {
let byteLength = 0;
for (let i = 0; i < str.length; i++) {
const charCode = str.charCodeAt(i);
if (charCode <= 0x7f) {
byteLength += 1;
} else if (charCode <= 0x7ff) {
byteLength += 2;
} else if (charCode <= 0xffff) {
byteLength += 3;
} else {
byteLength += 4;
}
}
return byteLength;
}
var addbtnfunc;
(function () {
"use strict";
var window = unsafeWindow;
console.log("[UserScript]init", window);
window.allmodel = [];
var saveimagecache2 = {};
var objects = {};
var saveimage_to_list = function (url, file_name) {
if (!saveimagecache2[url]) {
var mdl = {
name: file_name,
};
saveimagecache2[url] = mdl;
}
};
addbtnfunc = function () {
var p = document.evaluate(
"//div[@class='titlebar']",
document,
null,
9,
null
).singleNodeValue;
if (p && !button_dw) {
console.log("[UserScript]add btn dwnld");
var btn = document.createElement("a");
btn.setAttribute("class", "control");
btn.innerHTML =
"<pre style='font-family:impact;font-size:16px;text-shadow: 1px 1px black;color:#99ff66;'>下载贴图</pre>";
btn.addEventListener("click", () => {
alert("Zipping files...");
});
btn.addEventListener("click", dodownload, false);
p.appendChild(btn);
button_dw = true;
waitdownload();
} else {
console.log("[UserScript]try add btn later");
setTimeout(addbtnfunc, 3000);
}
};
var waitdownload = function () {
if (!resourceReady) {
setTimeout(waitdownload, 500);
} else {
setTimeout(dodownload, 200);
}
};
var dodownload = function () {
console.log("[UserScript]download");
var idx = 0;
window.allmodel.forEach(function (obj) {
var mdl = null;
});
PackAll();
};
var PackAll = function () {
for (var obj in objects) {
console.log("[UserScript]save file", obj);
folder.file(obj, objects[obj], { binary: true });
}
var file_name = document.getElementsByClassName('canvas')[0].getAttribute('data-modeluid');
folder
.generateAsync({ type: "blob" })
.then((content) => saveAs(content, file_name + ".zip"));
window.top.close();
};
var parseobj = function (obj) {
console.log("[UserScript]: obj", obj);
var list = [];
obj._primitives.forEach(function (p) {
if (p && p.indices) {
list.push({
mode: p.mode,
indices: p.indices._elements,
});
}
});
var attr = obj._attributes;
return {
vertex: attr.Vertex._elements,
normal: attr.Normal ? attr.Normal._elements : [],
uv: attr.TexCoord0
? attr.TexCoord0._elements
: attr.TexCoord1
? attr.TexCoord1._elements
: attr.TexCoord2
? attr.TexCoord2._elements
: attr.TexCoord2
? attr.TexCoord2._elements
: attr.TexCoord3
? attr.TexCoord3._elements
: attr.TexCoord4
? attr.TexCoord4._elements
: attr.TexCoord5
? attr.TexCoord5._elements
: attr.TexCoord6
? attr.TexCoord6._elements
: attr.TexCoord7
? attr.TexCoord7._elements
: attr.TexCoord8
? attr.TexCoord8._elements
: [],
primitives: list,
};
};
var dosavefile = function (mdl) {
var obj = mdl.obj;
//console.log("TEST");
//console.log(obj);
var str = "";
str += "mtllib " + mdl.name + ".mtl\n";
str += "o " + mdl.name + "\n";
for (var i = 0; i < obj.vertex.length; i += 3) {
str += "v ";
for (var j = 0; j < 3; ++j) {
str += obj.vertex[i + j] + " ";
}
str += "\n";
}
for (i = 0; i < obj.normal.length; i += 3) {
str += "vn ";
for (j = 0; j < 3; ++j) {
str += obj.normal[i + j] + " ";
}
str += "\n";
}
for (i = 0; i < obj.uv.length; i += 2) {
str += "vt ";
for (j = 0; j < 2; ++j) {
str += obj.uv[i + j] + " ";
}
str += "\n";
}
//str += 'usemtl ' + mdl.name + '\n';
str += "s on \n";
var vn = obj.normal.length != 0;
var vt = obj.uv.length != 0;
for (i = 0; i < obj.primitives.length; ++i) {
var primitive = obj.primitives[i];
if (primitive.mode == 4 || primitive.mode == 5) {
var strip = primitive.mode == 5;
for (j = 0; j + 2 < primitive.indices.length; !strip ? (j += 3) : j++) {
str += "f ";
var order = [0, 1, 2];
if (strip && j % 2 == 1) {
order = [0, 2, 1];
}
for (var k = 0; k < 3; ++k) {
var faceNum = primitive.indices[j + order[k]] + 1;
str += faceNum;
if (vn || vt) {
str += "/";
if (vt) {
str += faceNum;
}
if (vn) {
str += "/" + faceNum;
}
}
str += " ";
}
str += "\n";
}
} else {
console.log(
"[UserScript]dosavefile: unknown primitive mode",
primitive
);
}
}
str += "\n";
var objblob = new Blob([str], { type: "text/plain" });
objects[mdl.name + ".obj"] = objblob;
};
window.attachbody = function (obj) {
if (
obj._faked != true &&
((obj.stateset && obj.stateset._name) ||
obj._name ||
(obj._parents && obj._parents[0]._name))
) {
obj._faked = true;
if (obj._name == "composer layer" || obj._name == "Ground - Geometry")
return;
// 用计时器判断资源是否准备好。
if (resourceTimer) {
clearTimeout(resourceTimer);
console.log("Resource not ready.");
}
resourceTimer = setTimeout(() => {
resourceReady = true;
console.log("Resource is ready.");
}, 2000);
// 根据_instanceID去重复,不然会下载很多次,导致每次下载大小不一样。
let isDuplicate = window.allmodel.some(
(existingObj) => existingObj._instanceID === obj._instanceID
);
if (!isDuplicate) {
window.allmodel.push(obj);
}
console.log(obj);
}
//console.log(obj);
};
var ParseAllImageList = function (imagemodel) {};
window.hook_test = function (e, idx) {
console.log("hooked index: " + idx);
console.log(e);
};
window.drawhookcanvas = function (e, imagemodel) {
// if((e.width == 128 && e.height == 128) || (e.width == 32 && e.height == 32) || (e.width == 64 && e.height == 64))
// {
// console.log("texture missed");
// console.log(e);
// return e;
// }
if (imagemodel) {
var alpha = e.options.format;
var filename_image = imagemodel.attributes.name;
if (filename_image == "internal_ground_ao_texture.jpeg") return e;
var uid = imagemodel.attributes.uid;
var url_image = e.url;
var max_size = 0;
var obr = e;
//let img = imagemodel.attributes.images[imagemodel.attributes.images.length-2]l
imagemodel.attributes.images.forEach(function (img) {
var alpha_is_check = alpha == "A" ? img.options.format == alpha : true;
var d = img.width;
while (d % 2 == 0) {
d = d / 2;
}
if (img.size > max_size && alpha_is_check && d == 1) {
max_size = img.size;
url_image = img.url;
uid = img.uid;
obr = img;
}
});
if (!saveimagecache2[url_image]) {
if (alpha == "A") {
let ext = filename_image.split(".");
filename_image = ext[0] + "_alpha." + ext[1];
}
saveimage_to_list(url_image, filename_image);
} else {
//console.log(e);
}
return obr;
}
return e;
};
window.drawhookimg = function (gl, t) {
var url = t[5].currentSrc;
var width = t[5].width;
var height = t[5].height;
if (!saveimagecache2[url]) {
//console.log("rejected:"+url);
return;
} else {
//console.log("saved texture:"+url);
}
var data = new Uint8Array(width * height * 4);
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
var halfHeight = (height / 2) | 0;
var bytesPerRow = width * 4;
var temp = new Uint8Array(width * 4);
for (var y = 0; y < halfHeight; ++y) {
var topOffset = y * bytesPerRow;
var bottomOffset = (height - y - 1) * bytesPerRow;
temp.set(data.subarray(topOffset, topOffset + bytesPerRow));
data.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
data.set(temp, bottomOffset);
}
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var context = canvas.getContext("2d");
var imageData = context.createImageData(width, height);
imageData.data.set(data);
context.putImageData(imageData, 0, 0);
var re = /(?:\.([^.]+))?$/;
var ext = re.exec(saveimagecache2[url].name)[1];
var name = saveimagecache2[url].name + ".png";
if (ext == "png" || ext == "jpg" || ext == "jpeg") {
var ret = saveimagecache2[url].name.replace("." + ext, "");
name = ret + ".png";
}
console.log("saved texture to blob " + name);
canvas.toBlob(function (blob) {
objects[name] = blob;
}, "image/png");
};
})();
(() => {
"use strict";
const Event = class {
constructor(script, target) {
this.script = script;
this.target = target;
this._cancel = false;
this._replace = null;
this._stop = false;
}
preventDefault() {
this._cancel = true;
}
stopPropagation() {
this._stop = true;
}
replacePayload(payload) {
this._replace = payload;
}
};
let callbacks = [];
window.addBeforeScriptExecuteListener = (f) => {
if (typeof f !== "function") {
throw new Error("Event handler must be a function.");
}
callbacks.push(f);
};
window.removeBeforeScriptExecuteListener = (f) => {
let i = callbacks.length;
while (i--) {
if (callbacks[i] === f) {
callbacks.splice(i, 1);
}
}
};
const dispatch = (script, target) => {
if (script.tagName !== "SCRIPT") {
return;
}
const e = new Event(script, target);
if (typeof window.onbeforescriptexecute === "function") {
try {
window.onbeforescriptexecute(e);
} catch (err) {
console.error(err);
}
}
for (const func of callbacks) {
if (e._stop) {
break;
}
try {
func(e);
} catch (err) {
console.error(err);
}
}
if (e._cancel) {
script.textContent = "";
script.remove();
} else if (typeof e._replace === "string") {
script.textContent = e._replace;
}
};
const observer = new MutationObserver((mutations) => {
for (const m of mutations) {
for (const n of m.addedNodes) {
dispatch(n, m.target);
}
}
});
observer.observe(document, {
childList: true,
subtree: true,
});
})();
(() => {
"use strict";
window.onbeforescriptexecute = (e) => {
var links_as_arr = Array.from(e.target.childNodes);
links_as_arr.forEach(function (srimgc) {
if (srimgc instanceof HTMLScriptElement) {
if (
srimgc.src.indexOf("web/dist/") >= 0 ||
srimgc.src.indexOf("standaloneViewer") >= 0
) {
e.preventDefault();
e.stopPropagation();
var req = new XMLHttpRequest();
req.open("GET", srimgc.src, false);
req.send("");
var jstext = req.responseText;
var ret = func_renderInto1.exec(jstext);
if (ret) {
let index = ret.index + ret[0].length;
let head = jstext.slice(0, index);
let tail = jstext.slice(index);
jstext = head + ",i" + tail;
console.log(
"[UserScript] Injection: patch_0 injected successful " +
srimgc.src
);
}
ret = func_renderInto2.exec(jstext);
if (ret) {
let index = ret.index + ret[0].length;
let head = jstext.slice(0, index);
let tail = jstext.slice(index);
jstext = head + ",iin,iir,iid,image_data" + tail;
console.log(
"[UserScript] Injection: patch_1 injected successful " +
srimgc.src
);
}
ret = fund_drawArrays.exec(jstext);
if (ret) {
let index = ret.index + ret[0].length;
let head = jstext.slice(0, index);
let tail = jstext.slice(index);
jstext = head + ",window.drawhookimg(t,image_data)" + tail;
console.log(
"[UserScript] Injection: patch_2 injected successful " +
srimgc.src
);
}
ret = func_getResourceImage.exec(jstext);
if (ret) {
let index = ret.index + ret[0].length;
let head = jstext.slice(0, index);
let tail = jstext.slice(index);
jstext =
head + "e = window.drawhookcanvas(e,this._imageModel);" + tail;
console.log(
"[UserScript] Injection: patch_3 injected successful " +
srimgc.src
);
}
ret = func_drawGeometry.exec(jstext);
if (ret) {
let index1 = ret.index + ret[1].length;
let head1 = jstext.slice(0, index1);
let tail1 = jstext.slice(index1);
jstext = head1 + ";window.attachbody(t);" + tail1;
console.log(
"[UserScript] Injection: patch_4 injected successful" + srimgc.src
);
setTimeout(addbtnfunc, 3000);
}
var obj = document.createElement("script");
obj.type = "text/javascript";
obj.text = jstext;
document.getElementsByTagName("head")[0].appendChild(obj);
}
}
});
};
})();