December SPECIAL! For a limited time only. Get 20% discount on a LIFETIME PRO account!Want more features on Pastebin? Sign Up, it's FREE!
tweet
Guest

Skullcode VM management code partial deobfuscation (ver 2)

By: a guest on Nov 24th, 2015  |  syntax: JavaScript  |  size: 63.43 KB  |  views: 16  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print  |  QR code  |  clone
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /**
  2.  * Implements the Skullcode system.
  3.  *
  4.  * This includes event dispatching, VM initialization, and main loop execution.
  5.  * @global
  6.  */
  7. var System = new function() {
  8.   var that = this;
  9.  
  10.   /**
  11.    * Represents a phase of execution (e.g. init, run...).
  12.    * @constructor
  13.    * @abstract
  14.    *
  15.    * Phase life-cycle is as follows:
  16.    *   begin(oldPhase);
  17.    *    - called after previous phase ends but before this phase is active.
  18.    *   wake();
  19.    *    - called after this phase is activated (i.e. System.oldPhase = this).
  20.    *   while (System.phase == this) {
  21.    *     update(dt);
  22.    *      - called on every main loop iteration, dt = time since last call.
  23.    *   }
  24.    *   end();
  25.    *    - called when phase is ending but before next phase begins.
  26.    */
  27.   function Phase() {
  28.     /**
  29.      * Called when this phase is activating.
  30.      *
  31.      * @param {Phase} oldPhase - The previous phase (null if none).
  32.      */
  33.     this.begin = function(oldPhase) {};
  34.  
  35.     /** Called when this phase is ending, before the next phase activates. */
  36.     this.end = function() {};
  37.  
  38.     /** Called when this phase has activated but before the first update. */
  39.     this.wake = function() {};
  40.  
  41.     /**
  42.      * Default NO-OP global event handler.
  43.      * @callback
  44.      * @param {InputState} s - The updated input state.
  45.      */
  46.     var defaultHandler = function(s) {
  47.       return false;
  48.     };
  49.  
  50.     /** Handle a 'character typed' event. */
  51.     this.charTyped = defaultHandler;
  52.     /** Handle a 'mouse scrolled' event. */
  53.     this.mouseWheel = defaultHandler;
  54.     /** Handle a 'mouse button released' event. */
  55.     this.mouseRelease = defaultHandler;
  56.     /** Handle a 'mouse button pressed' event. */
  57.     this.mousePress = defaultHandler;
  58.     /** Handle a 'mouse moved' event. */
  59.     this.mouseMove = defaultHandler;
  60.     /** Handle a 'key released' event. */
  61.     this.keyRelease = defaultHandler;
  62.     /** Handle a 'key pressed' event. */
  63.     this.keyPress = defaultHandler;
  64.  
  65.     /**
  66.      * Called on every main loop iteration whilst this phase is activated.
  67.      */
  68.     this.update = function(dt) {
  69.       return false;
  70.     };
  71.   }
  72.   this.Phase = Phase;
  73.  
  74.   /**
  75.    * @name Vector2
  76.    * Represents a 2-vector.
  77.    * @constructor
  78.    *
  79.    * Initialized to (0,0).
  80.    */
  81.   /**
  82.    * @name Vector2
  83.    * Copies a 2-vector.
  84.    * @constructor
  85.    *
  86.    * @param {Vector2} x - The vector to copy coordinates from.
  87.    */
  88.   /**
  89.    * Represents a 2-vector.
  90.    *
  91.    * @constructor
  92.    * @param {number} x - The initial x-coordinate of the vector.
  93.    * @param {number} y - The initial y-coordinate of the vector.
  94.    */
  95.   function Vector2(x, y) {
  96.     var that = this;
  97.     if (arguments.length >= 2) {
  98.       this.x = x;
  99.       this.y = y;
  100.     } else if (arguments.length == 1) {
  101.       this.x = x.x;
  102.       this.y = x.y;
  103.     } else {
  104.       this.x = this.y = 0;
  105.     }
  106.     this.toString = function() {
  107.       return "{x:" + that.x + ",y:" + that.y + "}";
  108.     };
  109.   }
  110.   this.Vector2 = Vector2;
  111.  
  112.   /**
  113.    * Represents a sample of input states.
  114.    * @constructor
  115.    *
  116.    * @param {InputState} [obj] - The InputState object to copy, if specified.
  117.    *
  118.    * If no InputState object is given, the state properties are initialized to
  119.    * 0/false.
  120.    *
  121.    * @member {Vector2} pagePos - Position of mouse in page coordinates.
  122.    * @member {Vector2} mousePos - Position of mouse in canvas coordinates.
  123.    * @member {Vector2} mouseMov - Distance moved since list mousemove event.
  124.    * @member {number} mouseWheel - 1 if last scrolled down, -1 if last scrolled up.
  125.    * @member {number} mouseButton - Bit-flags indicating currently pressed mouse buttons.
  126.    * @member {number} keyCode - key-code of last pressed key (see KeyboardEvent.keyCode).
  127.    * @member {number} charCode - char-code of last pressed key (see KeyboardEvent.charCode).
  128.    * @member {boolean} ctrlPressed - Flag indicating whether CTRL is pressed.
  129.    * @member {boolean} altPressed - Flag indicating whether ALT is pressed.
  130.    * @member {boolean} shiftPressed - Flag indicating whether SHIFT is pressed.
  131.    */
  132.   function InputState(obj) {
  133.     if (arguments.length > 0) {
  134.       this.pagePos = new Vector2(obj.pagePos);
  135.       this.mousePos = new Vector2(obj.mousePos);
  136.       this.mouseMov = new Vector2(obj.mouseMov);
  137.       this.mouseWheel = obj.mouseWheel;
  138.       this.mouseButton = obj.mouseButton;
  139.       this.keyCode = obj.keyCode;
  140.       this.charCode = obj.charCode;
  141.       this.ctrlPressed = obj.ctrlPressed;
  142.       this.altPressed = obj.altPressed;
  143.       this.shiftPressed = obj.shiftPressed;
  144.     } else {
  145.       this.pagePos = new Vector2();
  146.       this.mousePos = new Vector2();
  147.       this.mouseMov = new Vector2();
  148.       this.charCode = this.keyCode = this.mouseWheel = this.mouseButton = 0;
  149.       this.shiftPressed = this.altPressed = this.ctrlPressed = false;
  150.     }
  151.   }
  152.   this.InputState = InputState;
  153.  
  154.   /**
  155.    * Update the input state with the current modifier key states.
  156.    *
  157.    * @param {Event} evt - The current DOM event.
  158.    */
  159.   function updateModifiers(evt) {
  160.     inputState.ctrlPressed = evt.ctrlKey;
  161.     inputState.altPressed = evt.altKey;
  162.     inputState.shiftPressed = evt.shiftKey;
  163.   }
  164.  
  165.   /**
  166.    * Initialize input callbacks and start engine.
  167.    */
  168.   function init() {
  169.     if (canvasLoaded) {
  170.       that.phase = new Phase();
  171.       oldPhase = null;
  172.  
  173.       // mousewheel handler
  174.       var mousewheelHandler = function(evt) {
  175.         try {
  176.           updateModifiers(evt);
  177.           // Get direction of scroll (1 = down, -1 = up)
  178.           inputState.mouseWheel = (0 < (evt.detail ? evt.detail : -evt.wheelDelta) ? 1 : -1);
  179.           if (that.phase !== null) {
  180.             that.phase.mouseWheel(inputState);
  181.           }
  182.         } catch (err) {}
  183.         evt.preventDefault();
  184.       };
  185.       document.addEventListener("DOMMouseScroll", mousewheelHandler, false);
  186.       document.addEventListener("mousewheel", mousewheelHandler, false);
  187.  
  188.       // mousemove handler
  189.       window.addEventListener("mousemove", function(evt) {
  190.         updateModifiers(evt);
  191.         var canvasRect = canvas.getBoundingClientRect();
  192.         inputState.mousePos.x = Math.floor((inputState.pagePos.x = evt.pageX) - canvasRect.left);
  193.         inputState.mousePos.y = Math.floor((inputState.pagePos.y = evt.pageY) - canvasRect.top);
  194.  
  195.         if (evt.movementX !== 0) {
  196.           inputState.mouseMov.x = evt.movementX;
  197.           inputState.mouseMov.y = evt.movementY;
  198.         } else if (evt.mozMovementX !== 0) {
  199.           inputState.mouseMov.x = evt.mozMovementX;
  200.           inputState.mouseMov.y = evt.mozMovementY;
  201.         } else if (evt.webkitMovementX !== 0) {
  202.           inputState.mouseMov.x = evt.webkitMovementX;
  203.           inputState.mouseMov.y = evt.webkitMovementY;
  204.         }
  205.         if (that.phase !== null) {
  206.           that.phase.mouseMove(inputState);
  207.         }
  208.         evt.preventDefault();
  209.       }, false);
  210.  
  211.       // mousedown handler
  212.       canvas.addEventListener("mousedown", function(evt) {
  213.         updateModifiers(evt);
  214.         evt.preventDefault();
  215.         document.activeElement.blur();
  216.         inputState.mouseButton |= 1 << evt.button;
  217.         if (that.phase !== null) {
  218.           that.phase.mousePress(inputState);
  219.         }
  220.       }, false);
  221.  
  222.       // contextmenu handler
  223.       bodyElement.oncontextmenu = function() {
  224.         return contextMenuAllowed;
  225.       };
  226.  
  227.       // mouseup handler
  228.       window.addEventListener("mouseup", function(evt) {
  229.         updateModifiers(evt);
  230.         evt.preventDefault();
  231.         inputState.mouseButton &= ~(1 << evt.button);
  232.         if (that.phase !== null) {
  233.           that.phase.mouseRelease(inputState);
  234.         }
  235.       }, false);
  236.  
  237.       // keydown handler
  238.       document.addEventListener("keydown", function(evt) {
  239.         updateModifiers(evt);
  240.         inputState.keyCode = evt.keyCode;
  241.         // Prevent tab/backspace from doing their default actions
  242.         if (evt.keyCode === 8 || evt.keyCode === 9) {
  243.           evt.preventDefault();
  244.         }
  245.         if (that.phase !== null) {
  246.           that.phase.keyPress(inputState);
  247.         }
  248.       }, false);
  249.  
  250.       // keypress handler
  251.       document.addEventListener("keypress", function(evt) {
  252.         updateModifiers(evt);
  253.         inputState.keyCode = evt.keyCode;
  254.         inputState.charCode = evt.charCode;
  255.         if (that.phase !== null) {
  256.           that.phase.charTyped(inputState);
  257.         }
  258.         evt.preventDefault();
  259.       }, false);
  260.  
  261.       // keyup handler
  262.       document.addEventListener("keyup", function(evt) {
  263.         updateModifiers(evt);
  264.         inputState.keyCode = evt.keyCode;
  265.         if (that.phase !== null) {
  266.           that.phase.keyRelease(inputState);
  267.         }
  268.         evt.preventDefault();
  269.       }, false);
  270.       postInit();
  271.       that.update();
  272.     }
  273.   }
  274.  
  275.   /**
  276.    * Call onInit handlers and prevent further attempts to initialize.
  277.    */
  278.   function postInit() {
  279.     for (var i = 0; i < initHandlers.length; i++) {
  280.       try {
  281.         initHandlers[i](systemConfig);
  282.       } catch (err) {
  283.         that.err("postInit routine #" + i, err);
  284.       }
  285.     }
  286.     initDone = true;
  287.   }
  288.  
  289.   /**
  290.    * Execute a loop of the engine.
  291.    *
  292.    * @param {DOMHighResTimeStamp} timestamp - The current time.
  293.    * @callback Window.requestAnimationFrame
  294.    */
  295.   function mainLoop(timestamp) {
  296.     if (realtimeEnabled) {
  297.       // Schedule update ASAP
  298.       window.requestAnimationFrame(mainLoop);
  299.     }
  300.     var dt = 0;
  301.     if (prevTimestamp != 0) {
  302.       dt = timestamp - prevTimestamp;
  303.     }
  304.     prevTimestamp = timestamp;
  305.     try {
  306.       if (that.phase !== oldPhase) {
  307.         if (oldPhase !== null) {
  308.           oldPhase.end();
  309.         }
  310.         if (that.phase != null) {
  311.           that.phase.begin(oldPhase);
  312.           oldPhase = that.phase;
  313.           that.phase.wake();
  314.         }
  315.       }
  316.       if (that.phase != null) {
  317.         that.phase.update(dt);
  318.       }
  319.     } catch (err) {
  320.       that.err("mainLoop()", err);
  321.     }
  322.   }
  323.  
  324.   /** Skullcode version? */
  325.   this.version = 1408337099853;
  326.   /** @constant {number} */
  327.   this.TAU = 6.283185307179586;
  328.   /** @constant {number} */
  329.   this.RAD2DEG = 57.2957795131;
  330.   /** @constant {number} */
  331.   this.DEG2RAD = 0.01745329251;
  332.   /** @constant {number} */
  333.   this.VIEW2D = 0;
  334.   /** @constant {number} */
  335.   this.VIEW3D = 1;
  336.   /** @constant {number} */
  337.   this.NEAREST = 0;
  338.   /** @constant {number} */
  339.   this.LINEAR = 1;
  340.   /** @constant {number} */
  341.   this.DRAW = 1;
  342.   /** @constant {number} */
  343.   this.CACHE = 2;
  344.   /** @constant {number} */
  345.   this.DRAW_CACHE = this.DRAW | this.CACHE;
  346.  
  347.   /** Currently active phase. */
  348.   this.phase = null;
  349.  
  350.   var realtimeEnabled = true,
  351.     bodyElement = null,
  352.     oldPhase = null,
  353.     canvas = null,
  354.     initHandlers = [],
  355.     initDone = false,
  356.     canvasLoaded = false,
  357.     systemConfig = null,
  358.     intervalID = null,
  359.     inputState = new InputState(),
  360.     prevTimestamp = 0,
  361.     contextMenuAllowed = false;
  362.  
  363.   /**
  364.    * Set whether the context menu is enabled.
  365.    *
  366.    * @param {boolean} enable - If true, allow the context menu to appear.
  367.    */
  368.   this.allowContextMenu = function(enable) {
  369.     contextMenuAllowed = enable ? true : false;
  370.   };
  371.  
  372.   /**
  373.    * Log a given message (triggers a window.alert()).
  374.    *
  375.    * @param {string} msg - The message to log.
  376.    */
  377.   this.log = function(msg) {
  378.     window.alert(msg);
  379.   };
  380.  
  381.   /**
  382.    * Log a given error.
  383.    *
  384.    * @param {string} msg - A string describing the error.
  385.    * @param {exception} [exc] - An exception associated with the error.
  386.    */
  387.   this.err = function(msg, exc) {
  388.     try {
  389.       if (arguments.length < 2) {
  390.         this.log("ERROR: " + msg);
  391.       } else {
  392.         this.log("ERROR: " + msg + " : " + exc.toString() + " : line " +
  393.           exc.lineNumber + " of file: " + exc.fileName);
  394.       }
  395.     } catch (err) {}
  396.   };
  397.  
  398.   /**
  399.    * Log a given warning.
  400.    *
  401.    * @param {string} msg - A string describing the warning.
  402.    * @param {exception} [exc] - An exception associated with the warning.
  403.    */
  404.   this.warn = function(msg, exc) {
  405.     try {
  406.       if (arguments.length < 2) {
  407.         this.log("WARNING: " + msg);
  408.       } else {
  409.         this.log("WARNING: " + msg + " : " + exc.toString() + " : line " +
  410.           exc.lineNumber + " of file: " + exc.fileName);
  411.       }
  412.     } catch (err) {}
  413.   };
  414.  
  415.   /**
  416.    * Add a callback to the list of onInit handlers.
  417.    *
  418.    * @param {function(systemConfig)} f - The function to call on initialization.
  419.    */
  420.   this.onInit = function(f) {
  421.     if (!initDone) {
  422.       initHandlers.push(f);
  423.     } else {
  424.       this.err("Function not added via onInit(), already initialized");
  425.     }
  426.   };
  427.  
  428.   /**
  429.    * Configure and initialize the engine.
  430.    *
  431.    * @param {dictionary} config - The configuration of the engine.
  432.    *
  433.    * Supported configuration options are:
  434.    *   realtime - ?
  435.    *   canvas - ID/DOM object of canvas to render to.
  436.    */
  437.   this.start = function(config) {
  438.     if (systemConfig != null) {
  439.       this.err("start() called while engine is already running.");
  440.       return false;
  441.     }
  442.     systemConfig = {
  443.       realtime: true,
  444.       canvas: "viewport"
  445.     };
  446.     if (typeof config != "undefined" && config !== null) {
  447.       try {
  448.         systemConfig.realtime = config.realtime ? true : false;
  449.         if (typeof config.canvas != "undefined") {
  450.           systemConfig.canvas = config.canvas;
  451.         }
  452.       } catch (err) {
  453.         this.err("setup object invalid ", err);
  454.         return false;
  455.       }
  456.     }
  457.     return init();
  458.   };
  459.  
  460.   /**
  461.    * Enable or disable realtime execution.
  462.    *
  463.    * @param {boolean} enable - If true, VM updates will occur as soon as possible.
  464.    */
  465.   this.setRealtime = function(enable) {
  466.     if (enable && realtimeEnabled != enable) {
  467.       window.requestAnimationFrame(mainLoop);
  468.     }
  469.     realtimeEnabled = enable;
  470.   };
  471.  
  472.   /**
  473.    * Enable or disable image smoothing.
  474.    *
  475.    * @param {CanvasRenderingContext2D} context - The context to set image smoothing state on.
  476.    * @param {boolean} enable
  477.    */
  478.   this.scaleSmoothing = function(context, enable) {
  479.     enable = enable ? true : false;
  480.     context.imageSmoothingEnabled = enable;
  481.     context.mozImageSmoothingEnabled = enable;
  482.     context.webkitImageSmoothingEnabled = enable;
  483.   };
  484.  
  485.   /**
  486.    * Enter or leave full-screen mode.
  487.    *
  488.    * @param {boolean} enable
  489.    */
  490.   this.setFullScreen = function(enable) {
  491.     this.log("Setting screen mode: " + enable);
  492.     if (enable) {
  493.       if (canvas.requestFullscreen) {
  494.         canvas.requestFullscreen();
  495.       } else if (canvas.msRequestFullscreen) {
  496.         canvas.msRequestFullscreen();
  497.       } else if (canvas.mozRequestFullScreen) {
  498.         canvas.mozRequestFullScreen();
  499.       } else if (canvas.webkitRequestFullscreen) {
  500.         canvas.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
  501.       } else {
  502.         this.log("Fullscreen not supported.");
  503.       }
  504.     } else {
  505.       if (document.exitFullscreen) {
  506.         document.exitFullscreen();
  507.       } else if (document.msExitFullscreen) {
  508.         document.msExitFullscreen();
  509.       } else if (document.mozCancelFullScreen) {
  510.         document.mozCancelFullScreen();
  511.       } else if (document.webkitExitFullscreen) {
  512.         document.webkitExitFullscreen();
  513.       } else {
  514.         this.log("Fullscreen not supported.");
  515.       }
  516.     }
  517.   };
  518.  
  519.   /**
  520.    * Enable or disable pointer lock.
  521.    *
  522.    * @param {boolean} enable
  523.    */
  524.   this.setPointerLock = function(enable) {
  525.     this.log("Setting pointer lock: " + enable);
  526.     if (enable) {
  527.       if (canvas.requestPointerLock) {
  528.         canvas.requestPointerLock();
  529.       } else if (canvas.mozRequestPointerLock) {
  530.         canvas.mozRequestPointerLock();
  531.       } else if (canvas.webkitRequestPointerLock) {
  532.         canvas.webkitRequestPointerLock();
  533.       } else {
  534.         this.log("Pointer lock not supported.");
  535.       }
  536.     } else {
  537.       if (canvas.exitPointerLock) {
  538.         canvas.exitPointerLock();
  539.       } else if (canvas.mozExitPointerLock) {
  540.         canvas.mozExitPointerLock();
  541.       } else if (canvas.webkitExitPointerLock) {
  542.         canvas.webkitExitPointerLock();
  543.       } else {
  544.         this.log("Pointer lock not supported.");
  545.       }
  546.     }
  547.   };
  548.  
  549.   /**
  550.    * Run a single iteration of the main engine loop.
  551.    */
  552.   this.update = function() {
  553.     window.requestAnimationFrame(mainLoop);
  554.   };
  555.  
  556.   // Wait for canvas to load before initializing.
  557.   intervalID = window.setInterval(function preInit() {
  558.     try {
  559.       if (!bodyElement) {
  560.         bodyElement = document.getElementsByTagName("body")[0];
  561.       }
  562.       if (systemConfig !== null && canvas === null) {
  563.         if ("string" == typeof systemConfig.canvas) {
  564.           canvas = document.getElementById(systemConfig.canvas);
  565.           if (canvas) {
  566.             systemConfig.canvas = canvas;
  567.           }
  568.         } else {
  569.           canvas = systemConfig.canvas;
  570.         }
  571.       }
  572.       if (bodyElement && canvas) {
  573.         window.clearInterval(intervalID);
  574.         intervalID = null;
  575.         canvasLoaded = true;
  576.         if (systemConfig != null) {
  577.           // System is configured, make sure it is initialized.
  578.           init();
  579.         }
  580.       }
  581.     } catch (err) {
  582.       that.err("preInit() failed", err);
  583.     }
  584.   }, 250);
  585.  
  586.   /**
  587.    * Represents a point travelling on a linearly interpolated 1-D trajectory.
  588.    * The interpolation equation used is:
  589.    *   y(t) = t/MaxT
  590.    * where t starts at 0 and is clamped to the range [0, maxT].
  591.    *
  592.    * Effectively, this means the point travels at a constant speed defined by maxT.
  593.    *
  594.    * @member {number} value - The current position along the trajectory (normalized).
  595.    * @param {number} maxT - The end time of the trajectory.
  596.    */
  597.   function LinearInterp(maxT) {
  598.     var that = this,
  599.       t = that.value = 0;
  600.  
  601.     /**
  602.      * Advance the point along the trajectory.
  603.      *
  604.      * @param {number} dt - The time to advance the point by.
  605.      */
  606.     that.advance = function(dt) {
  607.       t += dt;
  608.  
  609.       // Clamp to [0, maxT]
  610.       if (t <= 0) {
  611.         that.value = t = 0;
  612.       } else if (t >= maxT) {
  613.         t = maxT;
  614.         that.value = 1;
  615.       } else {
  616.         that.value = t / maxT;
  617.       }
  618.       return that.value;
  619.     };
  620.  
  621.     /**
  622.      * Reset the point to the start of the trajectory.
  623.      *
  624.      * @param {number} [newMaxT] - A new end time for the trajectory.
  625.      */
  626.     that.reset = function(newMaxT) {
  627.       t = that.value = 0;
  628.       if (arguments.length > 0) {
  629.         maxT = newMaxT;
  630.       }
  631.     };
  632.   }
  633.   this.LinearInterp = LinearInterp;
  634.  
  635.   /**
  636.    * @name Vector3
  637.    * Represents a 3-vector.
  638.    * @constructor
  639.    *
  640.    * Initialized to (0,0,0).
  641.    */
  642.   /**
  643.    * @name Vector3
  644.    * Copies a 3-vector.
  645.    * @constructor
  646.    *
  647.    * @param {Vector3} x - The vector to copy coordinates from.
  648.    */
  649.   /**
  650.    * Represents a 3-vector.
  651.    *
  652.    * @constructor
  653.    * @param {number} x - The initial x-coordinate of the vector.
  654.    * @param {number} y - The initial y-coordinate of the vector.
  655.    * @param {number} z - The initial z-coordinate of the vector.
  656.    */
  657.   function Vector3(x, y, z) {
  658.     var that = this;
  659.     if (arguments.length >= 2) {
  660.       this.x = x;
  661.       this.y = y;
  662.       this.z = z;
  663.     } else if (arguments.length == 1) {
  664.       this.x = x.x;
  665.       this.y = x.y;
  666.       this.z = x.z;
  667.     } else {
  668.       this.x = this.y = this.z = 0;
  669.     }
  670.     this.toString = function() {
  671.       return "{x:" + that.x + ",y:" + that.y + ",z:" + that.z + "}";
  672.     };
  673.   }
  674.   this.Vector3 = Vector3;
  675.  
  676.   /**
  677.    * Represents a linked list of objects.
  678.    * @constructor
  679.    *
  680.    * Note: 'prior' and 'next' properties are added to objects that are
  681.    * added to this linked list, and can be used to navigate it.
  682.    */
  683.   this.LinkedList = function() {
  684.     var _first = null,
  685.       _last = null;
  686.  
  687.     /** @returns {object} The first object in the list. */
  688.     this.first = function() {
  689.       return _first;
  690.     };
  691.  
  692.     /** @returns {object} The last object in the list. */
  693.     this.last = function() {
  694.       return _last;
  695.     };
  696.  
  697.     /**
  698.      * Add an object to the end of the list.
  699.      *
  700.      * @param {object} elem - The object to add.
  701.      */
  702.     this.add = function(elem) {
  703.       if (_first == null) {
  704.         _first = elem;
  705.       } else {
  706.         _last.next = elem;
  707.       }
  708.       elem.prior = _last;
  709.       _last = elem;
  710.     };
  711.  
  712.     /**
  713.      * Remove an object from the list (next and prior are set to null).
  714.      *
  715.      * @param {object} elem - The object to remove.
  716.      */
  717.     this.unlink = function(elem) {
  718.       if (elem.next == null) {
  719.         _last = elem.prior;
  720.       } else {
  721.         elem.next.prior = elem.prior;
  722.       }
  723.       if (elem.prior == null) {
  724.         _first = elem.next;
  725.       } else {
  726.         elem.prior.next = elem.next;
  727.       }
  728.       elem.prior = elem.next = null;
  729.     };
  730.  
  731.     /**
  732.      * Insert an object after an element.
  733.      *
  734.      * @param {object} elem - The object to insert.
  735.      * @param {object} [target] - The object to insert before (defaults to this.first)
  736.      */
  737.     this.insert = function(elem, target) {
  738.       if (null == target) {
  739.         target = _first;
  740.       }
  741.       if (null == target) {
  742.         _last = _first = elem;
  743.       } else {
  744.         if (target === _first) {
  745.           _first = elem;
  746.         } else {
  747.           target.prior.next = elem;
  748.         }
  749.         elem.prior = target.prior;
  750.         target.prior = elem;
  751.         elem.next = target;
  752.       }
  753.     };
  754.  
  755.     /**
  756.      * Clear the contents of this list (note: does not reset elements).
  757.      */
  758.     this.empty = function() {
  759.       _first = _last = null;
  760.     };
  761.  
  762.     /**
  763.      * Attach a list to the end of this list.
  764.      *
  765.      * @param {LinkedList} list - The list to attach (will be emptied afterwards).
  766.      */
  767.     this.attach = function(list) {
  768.       if (_first == null) {
  769.         _first = list.first();
  770.         _last = list.last();
  771.       } else {
  772.         _last.next = list.first();
  773.         if (_last.next != null) {
  774.           _last.next.prior = _last;
  775.           _last = list.last();
  776.         }
  777.       }
  778.       list.empty();
  779.     };
  780.   };
  781.  
  782.   /**
  783.    * Represents a linear-congruential pseudo-random number generator (LCPRNG).
  784.    * @constructor
  785.    *
  786.    * Note: This doesn't actually appear to be an LCPRNG, instead it populates
  787.    * a table using the original MINSTD PRNG then uses those numbers to generate
  788.    * the output stream according to X_n = X_{n+1} + X_{n+30} (for the default
  789.    * tblSize of 32 this is equivalent to X_n = X_{n-31} + X_{n-2}).
  790.    *
  791.    * @param {number} [seed=1] - Seed value used to populate table.
  792.    * @param {number} [tblSize=32] - Size of table.
  793.    */
  794.   this.LCPRNG = function(seed, tblSize) {
  795.     var that = this,
  796.       tbl = [],
  797.       b = 0;
  798.  
  799.     /**
  800.      * @function seed
  801.      * Get the seed value for this PRNG.
  802.      * @returns {number} The current PRNG seed.
  803.      */
  804.     /**
  805.      * Set the seed value for this PRNG and reinitialize it.
  806.      *
  807.      * @param {number} newSeed - New seed value to use.
  808.      * @returns {number} The new PRNG seed.
  809.      */
  810.     that.seed = function(newSeed) {
  811.       if (arguments.length > 0) {
  812.         tbl[0] = seed = newSeed | 0;
  813.         for (var g = tblSize - 1, d = 1; d < g; ++d) {
  814.           tbl[d] = 16807 * tbl[d - 1] & 0x7FFFFFFF;
  815.         }
  816.         tbl[g] = tbl[0];
  817.         tbl[0] = tbl[1];
  818.         tbl[1] = tbl[2];
  819.         b = 2;
  820.         g = tblSize << 4;
  821.         for (d = 0; d < g; ++d) {
  822.           that.rand();
  823.         }
  824.       }
  825.       return seed;
  826.     };
  827.  
  828.     /**
  829.      * Generate a new random number.
  830.      * @returns {number} A new random number.
  831.      */
  832.     that.rand = function() {
  833.       var e = b;
  834.       b = (b + 1) % tblSize;
  835.       tbl[e] = tbl[b] + tbl[(b + 29) % tblSize] >> 0;
  836.       return tbl[e];
  837.     };
  838.  
  839.     /**
  840.      * Return a string representation of this PRNG.
  841.      *
  842.      * Note: Actually returns a number.
  843.      *
  844.      * @returns {number} The seed of the PRNG.
  845.      */
  846.     that.toString = function() {
  847.       return that.seed();
  848.     };
  849.  
  850.     // Set defaults
  851.     if (arguments.length < 1) {
  852.       seed = 1;
  853.     }
  854.     if (arguments.length < 2) {
  855.       tblSize = 32;
  856.     }
  857.     // Initialize PRNG.
  858.     that.seed(seed);
  859.   };
  860.  
  861.   /**
  862.    * Represents a point travelling on a sine-interpolated 1-D trajectory.
  863.    * The interpolation equation used is:
  864.    *   y(t) = sin((pi/2) * (t/MaxT))
  865.    * where t starts at 0 and is clamped to the range [0, maxT].
  866.    *
  867.    * Effectively, this means the point starts fast then slows down until it reaches half of maxT,
  868.    * before speeding up as it approaches maxT.
  869.    *
  870.    * @member {number} value - The current position along the trajectory (normalized).
  871.    * @param {number} maxT - The end point of the trajectory.
  872.    */
  873.   this.SineInterp = function(maxT) {
  874.     var that = this;
  875.     that.value = 0;
  876.     var linearInterp = new LinearInterp(maxT),
  877.       c = Math.PI / 2;
  878.  
  879.     /**
  880.      * Advance the point along the trajectory.
  881.      *
  882.      * @param {number} dt - The time to advance the point by.
  883.      */
  884.     that.advance = function(dt) {
  885.       dt = linearInterp.advance(dt);
  886.       if (dt == 1) {
  887.         that.value = 1;
  888.       } else if (dt == 0) {
  889.         that.value = 0;
  890.       } else {
  891.         that.value = Math.sin(dt * c);
  892.       }
  893.       return that.value;
  894.     };
  895.  
  896.     /**
  897.      * Reset the point to the start of the trajectory.
  898.      *
  899.      * @param {number} [newMaxT] - A new end time for the trajectory.
  900.      */
  901.     that.reset = function(newMaxT) {
  902.       that.value = 0;
  903.       if (arguments.length > 0) {
  904.         linearInterp.reset(newMaxT);
  905.       } else {
  906.         linearInterp.reset();
  907.       }
  908.     };
  909.   };
  910.  
  911.   /**
  912.    * Represents a point travelling on a cosine-interpolated 1-D trajectory.
  913.    * The interpolation equation used is:
  914.    *   y(t) = 0.5*(1-cos(pi * (t/MaxT)))
  915.    * where t starts at 0 and is clamped to the range [0, maxT].
  916.    *
  917.    * Effectively, this means the point starts slow and first speeds up
  918.    * until it reaches maxT/4, slows down as it approaches maxT/2, speeds
  919.    * up again as it reaches 3*maxT/4 and finally slows to a stop at maxT.
  920.    *
  921.    * @member {number} value - The current position along the trajectory (normalized).
  922.    * @param {number} maxT - The end point of the trajectory.
  923.    */
  924.   this.CosineInterp = function(maxT) {
  925.     var that = this;
  926.     that.value = 0;
  927.     var linearInterp = new LinearInterp(maxT),
  928.       c = Math.PI;
  929.  
  930.     /**
  931.      * Advance the point along the trajectory.
  932.      *
  933.      * @param {number} dt - The time to advance the point by.
  934.      */
  935.     that.advance = function(dt) {
  936.       dt = linearInterp.advance(dt);
  937.       if (dt == 1) {
  938.         that.value = 1;
  939.       } else if (dt == 0) {
  940.         that.value = 0;
  941.       } else {
  942.         that.value = 0.5 * (1 - Math.cos(dt * c));
  943.       }
  944.       return that.value;
  945.     };
  946.  
  947.     /**
  948.      * Reset the point to the start of the trajectory.
  949.      *
  950.      * @param {number} [newMaxT] - A new end time for the trajectory.
  951.      */
  952.     that.reset = function(newMaxT) {
  953.       that.value = 0;
  954.       if (arguments.length > 0) {
  955.         linearInterp.reset(newMaxT);
  956.       } else {
  957.         linearInterp.reset();
  958.       }
  959.     };
  960.   };
  961.  
  962.   /**
  963.    * @name Box3
  964.    * Represents a 3D box, defined by two coordinate vectors.
  965.    * @constructor
  966.    *
  967.    * Initialized to {a=(0,0,0), b=(0,0,0)}.
  968.    */
  969.   /**
  970.    * @name Box3
  971.    * Copies a 3D box.
  972.    * @constructor
  973.    *
  974.    * @param {Box3} aX - The vector to copy coordinates from.
  975.    */
  976.   /**
  977.    * @name Box3
  978.    * Represents a 3D box, defined by two coordinate vectors.
  979.    *
  980.    * @constructor
  981.    * @param {Vector3} aX - A vector (a) defining a corner of the box.
  982.    * @param {Vector3} aY - A vector (b) defining the corner of the box opposite to a.
  983.    */
  984.   /**
  985.    * Represents a 3D box, defined by two coordinate vectors.
  986.    *
  987.    * @constructor
  988.    * @param {number} aX - The initial x-coordinate of the a vector.
  989.    * @param {number} aY - The initial y-coordinate of the a vector.
  990.    * @param {number} aZ - The initial z-coordinate of the a vector.
  991.    * @param {number} bX - The initial x-coordinate of the b vector.
  992.    * @param {number} bY - The initial y-coordinate of the b vector.
  993.    * @param {number} bZ - The initial z-coordinate of the b vector.
  994.    */
  995.   this.Box3 = function(aX, aY, aZ, bX, bY, bZ) {
  996.     var that = this;
  997.     if (arguments.length >= 5) {
  998.       // aX-aZ = a coords, bX-bZ = b coords
  999.       that.a = new Vector3(aX, aY, aZ);
  1000.       that.b = new Vector3(bX, bY, bZ);
  1001.     } else if (arguments.length > 1) {
  1002.       // aX = Vector3, aY = Vector3
  1003.       that.a = new Vector3(aX);
  1004.       that.b = new Vector3(aY);
  1005.     } else if (arguments.length == 1) {
  1006.       // aX = Box3
  1007.       that.a = new Vector3(aX.a);
  1008.       that.b = new Vector3(aX.b);
  1009.     } else {
  1010.       that.a = new Vector3();
  1011.       that.b = new Vector3();
  1012.     }
  1013.     this.toString = function() {
  1014.       return "{a:" + that.a + ",b:" + that.b + "}";
  1015.     };
  1016.   };
  1017.  
  1018.   /**
  1019.    * Calculates the cross product of two Vector3 instances.
  1020.    *
  1021.    * @param {Vector3} a - The left parameter to the cross product.
  1022.    * @param {Vector3} b - The right parameter to the cross product.
  1023.    * @param {Vector3} c - The Vector3 to set to the result.
  1024.    */
  1025.   this.cross = function(a, b, c) {
  1026.     c.x = a.y * b.z - a.z * b.y;
  1027.     c.y = a.z * b.x - a.x * b.z;
  1028.     c.z = a.x * b.y - a.y * b.x;
  1029.   };
  1030.  
  1031.   var isLocal = "file:" == window.location.protocol;
  1032.  
  1033.   /**
  1034.    * Called when a remote object has been fetched.
  1035.    *
  1036.    * @callback System~fetchObjectCallback
  1037.    * @param {object} obj - The fetched object, or null if an error occurred.
  1038.    */
  1039.   /**
  1040.    * Fetches a remote script and executes it.
  1041.    *
  1042.    * If Skullcode is running remotely, this uses an XMLHttpRequest to
  1043.    * download the script and evals it to get the fetched object.
  1044.    *
  1045.    * If Skullcode is running locally, this uses a script tag to source the
  1046.    * script and gets the fetched object from the _JSE_ variable.
  1047.    *
  1048.    * @param {string} fileName - The filename of the script to load.
  1049.    * @param {System~fetchObjectCallback} callback - A callback to pass the fetched object to.
  1050.    */
  1051.   function fetchObject(fileName, callback) {
  1052.     function handleXMLHttpRequest(xhr_param) {
  1053.       if (!xhr_param.isLoaded) {
  1054.         xhr_param.isLoaded = true;
  1055.         try {
  1056.           var obj = eval(xhr.responseText);
  1057.           callback(obj);
  1058.         } catch (err) {
  1059.           callback(null);
  1060.         }
  1061.       }
  1062.     }
  1063.     var bodyElement = document.getElementsByTagName("body")[0];
  1064.     if (!bodyElement) {
  1065.       return false;
  1066.     }
  1067.     if (isLocal) {
  1068.       _JSE_ = null;
  1069.       var scriptElement = document.createElement("script");
  1070.       scriptElement.onload = function() {
  1071.         callback(_JSE_);
  1072.         bodyElement.removeChild(scriptElement);
  1073.       };
  1074.       scriptElement.onerror = function() {
  1075.         that.log("fetchObj() failed to load: " + fileName);
  1076.         bodyElement.removeChild(scriptElement);
  1077.         callback(null);
  1078.       };
  1079.       scriptElement.src = fileName;
  1080.       bodyElement.appendChild(scriptElement);
  1081.     } else {
  1082.       var xhr = new XMLHttpRequest();
  1083.       xhr.isLoaded = false;
  1084.       xhr.onreadystatechange = function() {
  1085.         if (xhr.readyState === 4) {
  1086.           handleXMLHttpRequest(xhr);
  1087.         }
  1088.       };
  1089.       xhr.onload = function() {
  1090.         handleXMLHttpRequest(xhr);
  1091.       };
  1092.       xhr.ontimeout = function() {
  1093.         that.log("fetchObj() failed to download: " + fileName);
  1094.         callback(null);
  1095.       };
  1096.       xhr.open("GET", fileName, true);
  1097.       xhr.send(null);
  1098.     }
  1099.   }
  1100.   this.fetchObject = fetchObject;
  1101.  
  1102.   /**
  1103.    * Called when a queued image has been loaded.
  1104.    *
  1105.    * @callback ImageLoader~imageLoadedCallback
  1106.    * @param {Image} img - The fetched image.
  1107.    * @param {boolean} loaded - True if the image loaded, false if an error occurred.
  1108.    * @param {string} filename - The filename of the fetched image.
  1109.    * @param {object} param - The parameter passed to ImageLoader~queue.
  1110.    */
  1111.   /**
  1112.    * Loads images, asynchronously.
  1113.    * @constructor
  1114.    *
  1115.    * @param {string} [basePath=""] - A base path to prepend to image filenames.
  1116.    * @param {ImageLoader~imageLoadedCallback} [callback] - A callback to pass the loaded image to.
  1117.    * @member {number} loaded - The number of images which have successfully loaded.
  1118.    * @member {number} fails - The number of images which have failed to load.
  1119.    * @member {number} count - The number of images that have been queued to load.
  1120.    */
  1121.   this.ImageLoader = function(basePath, callback) {
  1122.     this.fails = this.loaded = this.count = 0;
  1123.  
  1124.     /**
  1125.      * Queues an image to be loaded.
  1126.      * Upon completion, the callback supplied to the class
  1127.      * constructor will be called with the image filename and parameter.
  1128.      *
  1129.      * @param {string} filename - The image filename.
  1130.      * @param {object} param - The parameter to pass to the callback.
  1131.      */
  1132.     this.queue = function(filename, param) {
  1133.       var img = new Image();
  1134.       filename = basePath + filename;
  1135.       img.onload = function() {
  1136.         var fn = filename;
  1137.         try {
  1138.           that.loaded++;
  1139.           if (callback) {
  1140.             callback(img, true, fn, param);
  1141.           }
  1142.         } catch (err) {}
  1143.       };
  1144.       img.onerror = function() {
  1145.         var fn = filename;
  1146.         try {
  1147.           that.fails++;
  1148.           if (callback) {
  1149.             callback(img, false, fn, param);
  1150.           }
  1151.         } catch (err) {}
  1152.       };
  1153.       img.src = filename;
  1154.       imageQueue[imageQueue.length] = img;
  1155.       that.count = imageQueue.length;
  1156.     };
  1157.  
  1158.     var that = this,
  1159.       imageQueue = [];
  1160.  
  1161.     // Default to empty basePath
  1162.     if (basePath == 0) {
  1163.       basePath = "";
  1164.     }
  1165.   };
  1166.  
  1167.   /**
  1168.    * Represents an 'asset'.
  1169.    *
  1170.    * Note if no asset ID is supplied, one will be generated automatically
  1171.    * and bits 0 and 2 of Asset~flag will be set.
  1172.    *
  1173.    * Asset~flag is a bitmap, defined as follows:
  1174.    *  bit 0: set if asset is loaded
  1175.    *  bit 1: set if asset is stowed
  1176.    *  bit 2: set if asset has been updated.
  1177.    *
  1178.    * Assets are automatically added to the 'assets' free variable.
  1179.    *
  1180.    * @param {string} [type="unknown"] - Type of asset.
  1181.    * @param {string} [name=""] - Name of asset.
  1182.    * @param {string} [path=""] - Path to asset.
  1183.    * @param {number} [flag=0] - Asset flags.
  1184.    * @param {number} [id] - Unique asset ID.
  1185.    * @param {number} [rev=0] - Asset revision?
  1186.    */
  1187.   this.Asset = function(type, name, path, flag, id, rev) {
  1188.     /**
  1189.      * Gets/sets the value of a set of flags.
  1190.      *
  1191.      * @param {boolean|null} val - The value to set the flags to (if null, change nothing).
  1192.      * @param {number} mask - A bitmask indicating which flags to read/write.
  1193.      * @returns {boolean} True if any of the masked flags were set.
  1194.      */
  1195.     function setflag(val, mask) {
  1196.       if (val != null) {
  1197.         if (val) {
  1198.           that.flag = that.flag | mask;
  1199.         } else {
  1200.           that.flag = that.flag & ~mask;
  1201.         }
  1202.       }
  1203.       if (that.flag & mask > 0) {
  1204.         return true;
  1205.       } else {
  1206.         return false;
  1207.       }
  1208.     }
  1209.  
  1210.     var that = this;
  1211.     /** Type of asset. */
  1212.     that.type = type ? type : "unknown";
  1213.     /** Name of asset. */
  1214.     that.name = name ? name : "";
  1215.     /** Path to asset. */
  1216.     that.path = path ? path : "";
  1217.     /** Asset flags. */
  1218.     that.flag = flag ? flag : 0;
  1219.     /** Asset revision? */
  1220.     that.rev = rev ? rev : 0;
  1221.     if (arguments.length >= 5) {
  1222.       /** Asset ID */
  1223.       that.id = id;
  1224.     } else {
  1225.       that.id = assets.length;
  1226.       that.flag |= 6;
  1227.     }
  1228.     assets[that.id] = that;
  1229.  
  1230.     /**
  1231.      * Get/set the asset loaded flag.
  1232.      *
  1233.      * @param {boolean|null} val - The value to set the flag to (if null, change nothing).
  1234.      * @returns {boolean} True if the flag was set, otherwise false.
  1235.      */
  1236.     that.loaded = function(val) {
  1237.       return setflag(1 > arguments.length ? null : val, 1);
  1238.     };
  1239.  
  1240.     /**
  1241.      * Get/set the asset stowed flag.
  1242.      *
  1243.      * @param {boolean|null} val - The value to set the flag to (if null, change nothing).
  1244.      * @returns {boolean} True if the flag was set, otherwise false.
  1245.      */
  1246.     that.stow = function(val) {
  1247.       return setflag(1 > arguments.length ? null : val, 2);
  1248.     };
  1249.  
  1250.     /**
  1251.      * Get/set the asset updated flag.
  1252.      *
  1253.      * @param {boolean|null} val - The value to set the flag to (if null, change nothing).
  1254.      * @returns {boolean} True if the flag was set, otherwise false.
  1255.      */
  1256.     that.updated = function(val) {
  1257.       return setflag(1 > arguments.length ? null : val, 4);
  1258.     };
  1259.  
  1260.     /**
  1261.      * Unknown, toString implied that this returns null or a string
  1262.      * suitable for insertion in a JSON object?
  1263.      */
  1264.     that.customJSE = function() {
  1265.       return null;
  1266.     };
  1267.  
  1268.     /**
  1269.      * Serialize the Asset to a JSON string.
  1270.      *
  1271.      * @returns {string} A JSON representation of this Asset suitable.
  1272.      */
  1273.     that.toString = function() {
  1274.       var str = that.customJSE(),
  1275.         prevFlags = that.flags;
  1276.       that.loaded(false);
  1277.       that.updated(false);
  1278.       str = "{\tid :\t" + that.id +
  1279.         ",\n\trev :\t" + that.rev +
  1280.         ",\n\tflag :\t" + that.flag +
  1281.         ",\n\ttype :\t" + JSON.stringify(that.type) +
  1282.         ",\n\tname :\t" + JSON.stringify(that.name) +
  1283.         ",\n\tpath :\t" + JSON.stringify(that.path) +
  1284.         (null == str ? "" : ",\n\t" + str) + "\n}";
  1285.       that.flags = prevFlags;
  1286.       return str;
  1287.     };
  1288.   };
  1289.  
  1290.   var sys = this; // Shortcut to allow AssetManager to log to System.log
  1291.   /**
  1292.    * Manages storage and retrieval of Assets.
  1293.    *
  1294.    * @param {string} [assetPath=""] - Base path for remotely loaded assets
  1295.    */
  1296.   this.AssetManager = function(assetPath) {
  1297.     var that = this,
  1298.       managedAssets = {},
  1299.       fetchWaiters = {},
  1300.       assetConstructors = {};
  1301.     assetPath = arguments.length < 1 ? "" : assetPath.toString();
  1302.  
  1303.     /**
  1304.      * Called to create an Asset from it's stored representation.
  1305.      *
  1306.      * @callback AssetManager~assetConstructor
  1307.      * @param {object} data - The JSON object to reconstruct the asset from.
  1308.      * @param {string} path - The path to the asset.
  1309.      */
  1310.     /**
  1311.      * Register a constructor for a type of Asset.
  1312.      *
  1313.      * @param {string} type - The type to register the constructor under.
  1314.      * @param {AssetManager~assetConstructor} cons - The constructor to register.
  1315.      */
  1316.     that.register = function(id, cons) {
  1317.       assetConstructors[id] = cons;
  1318.     };
  1319.  
  1320.  
  1321.     /**
  1322.      * Store all managed Assets to local or remote storage.
  1323.      *
  1324.      * @param {boolean} partial - True if only assets with the 'updated' flag set should be stored.
  1325.      * @param {AssetManager~storeCallback} callback - The callback to call after each store completes.
  1326.      * @param {boolean} remote - If true, the assets will be stored remotely, otherwise localStorage will be used.
  1327.      */
  1328.     that.storeAll = function(partial, callback, remote) {
  1329.       var count = 0,
  1330.         id;
  1331.       for (id in managedAssets) {
  1332.         var data = managedAssets[id];
  1333.         /*
  1334.          * This block replaces the following expression:
  1335.          *    null == h || b && "function" == typeof h.updated && !h.updated() || (++f, a.store(h, c, d))
  1336.          * The original expression is rather confusing, so the process
  1337.          * of deobfuscation has been recorded below to help detect any mistakes.
  1338.          *
  1339.          * Logical AND has higher precedence than OR, so an equivalent expression is:
  1340.          *    null == h || (b && "function" == typeof h.updated && !h.updated()) || (++f, a.store(h, c, d))
  1341.          * Logical operators are left-associative:
  1342.          *    (null == h || (b && "function" == typeof h.updated && !h.updated())) || (++f, a.store(h, c, d))
  1343.          * Short-circuit evaluation means we can convert this to an 'if' statement:
  1344.          *    if (!(null == h || (b && "function" == typeof h.updated && !h.updated()))) {
  1345.          *      (++f, a.store(h, c, d))
  1346.          *    }
  1347.          * Applying De Morgan's law:
  1348.          *    if (null != h && !(b && "function" == typeof h.updated && !h.updated())) {
  1349.          *      (++f, a.store(h, c, d))
  1350.          *    }
  1351.          * Applying short-circuit evaluation again:
  1352.          *    if (null != h) {
  1353.          *      if (!(b && "function" == typeof h.updated && !h.updated())) {
  1354.          *        (++f, a.store(h, c, d))
  1355.          *      }
  1356.          *    }
  1357.          * Applying De Morgan's law again:
  1358.          *    if (null != h) {
  1359.          *      if (!b || "function" != typeof h.updated || h.updated()) {
  1360.          *        (++f, a.store(h, c, d))
  1361.          *      }
  1362.          *    }
  1363.          * Assuming typeof has no side-effects (since h != null), we
  1364.          * can group the latter two sub-expressions:
  1365.          *    if (null != h) {
  1366.          *      if (!b || ("function" != typeof h.updated || h.updated())) {
  1367.          *        (++f, a.store(h, c, d))
  1368.          *      }
  1369.          *    }
  1370.          * Applying De Morgan's law one last time:
  1371.          *    if (null != h) {
  1372.          *      if (!b || ("function" == typeof h.updated && h.updated())) {
  1373.          *        (++f, a.store(h, c, d))
  1374.          *      }
  1375.          *    }
  1376.          * This can then be rearranged to get the final expression below.
  1377.          */
  1378.         if (data != null) {
  1379.           if (!partial || ("function" == typeof data.updated && data.updated())) {
  1380.             count++;
  1381.             that.store(data, callback, remote);
  1382.           }
  1383.         }
  1384.       }
  1385.       return count;
  1386.     };
  1387.  
  1388.     /**
  1389.      * Called when an Asset has been stored.
  1390.      *
  1391.      * @callback AssetManager~storeCallback
  1392.      * @param {Asset} data - The stored asset.
  1393.      * @param {number} status - 1 on success, 0 on failure.
  1394.      */
  1395.     /**
  1396.      * Store an Asset to local or remote storage.
  1397.      *
  1398.      * Note: Remote storage is unimplemented.
  1399.      * Local storage stores assets according to their path fields.
  1400.      *
  1401.      * Assets are serialized using the toString function.
  1402.      *
  1403.      * @param {Asset} data - The asset to store.
  1404.      * @param {AssetManager~storeCallback} callback - The callback to call after the store completes.
  1405.      * @param {boolean} remote - If true, the asset will be stored remotely, otherwise localStorage will be used.
  1406.      */
  1407.     that.store = function(data, callback, remote) {
  1408.       if (remote) {
  1409.         sys.log("Error: AssetManager.store(): Remote storage unimplemented.", "#911");
  1410.         try {
  1411.           callback(data, 0);
  1412.         } catch (err) {
  1413.           sys.log("Error: AssetManager.store() callback(data,0): " + err);
  1414.         }
  1415.       } else if (localStorage) {
  1416.         try {
  1417.           localStorage.setItem(data.path, "_JSE_=" + data.toString());
  1418.           try {
  1419.             callback(data, 1);
  1420.           } catch (err) {
  1421.             sys.log("Error: AssetManager.store() callback(data,1): " + err);
  1422.           }
  1423.         } catch (err) {
  1424.           sys.log("Error: AssetManager.store() localStorage.setItem(): " + err);
  1425.           try {
  1426.             callback(data, 0);
  1427.           } catch (err2) {
  1428.             sys.log("Error: AssetManager.store() callback(data,0): " + err2);
  1429.           }
  1430.         }
  1431.       } else {
  1432.         sys.log("Error: AssetManager.store(): No localStorage");
  1433.         try {
  1434.           callback(data, 0);
  1435.         } catch (err2) {
  1436.           sys.log("Error: AssetManager.store() callback(data,0): " + err2);
  1437.         }
  1438.       }
  1439.     };
  1440.  
  1441.     /**
  1442.      * Called when a fetched asset becomes available.
  1443.      *
  1444.      * @callback AssetManager~fetchCallback
  1445.      * @param {object} obj - The fetched asset, or null if the fetch failed.
  1446.      * @param {string} path - The path to the fetched asset.
  1447.      */
  1448.     /**
  1449.      * Fetch an asset.
  1450.      *
  1451.      * This function searches for the asset in the following places, ordered by search order:
  1452.      *  1) Currently loaded/managed assets
  1453.      *  2) Browser LocalStorage
  1454.      *  3) Remote host (via fetchObject)
  1455.      *
  1456.      * @param {string} path - Path to asset.
  1457.      * @param {AssetManager~fetchCallback} callback - The callback to call when the asset is available.
  1458.      * @param {boolean} [asap=false] - If true, prevent the callback from being queued after other callbacks.
  1459.      *
  1460.      * @returns {number}
  1461.      *  -1 if callback triggered an error
  1462.      *   0 if asset was already loaded (callback was called)
  1463.      *   1 if asset retrieved from local storage (callback was called)
  1464.      *   2 if asset is being fetched (callback will be called when asset is available)
  1465.      *   3 if callback was queued (callback will be called after a previous call to fetch for this asset resolves).
  1466.      *   The value of asap, if callback would have been queued but asap was true (callback will not be called).
  1467.      *
  1468.      * If asap is true, the callback will not be queued if a fetch is in progress. This ensures that the callback
  1469.      * will either be called as soon as the asset is available (immediately if it is already loaded or before any
  1470.      * other callbacks if it is being fetched asynchronously) or not at all.
  1471.      */
  1472.     that.fetch = function(path, callback, asap) {
  1473.       if (arguments.length < 3) {
  1474.         asap = false;
  1475.       }
  1476.       var asset = managedAssets[path];
  1477.       // Fetch asset from cache
  1478.       if (asset) {
  1479.         try {
  1480.           sys.log("AM: already loaded: " + path);
  1481.           callback(asset, path);
  1482.         } catch (err) {
  1483.           sys.log("AssetManager callback error '" + assetPath + path + "': " + err, "#911");
  1484.           return -1;
  1485.         }
  1486.         return 0;
  1487.       }
  1488.  
  1489.       // Check for existing waiters (lists of callbacks) for this path
  1490.       var callbackList = fetchWaiters[path];
  1491.       if (callbackList) {
  1492.         if (asap) {
  1493.           return asap;
  1494.         } else {
  1495.           callbackList.push(func); // possible bug, should be callback?
  1496.           return 3;
  1497.         }
  1498.       }
  1499.       var callbackList = fetchWaiters[path] = [callback],
  1500.         am_callback = function(obj) {
  1501.           var i;
  1502.           if (obj == null) {
  1503.             // Object load failed
  1504.             for (i in callbackList) {
  1505.               try {
  1506.                 callbackList[i](null, path);
  1507.               } catch (err) {
  1508.                 sys.log("AssetManager callback error '" + assetPath + path + "': " + err, "#911");
  1509.               }
  1510.             }
  1511.           } else {
  1512.             // If the object is an asset with a constructor, run the constructor first.
  1513.             if (obj.type) {
  1514.               var assetConstructor = assetConstructors[obj.type];
  1515.               if (assetConstructor) {
  1516.                 try {
  1517.                   obj = assetConstructor(obj, path);
  1518.                 } catch (err) {
  1519.                   sys.log("AssetManager creation error '" + assetPath + path + "': " + err, "#911");
  1520.                   for (i in callbackList) {
  1521.                     try {
  1522.                       var cb = callbackList[i];
  1523.                       if (cb) {
  1524.                         cb(null, path);
  1525.                       }
  1526.                     } catch (err2) {
  1527.                       sys.log("AssetManager callback error '" + assetPath + path + "': " + err2, "#911");
  1528.                     }
  1529.                   }
  1530.                   delete fetchWaiters[path];
  1531.                   return;
  1532.                 }
  1533.               }
  1534.             }
  1535.  
  1536.             // Add object to list of managed assets and call callbacks
  1537.             managedAssets[path] = obj;
  1538.             for (i in callbackList) {
  1539.               try {
  1540.                 cb = callbackList[i];
  1541.                 if (cb) {
  1542.                   cb(obj, path);
  1543.                 }
  1544.               } catch (err) {
  1545.                 sys.log("AssetManager callback error '" + assetPath + path + "': " + err, "#911");
  1546.               }
  1547.             }
  1548.           }
  1549.  
  1550.           delete fetchWaiters[path];
  1551.         };
  1552.  
  1553.       // Fetch asset from local storage
  1554.       if (localStorage) {
  1555.         asset = localStorage.getItem(path);
  1556.         if (asset != null) {
  1557.           try {
  1558.             am_callback(eval(asset));
  1559.             return 1;
  1560.           } catch (err) {
  1561.             sys.log("AssetManager localStorage eval() error '" + path + "': " + err + "\n" + asset.toString(), "#911");
  1562.           }
  1563.         }
  1564.       }
  1565.  
  1566.       // Fetch asset from remote host
  1567.       fetchObject(assetPath + path, am_callback);
  1568.       return 2;
  1569.     };
  1570.  
  1571.     /**
  1572.      * Retrieve a managed asset.
  1573.      *
  1574.      * @param {string} path - The path to the asset.
  1575.      * @returns {object} - The requested asset, or null if the asset is not loaded.
  1576.      */
  1577.     that.getAsset = function(path) {
  1578.       path = managedAssets[path];
  1579.       if (typeof path != 0) {
  1580.         return path;
  1581.       } else {
  1582.         return null;
  1583.       }
  1584.     };
  1585.  
  1586.     /**
  1587.      * Write an asset to a path.
  1588.      *
  1589.      * @param {string} path - The path to the asset.
  1590.      * @param {object} obj - The asset to write, or null to delete the asset.
  1591.      */
  1592.     that.setAsset = function(path, obj) {
  1593.       if (obj === null) {
  1594.         delete managedAssets[path];
  1595.       } else {
  1596.         managedAssets[path] = obj;
  1597.       }
  1598.     };
  1599.   };
  1600.  
  1601.   /**
  1602.    * Retrieve an asset by it's ID.
  1603.    *
  1604.    * This function will retrieve any asset, even if it is not managed by an
  1605.    * AssetManager. Unlike AssetManager~getAsset, this function addresses
  1606.    * assets by ID, not their path.
  1607.    *
  1608.    * @returns {object} - The requested asset, or null if the asset was not found.
  1609.    */
  1610.   this.getAsset = function(id) {
  1611.     id = assets[id];
  1612.     if (typeof id == 0) {
  1613.       return null;
  1614.     } else {
  1615.       return id;
  1616.     }
  1617.   };
  1618. }();
  1619. var M = "/* snip */";
  1620. new function() {
  1621.   function h(a) {
  1622.     this.data = null;
  1623.     this.direct = false;
  1624.     this.size = this.pos = this.id = this.host = null;
  1625.     0 < arguments.length && (this.data = a.data, this.direct = a.direct, this.host = a.host, this.id = a.id, this.pos = a.pos, this.size = a.size)
  1626.   }
  1627.  
  1628.   function g() {
  1629.     this.skull = this.id = null;
  1630.     this.bind = function(a, e) {
  1631.       return null
  1632.     };
  1633.     this.connect = function(a, e, c) {
  1634.       k.log("sc.connect( " + a + ", " + e + ", " + c + " )");
  1635.       return true
  1636.     }
  1637.   }
  1638.   var k = System,
  1639.     f = M;
  1640.   M = "";
  1641.   k.Protocol = h;
  1642.   k.Machine = g;
  1643.   Math.imul || (Math.imul = function(a, e) {
  1644.     var c = a & 65535,
  1645.       b = e & 65535;
  1646.     return c * b + ((a >>> 16 & 65535) * b + c * (e >>> 16 & 65535) << 16 >>> 0) | 0
  1647.   });
  1648.   Math.fround || (Math.fround = function() {
  1649.     var a = new Float32Array(1);
  1650.     return function(e) {
  1651.       a[0] = +e;
  1652.       return a[0]
  1653.     }
  1654.   }());
  1655.   k.Environment = function() {
  1656.     function a(a) {
  1657.       if ("number" != typeof a) try {
  1658.         return a.toString()
  1659.       } catch (b) {
  1660.         a = v
  1661.       }
  1662.       a |= 0;
  1663.       return 0 > a || a >= m.length ? "Unknown Error." : m[a]
  1664.     }
  1665.  
  1666.     function e(a) {
  1667.       throw a >>> 0;
  1668.     }
  1669.  
  1670.     function c() {
  1671.       var a;
  1672.       do a = (+new Date ^ 65536 * Math.random() ^ 65536 * Math.random() << 16) >>> 0 | 0; while (void 0 !== w[a]);
  1673.       return a
  1674.     }
  1675.  
  1676.     function b() {
  1677.       var a;
  1678.       do a = (+new Date ^ 65536 * Math.random() ^ 65536 * Math.random() << 16) >>> 0 | 0; while (void 0 !== C[a]);
  1679.       return a
  1680.     }
  1681.  
  1682.     function l(b) {
  1683.       if ("number" == typeof b) return 0 < b ? (k.log("sc error " + b + ", 0x" + b.toString(16) + ": " + a(b)), b) : b - 1;
  1684.       k.log("sc error ?: " + a(b));
  1685.       k.log(b);
  1686.       return A
  1687.     }
  1688.  
  1689.     function n(a) {
  1690.       k.log("sc.logs: " + a)
  1691.     }
  1692.  
  1693.     function d(a) {
  1694.       k.log("sc.logv: " + (a >>> 0).toString(16) + " : " + a)
  1695.     }
  1696.  
  1697.     function p(a) {
  1698.       a |= 0;
  1699.       0 > a && (a = 0);
  1700.       throw -a;
  1701.     }
  1702.  
  1703.     function r() {
  1704.       return Date.now()
  1705.     }
  1706.  
  1707.     function u(a) {
  1708.       this.inherit = g;
  1709.       this.inherit();
  1710.       a = (a | 0) >>> 13;
  1711.       ++D;
  1712.       this.id = c();
  1713.       a <<= 13;
  1714.       var b = new ArrayBuffer(a);
  1715.       new Uint8Array(b);
  1716.       var l = new Uint32Array(b);
  1717.       this.connect = function(a, b, d) {
  1718.         return false
  1719.       };
  1720.       this.bind = function(a, b) {
  1721.         return l.subarray(a >> 2, (a >> 2) + (b >> 2))
  1722.       };
  1723.       this.brain = (new Function("std", "foriegn", "heap", f))(window, {
  1724.         error: e,
  1725.         imul: Math.imul,
  1726.         logs: n,
  1727.         logv: d,
  1728.         wait: p,
  1729.         time: r
  1730.       }, b);
  1731.       this.size = a;
  1732.       this.heap = b
  1733.     }
  1734.     var v = 6,
  1735.       A = 7,
  1736.       m = "OK.;Permission Required.;Out of memory.;Invalid operation.;Invalid memory access.;Invalid access alignment.;Unknown error.;Host environment exception.;Halted.;Operation not supported.".split(";"),
  1737.       s = void 0;
  1738.     this.LSB = s;
  1739.     var D = 0,
  1740.       w = [],
  1741.       C = [];
  1742.     this.explain = a;
  1743.     this.cycle = function(a, b) {
  1744.       try {
  1745.         return w[a].brain.cycle(b | 0)
  1746.       } catch (d) {
  1747.         return l(d)
  1748.       }
  1749.     };
  1750.     this.init = function(a, b, d, c, e, f, g) {
  1751.       try {
  1752.         w[a].brain.init(w[a].size - 1, b, d, c, e, f, g)
  1753.       } catch (h) {
  1754.         return l(h)
  1755.       }
  1756.     };
  1757.     this.load = function(b, d, c) {
  1758.       try {
  1759.         if ("string" == typeof d) throw a(9);
  1760.         var e = w[b].bind(c, d.length << 2);
  1761.         for (b = 0; b < d.length && b < e.length; ++b) e[b] = d[b];
  1762.         k.log("Loaded " + b + " words.")
  1763.       } catch (f) {
  1764.         return l(f)
  1765.       }
  1766.     };
  1767.     Date.now || (Date.now = function() {
  1768.       return (new Date).getTime()
  1769.     });
  1770.     try {
  1771.       s = function() {
  1772.         var a = new ArrayBuffer(4);
  1773.         (new DataView(a)).setUint32(0, 1718303319, true);
  1774.         switch ((new Int32Array(a))[0]) {
  1775.           case 1463446374:
  1776.             return false;
  1777.           case 1718303319:
  1778.             return true;
  1779.           default:
  1780.             return null
  1781.         }
  1782.       }()
  1783.     } catch (y) {
  1784.       k.log("sc: can't test LSB: " + y)
  1785.     }
  1786.     this.LSB = s;
  1787.     this.build = function(a) {
  1788.       a = new u(a);
  1789.       if (!a) throw 3;
  1790.       w[a.id] = a;
  1791.       return a.id
  1792.     };
  1793.     this.dump = function(a) {
  1794.       return w[a].heap
  1795.     };
  1796.     this.support = function(a) {
  1797.       var d = new h(a);
  1798.       d.id = b();
  1799.       C[d.id] = d;
  1800.       return a.id = d.id
  1801.     };
  1802.     this.register = function(a) {
  1803.       var b = c();
  1804.       w[b] = a;
  1805.       return b
  1806.     };
  1807.     this.connect = function(a, d, c) {
  1808.       var e = w[a];
  1809.       c = w[c];
  1810.       var f = C[d];
  1811.       if (!e || !c || !f) throw 3;
  1812.       try {
  1813.         if (f = new h(f), f.host = e, f.client = c, f.data = e.bind(f.pos, f.size), f.id = b(), f.direct) try {
  1814.           C[f.id] = f, c.connect(a, d, f.data)
  1815.         } catch (g) {
  1816.           throw delete C[f.id], A;
  1817.         } else C[f.id] = f
  1818.       } catch (l) {
  1819.         throw A;
  1820.       }
  1821.       return f.id
  1822.     }
  1823.   }
  1824. };
  1825. new function() {
  1826.   var h = System,
  1827.     g = "#000 #00a #0a0 #0aa #a00 #a0a #a50 #aaa #555 #55f #5f5 #5ff #f55 #f5f #ff5 #fff".split(" "),
  1828.     k = "#000000 #000084 #008400 #008080 #840000 #800080 #804000 #808080 #404040 #4040ff #38ff38 #40ffff #ff4040 #ff40ff #ffff40 #ffffff".split(" "),
  1829.     f = "#000000 #000099 #009900 #009292 #9b0000 #940094 #964b00 #939393 #4b4b4b #4c4ce0 #39e339 #40e2df #e34b4b #e34ae3 #e0e048 #e2e2e2".split(" ");
  1830.   h.InputDevice = function(a) {
  1831.     function e(b) {
  1832.       c[g] = (b.shiftPressed ? 1 : 0) | (b.ctrlPressed ? 2 : 0) | (b.altPressed ? 4 : 0);
  1833.       a(-1)
  1834.     }
  1835.     this.inherit = h.Phase;
  1836.     this.inherit();
  1837.     this.inherit = h.Machine;
  1838.     this.inherit();
  1839.     var c = null,
  1840.       b = false,
  1841.       f = 1,
  1842.       g = 2;
  1843.     this.setScale = function(a) {
  1844.       f = 0 + a
  1845.     };
  1846.     this.mouseMove = function(a) {
  1847.       c[5] = a.mousePos.x * f;
  1848.       c[6] = a.mousePos.y * f;
  1849.       c[9] += a.mouseMov.x * f;
  1850.       c[10] += a.mouseMov.y * f;
  1851.       c[0] |= 1;
  1852.       e(a)
  1853.     };
  1854.     this.mousePress = function(a) {
  1855.       c[7] = a.mouseButton;
  1856.       c[0] |= 2;
  1857.       e(a)
  1858.     };
  1859.     this.mouseRelease = function(a) {
  1860.       c[7] = a.mouseButton;
  1861.       c[0] |= 4;
  1862.       e(a)
  1863.     };
  1864.     this.mouseWheel = function(a) {
  1865.       c[8] = a.mouseWheel | 0;
  1866.       c[0] |= 8;
  1867.       e(a)
  1868.     };
  1869.     this.keyPress = function(a) {
  1870.       c[0] |= 16;
  1871.       c[3] = a.keyCode;
  1872.       e(a)
  1873.     };
  1874.     this.keyRelease = function(a) {
  1875.       c[3] = a.keyCode;
  1876.       c[0] |= 32;
  1877.       e(a)
  1878.     };
  1879.     this.charTyped = function(a) {
  1880.       c[0] |= 64;
  1881.       c[4] = a.charCode;
  1882.       e(a)
  1883.     };
  1884.     this.begin = function() {};
  1885.     this.end = function() {};
  1886.     this.update = function(b) {
  1887.       a(b);
  1888.       return true
  1889.     };
  1890.     this.connect = function(a, e, f) {
  1891.       if (b) return false;
  1892.       c = f;
  1893.       return b = true
  1894.     }
  1895.   };
  1896.   h.TextDisplay = function(a, e, c, b, l) {
  1897.     function n(a, b) {
  1898.       var c = b.length;
  1899.       if (null == N) {
  1900.         N = Array(c);
  1901.         for (var d = c; 0 <= --d;) N[d] = document.createElement("canvas")
  1902.       }
  1903.       for (d = c; 0 <= --d;) canvas = N[d], canvas.width = a.width, canvas.height = a.height, c = canvas.getContext("2d"), c.globalAlpha = 1, c.globalCompositeOperation = "source-over", c.drawImage(a, 0, 0), c.globalCompositeOperation = "source-atop", c.fillStyle = b[d], c.fillRect(0, 0, canvas.width, canvas.height)
  1904.     }
  1905.     var d = this;
  1906.     d.inherit = h.Machine;
  1907.     d.inherit();
  1908.     l = l ? true : false;
  1909.     b |= 0;
  1910.     e |= 0;
  1911.     c |= 0;
  1912.     var p = e * b,
  1913.       r = c * b;
  1914.     a.width = p;
  1915.     a.height = r;
  1916.     var u = null,
  1917.       v = 0,
  1918.       A = 0,
  1919.       m = a.getContext("2d"),
  1920.       s = m,
  1921.       D = 1 < b && l ? k : g;
  1922.     1 < b && (u = document.createElement("canvas"), u.width = e, u.height = c, s = u.getContext("2d"), u.ctx = s);
  1923.     s.fillStyle = D[0];
  1924.     s.fillRect(0, 0, e, c);
  1925.     var w = Math.floor(e / 9),
  1926.       C = c >>> 4,
  1927.       y = C * w,
  1928.       G = 4,
  1929.       T = G + (y >> 1) - 1,
  1930.       y = new ArrayBuffer(y << 2),
  1931.       R = new Uint32Array(y),
  1932.       U = false,
  1933.       q = 0,
  1934.       H, L, I, J, U = false,
  1935.       E = 0,
  1936.       B = null,
  1937.       Z = false,
  1938.       F, V, W, Q;
  1939.     d.connect = function(a, b, c) {
  1940.       if (Z) return false;
  1941.       B = c;
  1942.       V = +new Date;
  1943.       W = 666;
  1944.       Q = 0;
  1945.       F = false;
  1946.       G = 4;
  1947.       L = 13;
  1948.       H = 1;
  1949.       I = 7;
  1950.       J = 2;
  1951.       E = 0;
  1952.       q = E << 16 | H << 12 | L << 8 | I << 4 | J | 134217728;
  1953.       l && (q |= 268435456);
  1954.       B[0] = q;
  1955.       B[2] = C << 16 | w;
  1956.       B[1] = 0;
  1957.       B[3] = G;
  1958.       return Z = true
  1959.     };
  1960.     d.setScanLines = function(a) {
  1961.       l = a ? true : false;
  1962.       n(K[1], l ? k : g);
  1963.       l && 1 < b ? (viewport.parentElement.style.backgroundColor = f[E], D = k) : (viewport.parentElement.style.backgroundColor = g[E], D = g);
  1964.       s = 1 < b ? u.ctx : m;
  1965.       for (a = 0; a < R.length; ++a) R[a] = 0
  1966.     };
  1967.     d.render = function() {
  1968.       if (U) {
  1969.         h.scaleSmoothing(s, false);
  1970.         q = B[0];
  1971.         0 == (q & 2147483648) && (B[0] = q |= 2147483648, B[2] = C << 16 | w);
  1972.         var k = B[1],
  1973.           n = k >>> 16 & 65535,
  1974.           k = k & 65535;
  1975.         if (0 == (q & 134217728)) F && (q |= 536870912), F = false;
  1976.         else if (W) {
  1977.           var t = +new Date;
  1978.           Q += t - V;
  1979.           Q >= W && (Q = 0, F = !F, q |= 536870912);
  1980.           V = t
  1981.         } else F || (q |= 536870912), F = true;
  1982.         if (0 != (q & 1610612736)) {
  1983.           q &= -536870913;
  1984.           t = q >>> 16 & 15;
  1985.           H = q >>> 12 & 15;
  1986.           L = q >>> 8 & 15;
  1987.           I = q >>> 4 & 15;
  1988.           J = q & 15;
  1989.           v = (k + n * w >> 1) + G;
  1990.           0 != (q & 268435456) != l ? (E = t, d.setScanLines(q & 268435456)) : t != E && (E = t, viewport.parentElement.style.backgroundColor = l ? f[E] : g[E]);
  1991.           s.globalCompositeOperation = "source-over";
  1992.           s.globalAlpha = 1;
  1993.           for (var z = t = 0, y = 0, x = 0, S = 0, O = 0, P = 0, K = 0, y = G; y <= T; ++y) {
  1994.             x = B[y];
  1995.             if (x == R[K] && y != v && y != A) {
  1996.               if (++K, t += 9, t >= e && (z += 16, t = 0, z >= c)) break
  1997.             } else {
  1998.               R[K++] = x;
  1999.               O = x >> 8 & 15;
  2000.               S = x >> 12 & 15;
  2001.               P = x & 255;
  2002.               chx = P & 31;
  2003.               chy = P >> 5 & 7;
  2004.               s.fillStyle = D[S];
  2005.               s.fillRect(t, z, 9, 16);
  2006.               s.drawImage(N[O], 2 + 12 * chx, 2 + 19 * chy, 9, 16, t, z, 9, 16);
  2007.               t += 9;
  2008.               if (t >= e && (z += 16, t = 0, z >= c)) break;
  2009.               x >>>= 16;
  2010.               O = x >> 8 & 15;
  2011.               S = x >> 12 & 15;
  2012.               P = x & 255;
  2013.               chx = P & 31;
  2014.               chy = P >> 5 & 7;
  2015.               s.fillStyle = D[S];
  2016.               s.fillRect(t, z, 9, 16);
  2017.               s.drawImage(N[O], 2 + 12 * chx, 2 + 19 * chy, 9, 16, t, z, 9, 16)
  2018.             }
  2019.             t += 9;
  2020.             if (t >= e && (z += 16, t = 0, z >= c)) break
  2021.           }
  2022.           A = v;
  2023.           F && (k < w && n < C) && (x = B[v], k & 1 && (x >>>= 16), O = x >> 8 & 15, 9 > H && (0 < J && 0 < I) && (9 < H + I && (I = 9 - H), 16 < L + J && (J = 16 - L), s.fillStyle = D[O], s.fillRect(9 * k + H, (n << 4) + L, I, J)));
  2024.           1 < b && (h.scaleSmoothing(m, false), m.globalAlpha = 1, m.globalCompositeOperation = "source-over", m.drawImage(u, 0, 0, e, c, 0, 0, p, r), l && null != X && (m.fillStyle = X, m.fillRect(0, 0, p, r), m.globalCompositeOperation = "lighter", h.scaleSmoothing(m, true), m.globalAlpha = 0.5, m.drawImage(a, 0, 0, p, r, 1.5, 0, p, r), m.drawImage(a, 0, 0, p, r, -0.25, 1, p, r)));
  2025.           B[0] = q
  2026.         }
  2027.       }
  2028.     };
  2029.     var K = [],
  2030.       N = null,
  2031.       X = null,
  2032.       Y = new h.ImageLoader("img/", function(a, b, c, d) {
  2033.         if (b) {
  2034.           if (h.log("Loaded texture #" + d + " from: " + c), K[d] = a, Y.count == Y.loaded) {
  2035.             b = document.createElement("canvas");
  2036.             K[0] = b;
  2037.             b.width = 64;
  2038.             b.height = 64;
  2039.             c = b.getContext("2d");
  2040.             c.fillStyle = "#000";
  2041.             c.globalAlpha = 1;
  2042.             h.scaleSmoothing(c, false);
  2043.             for (d = 1; 64 > d; d += 2) c.fillRect(0, d, 64, 1);
  2044.             X = c.createPattern(b, "repeat");
  2045.             n(a, l ? k : g);
  2046.             h.log("Display ready.");
  2047.             U = true
  2048.           }
  2049.         } else h.log("Could not load texture #" + d + " from: " + c)
  2050.       });
  2051.     Y.queue("sc-font-9x16.png", 1);
  2052.     viewport.parentElement.style.backgroundColor = l && 1 < b ? f[E] : g[E];
  2053.     d.toString = function() {
  2054.       return "TextDisplay"
  2055.     }
  2056.   }
  2057. };
  2058. new function() {
  2059.   var h = System;
  2060.   h.onInit(function() {
  2061.     try {
  2062.       h.log = function(a) {
  2063.         console.log(a)
  2064.       };
  2065.       var g = new h.Environment,
  2066.         k = g.build(2097152),
  2067.         f = new h.Protocol;
  2068.       f.pos = 0;
  2069.       f.size = 64;
  2070.       f.direct = true;
  2071.       g.support(f);
  2072.       var a = new h.Protocol;
  2073.       a.pos = f.size;
  2074.       a.size = 16384;
  2075.       a.direct = true;
  2076.       g.support(a);
  2077.       var e = document.getElementById("viewport"),
  2078.         c = true,
  2079.         b = new h.InputDevice(function(a) {
  2080.           c || (0 <= a && (cycleDelay = -g.cycle(k, 32768), l.render()), 0 > cycleDelay && (c = true))
  2081.         }),
  2082.         l;
  2083.       1440 <= e.offsetParent.offsetWidth + 4 && 800 <= e.offsetParent.offsetHeight + 4 ? (l = new h.TextDisplay(e, 720, 400, 2, false), b.setScale(1)) : (l = new h.TextDisplay(e, 720, 400, 1, false), b.setScale(2));
  2084.       window.setScanLines = l.setScanLines;
  2085.       var n = g.register(l);
  2086.       g.connect(k, a.id, n);
  2087.       var d = g.register(b);
  2088.       g.connect(k, f.id, d);
  2089.       var p = a.pos + a.size + 1023 & 268434432,
  2090.         f = [/* snip */];
  2091.       g.load(k, f, p);
  2092.       g.init(k, p, p + (f.length << 2), 0, 0, 0, 0);
  2093.       c = false
  2094.     } catch (r) {
  2095.       if ("number" == typeof r) throw g.explain(r);
  2096.       throw "boot: " + r.toString() + ": line " + r.lineNumber + ": " + r.fileName;
  2097.     }
  2098.     h.phase = b
  2099.   });
  2100.   h.start({
  2101.     canvas: "viewport",
  2102.     realtime: true
  2103.   })
  2104. };
clone this paste RAW Paste Data
Top