test_ece_crawl_tex

downloadtextures

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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);
        }
      }
    });
  };
})();