FANDOM


To use this script, you're going to need to:

Afterwards the script will be usable to view various things in the Character Viewer.

Source

/*
    ALog3D Viewer UI
    ALog3D vertical movement, zoom, view NPC's, players and models
    Styles added to pages that will persist.
    Copyright (C) 2015  RuneApps
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
 
// ==UserScript==
// @name         ALog3D Viewer UI
// @version      1.0.8
// @description  Modify ALog3D to move vertically, zoom, view NPC's, players and models
// @author       Skillbert
// @contributor  Cook Me Plox
// @contributor  Ryan M (RyanMakise)
// @homepage     http://runeapps.org/
// @updateURL    http://runescape.wikia.com/wiki/MediaWiki:AL3DUI.js?action=raw
// @match        http://services.runescape.com/m=adventurers-log/*/character
// @match        http://services.runescape.com/m=adventurers-log/*/character?searchName=*
// @match        http://services.runescape.com/m=adventurers-log/*/character?searchname=*
// @match        http://services.runescape.com/m=adventurers-log/character?searchName=*
// @match        http://services.runescape.com/m=adventurers-log/character?searchname=*
// @run-at       document-end
// @grant        none
// ==/UserScript==
 
/* jshint bitwise: false, funcscope:true, expr:true, -W083, -W089 */
/* globals imgcapt:true, Module:true, curbin:true, binindex:true, chars:true,
   loadedStr:true, byteblock:true, setframecam:true, width:true, height:true,
   GIFEncoder:true, Whammy:true, GIF:true, scr:true, $ */
 
var GIF, GIFEncoder, Whammy, binindex, byteblock, chars, curbin, height, imgcapt, loadedStr, scr, setframecam, width;
 
function ab() {
  'use strict';
 
  if (window.imgcapt) {
    imgcapt.firstLoad = false;
  } else {
    imgcapt = {
      firstLoad: true
    };
  }
 
  //Definitions
  chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789*-";
  curbin = "000000001111111110111110000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
 
  //General
  imgcapt.currentPlayer = "";
  imgcapt.currentStr = "";
 
  //Player profile
  imgcapt.strlist = [];
  imgcapt.captured = [];
  imgcapt.captCnv = false;
  imgcapt.lastLoad = 0;
  imgcapt.skipTime = 15000;
  imgcapt.settleTime = 2000;
  imgcapt.unloadTime = 3000;
  imgcapt.checkTime = 500;
 
  //Upload
  imgcapt.uploadlog = true;
  imgcapt.uploading = false;
  imgcapt.uploadview = "http://runeapps.org/apps/pprofile/avatarimgs/ava-$0.png";
  imgcapt.uploadpage = "http://runeapps.org/apps/pprofile/uploadavatar.php";
  imgcapt.uploadpass = "";
  imgcapt.uploadcrop = {
    x: 325,
    y: 50,
    w: 350,
    h: 600
  };
  imgcapt.uploadcam = {
    angle: [0.052, 0.24, 0],
    pos: [0, 0, 560],
    fov: 0.17
  };
 
  //Stack Debugging (deprecated)
  imgcapt.stacks = {};
  imgcapt.stackcapt = false;
 
  //Camera
  imgcapt.overrideCam = true;
  imgcapt.overridecenter = true;
  imgcapt.bobblehead = false;
  imgcapt.verticleMatrix = [];
  imgcapt.cam = {
    angle: [0.052, 0.24, 0],
    pos: [0, 0, 560],
    fov: 0.17
  };
 
  //Frame animation
  imgcapt.manualpaint = false;
  imgcapt.paintfunc = false;
  imgcapt.waitpaintlist = [];
 
  //String editor
  imgcapt.stredit = {};
 
  //RuneApps Libraries
  imgcapt.liborigin = "http://runeapps.org";
  imgcapt.libs = ["/apps/avatarcapt/lib/gif/LZWEncoder.js", "/apps/avatarcapt/lib/gif/NeuQuant.js", "/apps/avatarcapt/lib/gif/GIFEncoder.js", "/apps/avatarcapt/lib/webm/whammy.js", "/apps/avatarcapt/lib/gif2/gif.js", "/apps/avatarcapt/items.js"];
  imgcapt.libstrings = {
    gif2workerstr: "/apps/avatarcapt/lib/gif2/gif_transfix.worker.js"
  };
 
  //GIF
  imgcapt.anims = {};
  imgcapt.anims.rotate = {
    cam: {
      angle: [0.052, 0.24, 0],
      pos: [0, 0, 560],
      fov: 0.17
    },
    anim: {
      angle: [0, Math.PI * 2, 0],
      pos: [0, 0, 0],
      fov: 0
    },
    startisend: true,
    crop: {
      x: 325,
      y: 50,
      w: 350,
      h: 600
    },
    recordtime: 5400,
    realtime: true
  };
  imgcapt.anims.test = {
    cam: {
      angle: [0, 0, 0],
      pos: [0, 0, 550],
      fov: 0.6
    },
    anim: {
      angle: [0, Math.PI * 2, 0],
      pos: [0, 0, 0],
      fov: 0
    },
    recordtime: 1000,
    realtime: true,
    startisend: true
  };
  imgcapt.anims.idle = {
    cam: {
      angle: [0.052, 0.24, 0],
      pos: [0, 0, 560],
      fov: 0.17
    },
    anim: {
      angle: [0, 0, 0],
      pos: [0, 0, 0],
      fov: 0
    },
    recordtime: 1800,
    recordinterval: 50,
    realtime: true,
    crop: {
      x: 75,
      y: 50,
      w: 350,
      h: 600
    }
  };
  imgcapt.anims.king = {
    cam: {
      angle: [-0.33, 0.38, 0],
      pos: [0, 0, 660],
      fov: 0.96
    },
    anim: {
      angle: [0, Math.PI * 2, 0],
      pos: [0, 0, 0],
      fov: 0
    },
    recordtime: 13200,
    realtime: true,
    startisend: true,
    crop: {
      x: 360,
      y: 200,
      w: 410,
      h: 500
    },
    framesize: [1000, 700]
  };
  imgcapt.makeGif = function(template, format) {
    var cnv, ctx, trans, encoder;
    var addframe, finish, log;
    var framenumber, animended;
    var starttime, intervaltimer, lastframetime, start, lastframeduration;
    if (!format) {
      format = "gif";
    }
    if (!template.crop) {
      template.crop = {
        x: 0,
        y: 0,
        w: imgcapt.el.width,
        h: imgcapt.el.height
      };
    }
    starttime = Date.now();
    trans = 0xFFFFFF;
    animended = false;
    framenumber = 0;
    cnv = document.createElement("canvas");
    ctx = cnv.getContext("2d");
    log = function(t) {
      console.log(new Date()
        .toLocaleTimeString(), t);
    };
    setframecam = function() {
      var camprogress, totalsteps;
      if (!template.realtime) {
        totalsteps = template.frames;
        if (!template.frames || !totalsteps) {
          camprogress = 0;
        } else {
          camprogress = framenumber / totalsteps;
        }
      } else {
        camprogress = (Date.now() - starttime) / template.recordtime;
      }
      imgcapt.cam.angle[0] = template.cam.angle[0] + template.anim.angle[0] * camprogress;
      imgcapt.cam.angle[1] = template.cam.angle[1] + template.anim.angle[1] * camprogress;
      imgcapt.cam.angle[2] = template.cam.angle[2] + template.anim.angle[2] * camprogress;
      imgcapt.cam.pos[0] = template.cam.pos[0] + template.anim.pos[0] * camprogress;
      imgcapt.cam.pos[1] = template.cam.pos[1] + template.anim.pos[1] * camprogress;
      imgcapt.cam.pos[2] = template.cam.pos[2] + template.anim.pos[2] * camprogress;
      imgcapt.cam.fov = template.cam.fov + template.anim.fov * camprogress;
      imgcapt.setCam();
    };
    finish = function() {
      var url;
      log("finished - " + (Date.now() - starttime) + "ms");
      animended = true;
      if (format == "gif") {
        encoder.finish();
        url = "data:image/gif;base64," + btoa(encoder.stream()
          .getData());
      }
      if (format == "webm") {
        url = (window.webkitURL || window.URL)
          .createObjectURL(encoder.compile());
      }
      if (format == "gif2") {
        encoder.on('finished', function(blob) {
          window.open(URL.createObjectURL(blob));
        });
        encoder.render();
      }
      if (url) {
        window.open(url);
      }
    };
    addframe = function() {
      var islastframe, frametime;
      if (animended) {
        return;
      }
      //check if this is last frame
      islastframe = false;
      if (template.frames) {
        if (framenumber >= template.frames - 1) {
          islastframe = true;
        }
      } else if (template.recordtime) {
        if (Date.now() + lastframeduration * (template.startisend ? 0.5 : -0.5) > starttime + template.recordtime) {
          islastframe = true;
        }
      }
      //make sure the image is drawn if on timer
      if (template.recordinterval) {
        if (islastframe) {
          clearInterval(intervaltimer);
        }
        imgcapt.trypaint();
      }
      //get the frame duration
      if (template.realtime) {
        frametime = Date.now() - lastframetime;
      } else {
        frametime = template.interval;
      }
      //copy frame
      cnv.width = template.crop.w;
      cnv.height = template.crop.h;
      ctx.fillStyle = "#" + numToHex(trans, 6);
      ctx.fillRect(0, 0, width, height);
      ctx.drawImage(imgcapt.el, -template.crop.x, -template.crop.y);
      //send to one of the encoders
      if (format == "gif") {
        encoder.addFrame(ctx);
      }
      if (format == "webm") {
        encoder.add(ctx);
      }
      if (format == "gif2") {
        encoder.addFrame(ctx, {
          copy: true,
          delay: frametime * 3
        });
      }
      //finish up if done
      log("frame finished: " + framenumber + ", frametime: " + (Date.now() - lastframetime));
      lastframeduration = Date.now() - lastframetime;
      lastframetime = Date.now();
      if (islastframe) {
        finish();
        return;
      }
      //start next frame
      framenumber++;
      setframecam();
      if (!template.recordinterval) {
        imgcapt.waitpaint(addframe);
      }
    };
    start = function() {
      lastframeduration = 0;
      lastframetime = Date.now();
      setframecam();
      if (template.recordinterval) {
        intervaltimer = setInterval(addframe, template.recordinterval);
      } else {
        addframe();
      }
    };
    width = imgcapt.el.width;
    height = imgcapt.el.height;
    if (format == "gif") {
      encoder = new GIFEncoder();
      encoder.start();
      encoder.setSize(width, height);
      encoder.setDelay(template.interval);
      encoder.setRepeat(0);
    }
    if (format == "webm") {
      encoder = new Whammy.Video(1000 / template.interval);
    }
    if (format == "gif2") {
      encoder = new GIF({
        workers: 8,
        quality: 10,
        workerScript: imgcapt.getUrlForWorker(imgcapt.gif2workerstr),
        width: template.crop.w,
        height: template.crop.h,
        transparent: trans
      });
    }
    start();
  };
 
  //Hex table
  imgcapt.drawstr = function(str) {
    var a, b, c, r, parts, block, blocks, hex, root;
 
    //Byteblock class
    byteblock = function() {
        this.hex = "";
        this.read = function(l) {
          return parseInt(this.hex.slice(0, l * 2), 16);
        };
        this.readHex = function(l) {
          return this.hex.slice(0, l * 2);
        };
        this.setint = function(n, l) {
          this.hex = numToHex(n, l * 2);
        };
      };
 
    //Parsers
    var parsegender = function(obj) {
      obj.l = 1;
      obj.control = {
        t: "dropdown",
        v: obj.read(1),
        opts: [
          [0, "Male"],
          [1, "Female"]
        ],
        func: function(obj, v) {
          obj.setint(v, 1);
        }
      };
      obj.reset = function(obj) {
        obj.setint(0, 1);
      };
    };
    var parseslot = function(obj) {
      var id;
      if (obj.read(1) !== 0) { //is equiped
        id = obj.read(2) - 0x4000;
        obj.l = 2;
        obj.control = {
          t: "int",
          v: id,
          func: function(obj, v) {
            obj.l = 2;
            obj.setint(+v + 0x4000, 2);
          }
        };
        if (id >= 0) {
         // obj.control.title = items[id] && items[id].n;
        }
        obj.reset = function(obj) {
          obj.l = 1;
          obj.setint(0, 1);
        };
      } else {
        obj.l = 1;
        obj.control = {
          t: "button",
          v: "Equip",
          func: function(obj) {
            obj.l = 2;
            obj.setint(0x4000, 2);
          }
        };
      }
    };
    var setrecol = function(obj, index) {
      var a, b, len;
      obj.l = 2;
      a = numToBin(parseInt(obj.read(2)), 16);
      len = a.length - 1;
      for (b = len; b >= 0; b--) {
        if (a[b] == "0") {
          continue;
        }
        parts.splice(++index, 0, {
          n: "c." + slotnames[len - b],
          parse: parseitemdata
        });
      }
    };
    var parseitemdata = function(obj, index) {
      var flags;
      obj.l = 1;
      flags = obj.read(1);
      if (flags & 0x1) {
        parts.splice(++index, 0, {
          n: "data 0",
          l: 2
        });
        parts.splice(++index, 0, {
          n: "data 1",
          l: 2
        });
        parts.splice(++index, 0, {
          n: "data 2",
          l: 2
        });
        parts.splice(++index, 0, {
          n: "data 3",
          l: 2
        });
      }
      if (flags & 0x2) {
        //console.log("slot data flag 0x2 ??");
      }
      if (flags & 0x4) {
        parts.splice(++index, 0, {
          n: "type",
          parse: parsecol
        });
      }
      if (flags & 0x8) {
        //console.log("slot data flag 0x8 ??");
      }
    };
    var parsecol = function(obj, index) {
      var code;
      obj.l = 2;
      code = obj.read(2);
      if (code == 0x3210) {
        parts.splice(index + 1, 0, {
          n: "color",
          type: "color4"
        });
      }
      if (code == 0x8001 || code == 0x220f) {
        parts.splice(index + 1, 0, {
          n: "col 0",
          type: "color"
        });
        parts.splice(index + 2, 0, {
          n: "col 1",
          type: "color"
        });
      }
    };
    //Unused var
    /*var parsesubcol = function(obj) {
      obj.l = 2;
      obj.control = {
        t: "color",
        v: obj.readHex(2),
        func: function(obj, v) {
          obj.setint(rgbToHsl(v.slice(1)), 2);
        }
      };
    };*/
    var slotnames = ["Helm", "Cape", "Neck", "Weapon", "Body", "Off-hand", "Arms", "Legs", "Face", "Hands", "Boots", "Jaw", "Aura", "Sh. mh", "Sh. oh", "Wings"];
    parts = [{
        n: "Gender",
        parse: parsegender
      }, {
        n: "Helm",
        parse: parseslot
      }, {
        n: "Cape",
        parse: parseslot
      }, //Can also be Wing
      {
        n: "Neck",
        parse: parseslot
      }, {
        n: "Weapon",
        parse: parseslot
      }, //Item id +64, 0x40 boogie bow
      {
        n: "Body",
        parse: parseslot
      }, {
        n: "Off-hand",
        parse: parseslot
      }, {
        n: "Arms",
        parse: parseslot
      }, {
        n: "Legs",
        parse: parseslot
      }, {
        n: "Face",
        parse: parseslot
      }, {
        n: "Hands",
        parse: parseslot
      }, {
        n: "Boots",
        parse: parseslot
      }, {
        n: "Jaw",
        parse: parseslot
      }, {
        n: "Aura",
        parse: parseslot
      }, {
        n: "Sh. mh",
        parse: parseslot
      }, //mh
      {
        n: "Sh. oh",
        parse: parseslot
      }, {
        n: "Wings",
        parse: parseslot
      }, {
        n: "Recol",
        parse: setrecol
      }, {
        n: "c.Hair",
        l: 1
      }, {
        n: "c.Top",
        l: 1
      }, {
        n: "c.Legs",
        l: 1
      }, {
        n: "c.Boots",
        l: 1
      }, {
        n: "c.Wrist?",
        l: 1
      }, {
        n: "Bin",
        l: 2
      }, {
        n: "Bin",
        l: 2
      }, {
        n: "Bin",
        l: 1
      }, {
        n: "Anim",
        l: 2,
        type: "int"
      }
    ];
    hex = base64ToHex(str);
    blocks = [];
    for (a = 0; a < parts.length; a++) {
      block = new byteblock();
      block.hex = hex;
      block.name = parts[a].n;
      if (parts[a].parse) {
        parts[a].parse(block, a);
      }
      if (parts[a].l !== undefined) {
        block.l = parts[a].l;
      }
      if (parts[a].type) {
        if (parts[a].type == "int") {
          block.control = {
            v: block.read(block.l),
            t: "int",
            func: function(obj, v) {
              obj.setint(+v, obj.l);
            }
          };
        }
        if (parts[a].type == "color") {
          block.l = 2;
          block.control = {
            v: block.readHex(2),
            t: "color",
            func: function(obj, v) {
              obj.hex = rgbToHsl(v.slice(1));
            }
          };
        }
        if (parts[a].type == "color4") {
          block.l = 8;
          block.control = {
            v: block.readHex(8),
            t: "color4",
            func: function(obj, v) {
              obj.hex = obj.hex.slice(0, v[0] * 4) + rgbToHsl(v[1].slice(1)) + obj.hex.slice(v[0] * 4 + 4);
            }
          };
        }
      }
      block.hex = hex.slice(0, block.l * 2);
      hex = hex.slice(block.l * 2);
      blocks.push(block);
    }
    r = "<table><tr><th>Name</th><th>Hex</th><th>Edit</th><th>Reset</th></tr>";
    for (a = 0; a < blocks.length; a++) {
      block = blocks[a];
      r += "<tr>";
      //Name
      r += "<td>" + block.name + "</td>";
      //Hex
      r += "<td>";
      c = "";
      for (b = 0; b < block.l; b++) {
        c += (b === 0 ? "" : " ") + block.hex.slice(b * 2, b * 2 + 2);
      }
      r += "<input style='width:35px;' onkeydown='imgcapt.blockkeydown(" + a + ",event);' onchange='imgcapt.editblockhex(" + a + ",this.value)' value='" + c + "'/>";
      r += "</td>";
      //Edit
      if (block.control) {
        r += "<td>";
        if (block.control.t == "button") {
          r += "<input type='button' style='width:64px;' onclick='imgcapt.editblock(" + a + ",true)' value='" + block.control.v + "' />";
        }
        if (block.control.t == "int") {
          r += "<input type='number' style='width:64px;' " + (block.control.title ? "title='" + block.control.title.replace(/'/g, "&apos;") + "'" : "") + " onchange='imgcapt.editblock(" + a + ",this.value)' value='" + block.control.v + "' />";
        }
        if (block.control.t == "dropdown") {
          r += "<select onchange='imgcapt.editblock(" + a + ",this.value)'>";
          for (b = 0; b < block.control.opts.length; b++) {
            r += "<option " + (block.control.v == block.control.opts[b][0] ? "selected" : "") + " value='" + block.control.opts[b][0] + "'>" + block.control.opts[b][1] + "</option>";
          }
          r += "</select>";
        }
        if (block.control.t == "color") {
          r += "<input type='color' style='width:64px' onchange='imgcapt.editblock(" + a + ",this.value);' value='#" + hslToRgb(block.control.v) + "' />";
        }
        if (block.control.t == "color4") {
          r += "<input type='color' style='width:15px; padding:0px;' onchange='imgcapt.editblock(" + a + ",[0,this.value]);' value='#" + hslToRgb(block.control.v.slice(0, 4)) + "' />";
          r += "<input type='color' style='width:15px; padding:0px;' onchange='imgcapt.editblock(" + a + ",[1,this.value]);' value='#" + hslToRgb(block.control.v.slice(4, 8)) + "' />";
          r += "<input type='color' style='width:15px; padding:0px;' onchange='imgcapt.editblock(" + a + ",[2,this.value]);' value='#" + hslToRgb(block.control.v.slice(8, 12)) + "' />";
          r += "<input type='color' style='width:15px; padding:0px;' onchange='imgcapt.editblock(" + a + ",[3,this.value]);' value='#" + hslToRgb(block.control.v.slice(12, 16)) + "' />";
        }
        r += "</td>";
      }
      //Reset
      if (block.reset) {
        r += "<td><input type='button' onclick='imgcapt.resetblock(" + a + ");' value='X'/></td>";
      }
    }
    r += "</table>";
    root = document.createElement("div");
    root.style.position = "fixed";
    root.id = "streditroot";
    root.style.left = "0px";
    root.style.top = "0px";
    root.style.zIndex = "10000";
    root.style.background = "url(http://www.runescape.com/img/rs3/6-box-top.jpg) -1px 0px no-repeat, url(http://www.runescape.com/img/rs3/content_repeat_y.jpg) -1px 310px no-repeat";
    root.style.padding = "2px";
    root.style.border = "2px solid black";
    root.style.boxShadow = "2px 2px 10px 1px rgba(0, 0, 0, 0.7)";
    root.style.maxHeight = "100%";
    root.style.overflowX = "auto";
    root.style.width = "220px";
    root.style.overflowX = "hidden";
    root.onmousewheel = function(e) {
      root.scrollTop -= e.wheelDeltaY;
      return false;
    };
    root.innerHTML = r;
    if (imgcapt.editel) {
      imgcapt.editel.remove();
    }
    document.body.appendChild(root);
    imgcapt.editel = root;
    imgcapt.stredit.blocks = blocks;
    imgcapt.stredit.hextail = hex;
  };
  imgcapt.blockkeydown = function(index, e) {
    var block, key;
    key = String.fromCharCode(e.keyCode);
    block = imgcapt.stredit.blocks[index];
    if (key == "Q") {
      block.hex = numToHex(parseInt(block.hex, 16) + 1, block.l * 2);
      e.preventDefault();
      imgcapt.buildstr();
    }
    if (key == "W") {
      block.hex = numToHex(parseInt(block.hex, 16) - 1, block.l * 2);
      e.preventDefault();
      imgcapt.buildstr();
    }
  };
  imgcapt.resetblock = function(index) {
    var block;
    block = imgcapt.stredit.blocks[index];
    block.reset(block);
    imgcapt.buildstr();
  };
  imgcapt.editblockhex = function(index, value) {
    var block;
    value = value.toLowerCase();
    value = value.replace(/[^\da-f]/g, "");
    block = imgcapt.stredit.blocks[index];
    block.hex = value;
    imgcapt.buildstr();
  };
  imgcapt.editblock = function(index, value) {
    var block;
    block = imgcapt.stredit.blocks[index];
    block.control.func(block, value);
    imgcapt.buildstr();
  };
  imgcapt.buildstr = function() {
    var a, b, str, block, nth, nth2, selectstart, root, scroll;
    str = "";
    for (a = 0; a < imgcapt.stredit.blocks.length; a++) {
      block = imgcapt.stredit.blocks[a];
      str += block.hex;
    }
    str += imgcapt.stredit.hextail;
    if (window.event) {
      root = document.getElementById("streditroot");
      if (root) {
        scroll = root.scrollTop;
      }
      a = window.event.target;
      if (a) {
        if (a.type == "text") {
          selectstart = a.selectionStart;
        }
        a = a.parentElement;
      }
      if (a) {
        nth2 = 1;
        b = a;
        while (b.nodeType === Node.ELEMENT_NODE && (b = b.previousSibling)) {
          nth2++;
        }
        a = a.parentElement;
      }
      if (a) {
        nth = 1;
        b = a;
        while (b.nodeType === Node.ELEMENT_NODE && (b = b.previousSibling)) {
          nth++;
        }
      }
    }
    imgcapt.setstring(hexToBase64(str));
    if (nth !== undefined) {
      a = "#streditroot > table > tbody > tr:nth-child(" + nth + ") > td:nth-child(" + nth2 + ") > *";
      b = document.querySelector(a);
      if (b) {
        b.focus();
        if (selectstart !== undefined) {
          b.selectionStart = selectstart;
        }
      }
      root = document.getElementById("streditroot");
      if (root && scroll !== undefined) {
        root.scrollTop = scroll;
      }
    }
  };
 
  //Auto and Manual paint
  imgcapt.waitpaint = function(func) {
    imgcapt.waitpaintlist.push(func);
    imgcapt.trypaint();
  };
  imgcapt.trypaint = function() {
    if (imgcapt.manualpaint && imgcapt.paintfunc) {
      imgcapt.paintfunc();
    }
  };
 
  //Upload functions
  imgcapt.overrideGetFrame = function() {
    imgcapt.oldGetFrame = imgcapt.oldGetFrame || window.requestAnimationFrame;
    window.requestAnimationFrame = function(func) {
      var a, b;
      if (!imgcapt.paintfunc) {
        imgcapt.paintfunc = func;
      } //Capture frame code
      b = []; //Clone the event array to prevent problems with multiple getframe() calls in the stack
      for (a = imgcapt.waitpaintlist.length - 1; a >= 0; a--) {
        b.push(imgcapt.waitpaintlist[a]);
        imgcapt.waitpaintlist.length--;
      }
      for (a = b.length - 1; a >= 0; a--) {
        b[a]();
        b.length--;
      }
      if (!imgcapt.manualpaint) {
        return imgcapt.oldGetFrame.apply(this, arguments);
      }
    };
  };
  imgcapt.setSize = function(w, h) {
    Module.setCanvasSize(w, h);
  };
  imgcapt.stackstart = function() {
    imgcapt.stacks = {};
    imgcapt.stackcapt = true;
  };
  imgcapt.stackend = function() {
    imgcapt.stackcapt = false;
    console.log(imgcapt.stacks);
  };
  imgcapt.getUrlForWorker = function(bodyString) {
    //Convert JS string into a URL to run a web worker from. Allows cross-domain worker injection.
    var blob = new Blob([bodyString], {
      type: 'text/javascript'
    }); //Pass a useful mime type here
    return URL.createObjectURL(blob);
  };
  imgcapt.logupload = function(str) {
    if (!imgcapt.uploadlog) {
      return;
    }
    console.log(new Date()
      .toLocaleTimeString(), str);
  };
  imgcapt.loadlibs = function() {
    var a;
    for (a in imgcapt.libs) {
      scr = document.createElement("script");
      scr.src = imgcapt.liborigin + imgcapt.libs[a];
      document.head.appendChild(scr);
    }
    for (a in imgcapt.libstrings) {
      (function() { // Make closure to copy the index value
        var strname = a;
        dlpage(imgcapt.liborigin + imgcapt.libstrings[a], function(t) {
          imgcapt[strname] = t;
        });
      })();
    }
  };
  imgcapt.startuploading = function() {
    if (!imgcapt.uploadpass) {
      imgcapt.uploadpass = prompt("This function will start updating runeapps image database\n\npassword?");
      if (!imgcapt.uploadpass) {
        return;
      }
    }
    imgcapt.setCam(imgcapt.uploadcam);
    if (imgcapt.el.width != 1000 || imgcapt.el.height != 700) {
      console.log("Wrong canvas size to start upload (not 1000x700)");
      return;
    }
    dlpage(imgcapt.uploadpage + "?pass=" + imgcapt.uploadpass, function(t) {
      var obj;
      obj = JSON.parse(t);
      imgcapt.strlist = obj.todo;
      imgcapt.logupload("upload started");
      imgcapt.uploading = true;
      imgcapt.checkImg();
      document.getElementById("imgcaptupload")
        .style.background = "green";
      document.getElementById("imgcaptupload")
        .innerHTML = "Cancel upload";
    });
  };
  imgcapt.stopUpload = function() {
    if (!imgcapt.uploading) {
      return;
    }
    imgcapt.logupload("upload stopped");
    imgcapt.uploading = false;
    document.getElementById("imgcaptupload")
      .style.background = "red";
    document.getElementById("imgcaptupload")
      .innerHTML = "Start upload";
    return;
  };
  imgcapt.checkImg = function() {
    var a, b, c, buffer;
    if (!imgcapt.uploading) {
      return;
    }
    imgcapt.captCnv.width = imgcapt.el.width;
    imgcapt.captCnv.height = imgcapt.el.height;
    imgcapt.trypaint();
    imgcapt.captCtx.drawImage(imgcapt.el, 0, 0);
    buffer = imgcapt.captCtx.getImageData(imgcapt.captCnv.width / 2 - 20, imgcapt.captCnv.height / 2 - 20, 40, 40);
    c = 0;
    for (a = 0; a < buffer.width; a++) {
      for (b = 0; b < buffer.height; b++) {
        c += buffer.data[a * 4 + b * 4 * buffer.width + 3];
      }
    }
    c /= 255 * 40 * 40;
    if (c > 0.5 && imgcapt.lastLoad < Date.now() - imgcapt.unloadTime) {
      imgcapt.logupload("load completed");
      setTimeout(function() {
        imgcapt.logupload("load settled");
        var a, b;
        b = true;
        for (a in imgcapt.captured) {
          if (imgcapt.captured[a].str == imgcapt.currentStr) {
            b = false;
            break;
          }
        }
        if (imgcapt.firstLoad) {
          imgcapt.firstLoad = false;
        } else if (b) {
          imgcapt.uploadImg();
          imgcapt.lastLoad = Date.now();
          imgcapt.captured.push({
            str: imgcapt.currentStr,
            success: true
          });
        }
        imgcapt.setNextPlayer();
      }, imgcapt.settleTime);
      return;
    }
    if (c < 0.5 && imgcapt.lastLoad < Date.now() - imgcapt.skipTime && !imgcapt.firstLoad) {
      imgcapt.logupload("load failed");
      imgcapt.lastLoad = Date.now();
      imgcapt.captured.push({
        str: imgcapt.currentStr,
        success: false
      });
      imgcapt.setNextPlayer();
      return;
    }
    setTimeout(imgcapt.checkImg, imgcapt.checkTime);
  };
  imgcapt.getImg = function() {
    imgcapt.saveCnv.width = imgcapt.uploadcrop.w;
    imgcapt.saveCnv.height = imgcapt.uploadcrop.h;
    imgcapt.saveCtx.drawImage(imgcapt.el, -imgcapt.uploadcrop.x, -imgcapt.uploadcrop.y);
    return imgcapt.saveCnv.toDataURL("image/png");
  };
  imgcapt.uploadImg = function() {
    var imgdata, name, str;
    name = imgcapt.currentPlayer;
    str = imgcapt.currentStr;
    imgdata = imgcapt.getImg();
    dlpagepost(imgcapt.uploadpage, {
      pass: imgcapt.uploadpass,
      img: imgdata,
      avatarstr: str
    }, function(t) {
      if (t.indexOf("Fatal error") === 0) {
        console.log(t);
        return;
      }
      var obj = JSON.parse(t);
      imgcapt.logupload(imgcapt.uploadview.replace("$0", obj.uploaded));
    }, function() {
      console.log("Upload failed.");
    });
  };
  imgcapt.displayImg = function() {
    var cnv, ctx;
    cnv = document.createElement("canvas");
    cnv.width = imgcapt.el.width;
    cnv.height = imgcapt.el.height;
    ctx = cnv.getContext("2d");
    ctx.drawImage(imgcapt.el, 0, 0);
    window.open(cnv.toDataURL("image/png"));
  };
 
  //Start
  imgcapt.start = setTimeout(function() {
    console.clear();
    //Log ASM events
    Module.addOnInit(function() {
      console.log("init");
    });
    Module.addOnPreRun(function() {
      console.log("prerun");
    });
    Module.addOnPreMain(function() {
      console.log("premain");
    });
    Module.addOnPostRun(function() {
      console.log("postrun");
    });
    Module.addOnExit(function() {
      console.log("exit");
    });
    imgcapt.el = document.getElementById("canvas");
    imgcapt.captCnv = document.createElement("canvas");
    imgcapt.captCtx = imgcapt.captCnv.getContext("2d");
    imgcapt.saveCnv = document.createElement("canvas");
    imgcapt.saveCtx = imgcapt.saveCnv.getContext("2d");
    if (imgcapt.firstLoad) {
      imgcapt.overrideContext();
      imgcapt.loadlibs();
    }
    imgcapt.overrideGetFrame();
    imgcapt.overrideCam();
    imgcapt.controlPanel();
    imgcapt.setCam();
    document.location.search.replace(/searchName=([\w%_]+)(\&|$)/, function(a, b) {
      imgcapt.setplayer(b);
    });
  }, 3000);
  imgcapt.overrideContext = function() {
    imgcapt.oldGetContext = HTMLCanvasElement.prototype.getContext;
    HTMLCanvasElement.prototype.getContext = function(type, args) {
      if (type == "webgl") {
        args.preserveDrawingBuffer = true;
        console.log("Webgl context created.");
      }
      return imgcapt.oldGetContext.apply(this, arguments);
    };
  };
  imgcapt.overrideCam = function() {
    imgcapt.oldUniformMatrix4fv = imgcapt.oldUniformMatrix4fv || WebGLRenderingContext.prototype.uniformMatrix4fv;
    WebGLRenderingContext.prototype.uniformMatrix4fv = function() {
      if (imgcapt.overrideCam) {
        //Override 3D rasterizer
        if (imgcapt.overrideCam && arguments[2][0] != 1) {
          for (var a = 0; a < 16; a++) {
            arguments[2][a] = imgcapt.verticleMatrix[a];
          }
        } else {
          //Vertical lock
          if (imgcapt.overridecenter && arguments[2][13] < -200) {
            arguments[2][13] = -400;
          }
        }
        //Begin detection stacks
        if (imgcapt.stackcapt) {
          var stack = new Error()
            .stack;
          var stackstr = "";
          stack.replace(/:(\d+:\d+)\)/g, function(a, b) {
            stackstr += b + " - ";
          });
          if (!imgcapt.stacks[stackstr]) {
            imgcapt.stacks[stackstr] = {};
          }
          var arstr = "";
          for (a = 0; a < 16; a++) {
            arstr += arguments[2][a].toFixed(1) + ",";
          }
          if (!imgcapt.stacks[stackstr][arstr]) {
            imgcapt.stacks[stackstr][arstr] = 0;
          }
          imgcapt.stacks[stackstr][arstr]++;
        }
        //End detection stacks
      }
      imgcapt.oldUniformMatrix4fv.apply(this, arguments);
    };
  };
 
  //Set block
  imgcapt.setNextPlayer = function() {
    var a, b, c;
    for (a in imgcapt.strlist) {
      b = true;
      for (c in imgcapt.captured) {
        if (imgcapt.captured[c].str == imgcapt.strlist[a].avatarstr) {
          b = false;
          break;
        }
      }
      if (b) {
        imgcapt.setstring(imgcapt.strlist[a].avatarstr);
        imgcapt.logupload("new string: " + imgcapt.currentStr);
        setTimeout(imgcapt.checkImg, imgcapt.unloadTime);
        return;
      }
    }
    imgcapt.stopUpload();
    console.log("Upload done.");
  };
  imgcapt.setplayer = function(player) {
    player = player.replace(/( |\-|\u00a0|%A0|%20)/g, "_");
    imgcapt.currentPlayer = player;
    //avatarViewer.avatarChange(player);
    dlpage("http://services.runescape.com/m=avatar-rs/" + encodeURIComponent(player) + "/appearance.dat", function(t) {
      if (t.indexOf(" ") != -1) {
        imgcapt.setMessage("Failed to load: " + player);
        return;
      }
      else {
        imgcapt.setMessage("Loaded player: " + player);
      }
      imgcapt.setstring(t);
    });
  };
  imgcapt.setNpc = function(id) {
        var str;
        str = "00ffff" + numToHex(+id, 4) + "0000000000000000000000ffff";
        str = hexToBase64(str);
        callString(str);
        imgcapt.setMessage("Loaded NPC: " + id);
        imgcapt.setstring(str);
        $("#streditroot").remove();
  };
  imgcapt.setmodel = function (n) {
    var index = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789*-";
    n = n * 4;
    var model = index.charAt(~~(n / 262144)) + index.charAt(~~((n % 262144) / 4096)) + index.charAt(~~((n % 4096) / 64)) + index.charAt(~~((n % 64) / 1));
    imgcapt.setMessage("Model (string): " + n / 4 + " (" + model + ")");
    Module.avatar.module.ccall("SetAppearance", "void", ["string"], ['AEABQAFAAUABQAFAAUABQAFAAUABQAFAAUABnHoAACAAAYA' + model + 'eAASMyDDcQAwAADAAAAAqL']);
    imgcapt.setstring("AEABQAFAAUABQAFAAUABQAFAAUABQAFAAUABnHoAACAAAYA" + model + "eAASMyDDcQAwAADAAAAAqL");
    $("#streditroot").remove();
  };
  imgcapt.setAnim = function(id) {
    Module.avatar.SetAnimID(+id);
    if (id === undefined){
      Module.avatar.SetAnimID(0);
    }
  };
  imgcapt.setstring = function(str) {
    Module.ccall("SetAppearance", "void", ["string"], [str]);
    imgcapt.currentStr = str;
    document.getElementById("imgcaptstring")
      .value = str;
    document.getElementById("imgcaptstringhex")
      .value = base64ToHex(str);
    imgcapt.drawstr(str);
  };
  imgcapt.setstringbin = function(str) {
    imgcapt.setstring(binToBase64(str));
  };
  imgcapt.setstringhex = function(str) {
    imgcapt.setstring(hexToBase64(str));
  };
 
  function dlpagepost(url, data, func, errorfunc) {
      var req, post, a, b;
      if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
      }
      if (func) {
        req.onload = function() {
          func(req.responseText);
        };
      }
      if (errorfunc) {
        req.onerror = errorfunc;
      }
      post = "";
      b = "";
      for (a in data) {
        post += b + encodeURIComponent(a) + "=" + encodeURIComponent(data[a]);
        b = "&";
      }
      req.open("POST", url, true);
      req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      req.send(post);
    }
 
  //Math
  function vmpr(v, m) { //Vector-matrix product
    var a, b, r, vl;
    r = [];
    vl = v.length;
    for (a = 0; a < vl; a++) {
      r[a] = 0;
      for (b = 0; b * vl < m.length; b++) {
        r[a] += v[b] * m[a + b * vl];
      }
    }
    return r;
  }
 
  function mmpr(m1, m2) {
    var a, b, c, size, r;
    size = Math.sqrt(m1.length);
    r = [];
    for (b = 0; b < size; b++) {
      for (a = 0; a < size; a++) {
        r[a + size * b] = 0;
        for (c = 0; c < size; c++) {
          r[a + size * b] += m1[c + size * b] * m2[a + size * c];
        }
      }
    }
    return r;
  }
 
  imgcapt.setCam = function(camobj) {
    var tr1, tr2, tr3, r, initial, aspect;
    var aa, ab, ac, x, y, z;
    if (camobj) {
      aa = camobj.angle[0];
      ab = camobj.angle[1];
      ac = camobj.angle[2];
      x = camobj.pos[0];
      y = camobj.pos[1];
      z = camobj.pos[2];
      imgcapt.fov = camobj.fov;
    } else {
      aa = imgcapt.cam.angle[0];
      ab = imgcapt.cam.angle[1];
      ac = imgcapt.cam.angle[2];
      x = imgcapt.cam.pos[0];
      y = imgcapt.cam.pos[1];
      z = imgcapt.cam.pos[2];
    }
    imgcapt.cam.angle = [aa, ab, ac];
    imgcapt.cam.pos = [x, y, z];
    initial = [
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1
    ];
    tr1 = [1, 0, 0, 0, 0, Math.cos(aa), -Math.sin(aa), 0, 0, Math.sin(aa), Math.cos(aa), 0, 0, 0, 0, 1];
    tr2 = [Math.cos(ab), 0, Math.sin(ab), 0, 0, 1, 0, 0, -Math.sin(ab), 0, Math.cos(ab), 0, 0, 0, 0, 1];
    tr3 = [Math.cos(ac), -Math.sin(ac), 0, 0, Math.sin(ac), Math.cos(ac), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
    r = mmpr(mmpr(mmpr(initial, tr2), tr1), tr3);
    aspect = imgcapt.el.height / imgcapt.el.width;
    //Fix aspect ratio, scale x
    r[0] *= aspect;
    r[4] *= aspect;
    r[8] *= aspect;
    r[3] = r[2] * imgcapt.cam.fov;
    r[7] = r[6] * imgcapt.cam.fov;
    r[11] = r[10] * imgcapt.cam.fov;
    r[12] = x;
    r[13] = y;
    r[14] = 200;
    r[15] = z;
    if (imgcapt.bobblehead) {
      r[7] = -1;
      r[13] -= 200;
    }
    imgcapt.verticleMatrix = r;
    return r;
  };
  imgcapt.controlPanel = function() {
    if (imgcapt.controlel) {
      imgcapt.controlel.remove();
    }
    var a, root, str, funcs, sp, di, db, re, rc, bobble, paint, up, orc, hh;
    root = document.createElement("div");
    root.style.position = "fixed";
    root.style.top = "0px";
    root.style.right = "0px";
    root.style.zIndex = "100000";
    root.style.display = "flex";
    root.style.alignItems = "flex-end";
    root.style.flexDirection = "column";
    root.style.fontFamily = "sans-serif";
    root.style.fontSize = "14px";
    root.style.textAlign = "center";
    root.style.color = "white";
    root.style.padding = "2px";
    root.style.border = "2px solid black";
    root.style.background = "url(http://www.runescape.com/img/rs3/6-box-top.jpg) -1px 0";
    root.style.boxShadow = "-2px 2px 10px 1px rgba(0, 0, 0, 0.7)";
    root.dragstart = false;
    root.dragstartangle = false;
    //override previous funcs
    if (imgcapt.controlfuncs) {
      imgcapt.el.removeEventListener("mousedown", imgcapt.controlfuncs.mousedown);
      imgcapt.el.removeEventListener("mouseup", imgcapt.controlfuncs.mouseup);
      imgcapt.el.removeEventListener("mousemove", imgcapt.controlfuncs.mousemove);
      imgcapt.el.removeEventListener("mousewheel", imgcapt.controlfuncs.mousewheel);
    }
    funcs = {
      mousemove: function(e) {
        e.preventDefault();
        if (root.dragstart) {
          imgcapt.cam.angle = [
            (e.offsetY - root.dragstart[0]) * Math.PI / 180 + root.dragstartangle[0], (e.offsetX - root.dragstart[1]) * Math.PI / 180 + root.dragstartangle[1],
            imgcapt.cam.angle[2]
          ];
          imgcapt.setCam();
          imgcapt.trypaint();
        }
      },
      mousedown: function(e) {
        e.preventDefault();
        root.dragstart = [e.offsetY, e.offsetX, 0];
        root.dragstartangle = [imgcapt.cam.angle[0], imgcapt.cam.angle[1], imgcapt.cam.angle[2]];
        e.preventDefault();
      },
      mouseup: function(e) {
        e.preventDefault();
        root.dragstart = false;
      },
      mousewheel: function(e) {
        imgcapt.cam.pos[2] += e.deltaY;
        imgcapt.setCam();
        imgcapt.trypaint();
      },
      DOMMouseScroll: function(e) {
        imgcapt.cam.pos[2] += (e.detail) * 10;
        imgcapt.setCam();
        imgcapt.trypaint();
      }
    };
    for (a in imgcapt.controlfuncs) {
      imgcapt.el.removeEventListener(a, imgcapt.controlfuncs[a], true);
    }
    for (a in funcs) {
      imgcapt.el.addEventListener(a, funcs[a], true);
    }
    imgcapt.controlfuncs = funcs;
 
    str = "";
    //str+="<div style='width:360px; height:180px; background:#F60;' onmousedown='this.parentElement.func.dragstart(event);' onmousemove='this.parentElement.func.mousemove(event);' onmouseup='this.parentElement.func.dragend(event);'></div>"
    sp = "<span style='display: inline;'><label style='color: ghostwhite; font-weight: bold; text-shadow: 2px 2px black;'>";
    re = '$("#3d-yaxis").val(0);$("#3d-xaxis").val(0);imgcapt.cam.pos[0]=0;imgcapt.cam.pos[1]=0;imgcapt.cam.pos[2]=560;imgcapt.cam.angle[0]=0.05;imgcapt.cam.angle[1]=0.25;imgcapt.setCam();imgcapt.setMessage("Default camera position");';
    rc = "<div id='3d-reset' title='Reset camera to original position' style='color:yellow; font-weight:bold; text-shadow: 2px 2px black;'><span style='cursor:pointer; padding:0 34px 0 5px' onclick='" + re + "'>Reset</span>";
    di = '$("#3d-yaxis").val(0);$("#3d-xaxis").val(0);imgcapt.cam.pos[0]=0;imgcapt.cam.pos[1]=0;imgcapt.cam.pos[2]=560;imgcapt.cam.angle[0]=1.5;imgcapt.cam.angle[1]=0;imgcapt.setCam();imgcapt.setMessage("Detailed item image position (top-down)");';
    db = "<div id='3d-dii' title='Set the camera to top-down view' style='color:yellow; font-weight:bold; text-shadow: 2px 2px black;'><span style='cursor:pointer; padding:0 34px 0 5px' onclick='" + di + "'>Detail</span>";
    orc = "<span title='Toggle Y-Axis center point override' style='color:" + (imgcapt.overridecenter ? "lime" : "red") + "; cursor:pointer; height:20px; font-weight:bold; padding: 0 17px 0 5px; text-shadow: 2px 2px black;'" + 
          "onclick='imgcapt.overridecenter=!imgcapt.overridecenter; this.style.color=(imgcapt.overridecenter?\"lime\":\"red\");imgcapt.setMessage((imgcapt.overridecenter?\"Override Center: On\":\"Override Center: Off\"))'>Center Y</span>";
    bobble = "<span title='Toggle Bobblehead mode' style='color:" + (imgcapt.bobblehead ? "lime" : "red") + "; cursor:pointer; font-weight:bold; padding: 0 39px 0 5px; text-shadow: 2px 2px black;'" +
             "onclick='imgcapt.bobblehead=!imgcapt.bobblehead; imgcapt.setCam(); this.style.color=(imgcapt.bobblehead?\"lime\":\"red\");imgcapt.setMessage((imgcapt.bobblehead?\"Bobblehead: On\":\"Bobblehead: Off\"))'>Bobble</span>";
    paint = "<span title='Toggle whether to draw frames as they are loaded' style='color:" + (imgcapt.manualpaint ? "red" : "lime") + "; cursor:pointer; height:20px; font-weight:bold; padding:0 14px 0 5px; text-shadow: 2px 2px black;'" +
            "onclick='imgcapt.manualpaint=!imgcapt.manualpaint; this.style.color=(imgcapt.manualpaint?\"red\":\"lime\");imgcapt.setMessage((imgcapt.manualpaint?\"Autopaint: Off (Less frames, slow)\":\"Autopaint: On (All frames, fast)\"))'>Autopaint</span>";
    up = "<div id='imgcaptupload' style='color:" + (imgcapt.uploading ? "lime" : "red") + "; display: none; cursor:default; font-size: 10px; margin: -5px; font-weight:bold; text-shadow: 2px 2px black;'" +
         "onclick='if(imgcapt.uploading){imgcapt.stopUpload();}else{imgcapt.startuploading();}'>" + (imgcapt.uploading ? "Cancel" : "Upload") + "</div>";
    hh = "<span id='imgcaptvhex' title='Toggle hex table visibility' style='color:yellow; font-weight:bold; text-shadow: 2px 2px black; cursor:pointer; padding:0 7px 0 5px;'" +
         "onclick='$(\"#streditroot\").toggle($(\"#streditroot\").display);imgcapt.setMessage(\"Toggled hex table visiblity\")'>Hex Table</span>";
 
    str = "";
    str += "<div id='alog3d-title' style='color: whitesmoke; font-weight: bold; line-height:18px; padding: 2px; text-shadow: 2px 2px black;'>Adventurer's Log 3D Character Viewer UI</div>";
    str += "<hr style='background-color: yellow; margin: 0 2px 5px; width: 280px;' />";
    str += "<input id='imgcaptstring' type='text' style='width:280px;' placeholder='loadoutstring base64' title='Base64 string' onkeydown='if(event.keyCode==13){imgcapt.setstring(this.value);}'/>";
    str += "<input id='imgcaptstringhex' type='text' style='width:280px;' placeholder='loadoutstring hex' title='Hex string' onkeydown='if(event.keyCode==13){imgcapt.setstringhex(this.value);}'/>";
    str += "<div id='imgcaptmes' style='color: yellow; font-weight: bold; line-height:18px; height: 18px; padding: 2px; text-shadow: 2px 2px black;'></div>";
    str += "<div style='display: inline;'>" + hh + sp + "Player: </label><input type='text' maxlength=12 style='width:150px;' placeholder='Player Name' onkeydown='if(event.keyCode==13){imgcapt.setplayer(this.value);}'/></span></div>";
    str += "<div style='display: inline;'>" + orc + sp + "Model: </label><input type='number' min=0 max=250000 style='width:150px;' placeholder='Model (Number)' onkeydown='if(event.keyCode==13){imgcapt.setmodel(this.value);}'/></span></div>";
    str += "<div style='display: inline;'>" + paint + sp + "Anim: </label><input type='number' min=0 max=50000 style='width:150px;' placeholder='Animation ID (Number)' onkeydown='if(event.keyCode==13){imgcapt.setAnim(this.value);}'/></span></div>";
    str += "<div style='display: inline;'>" + bobble + sp + "NPC: </label><input type='number' min=0 max=50000 style='width:150px;' placeholder='NPC ID (Number)' onkeydown='if(event.keyCode==13){imgcapt.setNpc(this.value);}'/></span></div>";
    str += db + sp + "X-Axis: </label><input type='range' value='" + imgcapt.cam.pos[0] + "' min='-600' max='600' step='5' style='width:150px;' oninput='imgcapt.cam.pos[0]=this.value; imgcapt.setCam();' id='3d-xaxis'/></span></div>";
    str += rc + sp + "Y-Axis: </label><input type='range' value='" + imgcapt.cam.pos[1] + "' min='-600' max='600' step='5' style='width:150px;' oninput='imgcapt.cam.pos[1]=this.value; imgcapt.setCam();' id='3d-yaxis'/></span></div>";
    str += "<div id='imgcaptanims'></div>";
    str += "<div id='stredit' style='background:#333;'></div>";
    $(up).insertBefore('#advlog-search');
    root.innerHTML = str;
    document.body.appendChild(root);
    imgcapt.controlel = root;
  };
  imgcapt.printmatrix = function() {
    var str, a, b;
    str = "[\n";
    for (a = 0; a < 4; a++) {
      str += "  ";
      for (b = 0; b < 4; b++) {
        str += imgcapt.verticleMatrix[a * 4 + b].toFixed(1) + ", ";
      }
      str += "\n";
    }
    return str + "]";
  };
  imgcapt.printcam = function() {
    console.log(JSON.stringify(imgcapt.cam));
  };
  imgcapt.setMessage = function(str) {
      document.getElementById("imgcaptmes")
        .innerHTML = str;
  };
 
  // String detect / API calls
  function callString(str) {
    Module.ccall("SetAppearance", "void", ["string"], [str]);
  }
 
  function callHex(str) {
    callString(hexToBase64(str));
  }
 
  function callBin(str) {
    callString(binToBase64(str));
  }
 
  function callEquip(itemlist) {
      var a, b, str;
      str = "1";
      for (a = 0; a < 19; a++) {
        if (itemlist[a] !== undefined) {
          b = itemlist[a] + 1 << 14;
        } else {
          b = 0;
        }
        str += numToBin(b, 16);
      }
      str += "000000000000000000001001100000111000001110100011000000100000001000000000000000000000000000000000000010101001010000000000000000000000000000";
      callBin(str);
      console.log(str);
    }
 
  //Binary
  function flipbit(index) {
    curbin = curbin.substr(0, index) + (curbin[index] == "1" ? "0" : "1") + curbin.substr(index + 1);
    callBin(curbin);
  }
  binindex = 0;
 
  function flipnext() {
    flipbit(binindex);
    binindex++;
    return (binindex - 1)
      .toString(8);
  }
 
  function flipcurrent() {
      flipbit(binindex - 1);
      return (binindex - 1)
        .toString(8);
    }
 
  //Conversions
  function binToBase64(str) {
    var a, b, r;
    r = "";
    for (a = 0; a < str.length; a += 6) {
      b = parseInt(str.substr(a, 6), 2) | 0;
      r += chars[b];
    }
    return r;
  }
 
  function base64ToBin(str) {
    var a, b, r;
    r = "";
    for (a = 0; a < str.length; a++) {
      b = chars.indexOf(str[a]);
      r += numToBin(b, 6);
    }
    return r;
  }
 
  function hexToBase64(str) {
    var a, b, r, len;
    str = str.toUpperCase();
    len = str.length;
    str += "000"; //ensure we pad it
    r = "";
    for (a = 0; a < len; a += 3) {
      b = parseInt(str.substr(a, 3), 16);
      r += chars[b >> 6] + chars[b % 64];
    }
    return r;
  }
 
  function base64ToHex(str) {
    var a, b, r;
    r = "";
    for (a = 0; a < str.length; a += 2) {
      b = chars.indexOf(str[a]) * 64;
      b += chars.indexOf(str[a + 1]);
      r += numToHex(b, 3);
    }
    return r;
  }
 
  function numToHex(n, length) {
    var a, r;
    r = n.toString(16);
    for (a = r.length; a < length; a++) {
      r = "0" + r;
    }
    return r.slice(-length);
  }
 
  function numToBin(n, length) {
    var a, r;
    r = n.toString(2);
    for (a = r.length; a < length; a++) {
      r = "0" + r;
    }
    return r;
  }
 
  //Player information
  loadedStr = "";
 
  function loadphex(player) {
    dlpage("http://services.runescape.com/m=avatar-rs/" + encodeURIComponent(player) + "/appearance.dat", function(t) {
      if (t.indexOf(" ") != -1) {
        return;
      }
      var hex = base64ToHex(t);
      console.log(hex);
      loadedStr = hex;
      callHex(hex);
    });
  }
 
  function loadpbin(player) {
    dlpage("http://services.runescape.com/m=avatar-rs/" + encodeURIComponent(player) + "/appearance.dat", function(t) {
      if (t.indexOf(" ") != -1) {
        return;
      }
      var bin = base64ToBin(t);
      console.log(bin);
      loadedStr = bin;
      callBin(bin);
    });
  }
 
  //Color
  function hslToRgb(hsl) {
    var num, rgb, h, s, l;
    num = parseInt(hsl, 16);
    h = num >> 10;
    s = (num >> 7) & 0x7;
    l = num & 0x7f;
    rgb = _hslToRgb(h / 63, s / 7, l / 127);
    return numToHex((rgb[0] << 16) + (rgb[1] << 8) + rgb[2], 6);
  }
 
  function rgbToHsl(rgb) {
    var num, hsl, r, g, b;
    num = parseInt(rgb, 16);
    r = num >> 16;
    g = (num >> 8) & 0xff;
    b = num & 0xff;
    hsl = _rgbToHsl(r, g, b);
    return numToHex((Math.round(hsl[0] * 63) << 10) + (Math.round(hsl[1] * 7) << 7) + Math.round(hsl[2] * 127), 4);
  }
 
  function _hslToRgb(h, s, l) {
    var r, g, b;
    if (s === 0) {
      r = g = b = l; // achromatic
    } else {
      var hue2rgb = function hue2rgb(p, q, t) {
        if (t < 0) t += 1;
        if (t > 1) t -= 1;
        if (t < 1 / 6) return p + (q - p) * 6 * t;
        if (t < 1 / 2) return q;
        if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
        return p;
      };
      var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
      var p = 2 * l - q;
      r = hue2rgb(p, q, h + 1 / 3);
      g = hue2rgb(p, q, h);
      b = hue2rgb(p, q, h - 1 / 3);
    }
    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
  }
 
  function _rgbToHsl(r, g, b) {
      r /= 255, g /= 255, b /= 255;
      var max = Math.max(r, g, b),
        min = Math.min(r, g, b);
      var h, s, l = (max + min) / 2;
      if (max == min) {
        h = s = 0; // achromatic
      } else {
        var d = max - min;
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        switch (max) {
          case r:
            h = (g - b) / d + (g < b ? 6 : 0);
            break;
          case g:
            h = (b - r) / d + 2;
            break;
          case b:
            h = (r - g) / d + 4;
            break;
        }
        h /= 6;
      }
      return [h, s, l];
    }
 
  //Library
  function dlpage(url, func, errorfunc) {
      var req;
      req = new XMLHttpRequest();
      if (func) {
        req.onload = function() {
          func(req.responseText);
        };
      }
      if (errorfunc) {
        req.onerror = function() {
          errorfunc();
        };
      }
      req.open("GET", url, true);
      req.send();
    }
 
  //Other
  function scanmem(str) {
    var a, b, r;
    r = [];
    for (a = 0; a < Module.HEAP8.length; a++) {
      if (a % 10000000 === 0) {
        console.log("memscan:", a);
      }
      for (b = 0; b < str.length; b++) {
        if (Module.HEAP8[a + b] != str.charCodeAt(b)) {
          break;
        }
      }
      if (b == str.length) {
        r.push(a);
      }
    }
    console.log(r);
  }
 
  function testwithmem(str) {
    callString(str);
    scanmem(str);
  }
 
  function escapeHtml(unsafe) {
    return unsafe.replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#039;");
  }
 
  function test(){
    vmpr();
    callEquip();
    flipnext();
    flipcurrent();
    loadphex();
    loadpbin();
    testwithmem();
    escapeHtml();
  }
}
setTimeout(ab(), 3000);

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.