test_ece_crawl_tex

downloadtextures

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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);
        }
      }
    });
  };
})();