tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gbemu-part1.js (51364B)


      1 // Portions copyright 2013 Google, Inc
      2 
      3 // Copyright (C) 2010 - 2012 Grant Galitz
      4 // This program is free software; you can redistribute it and/or modify
      5 // it under the terms of the GNU General Public License version 2 as
      6 // published by the Free Software Foundation.
      7 // The full license is available at http://www.gnu.org/licenses/gpl.html
      8 // This program is distributed in the hope that it will be useful, but
      9 // WITHOUT ANY WARRANTY; without even the implied warranty of
     10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     11 // See the GNU General Public License for more details.
     12 
     13 // The code has been adapted for use as a benchmark by Google.
     14 
     15 var GameboyBenchmark = new BenchmarkSuite('Gameboy', [26288412],
     16                                          [new Benchmark('Gameboy',
     17                                                         false,
     18                                                         false,
     19                                                         20,
     20                                                         runGameboy,
     21                                                         setupGameboy,
     22                                                         tearDownGameboy,
     23                                                         null,
     24                                                         4)]);
     25 
     26 var decoded_gameboy_rom = null;
     27 
     28 function setupGameboy() {
     29 
     30  // Check if all the types required by the code are supported.
     31  // If not, throw exception and quit.
     32  if (!(typeof Uint8Array != "undefined" &&
     33      typeof Int8Array != "undefined" &&
     34      typeof Float32Array != "undefined" &&
     35      typeof Int32Array != "undefined") ) {
     36    throw "TypedArrayUnsupported";
     37  }
     38  decoded_gameboy_rom = base64_decode(gameboy_rom);
     39  rom = null;
     40 }
     41 
     42 function runGameboy() {
     43  start(new GameBoyCanvas(), decoded_gameboy_rom);
     44 
     45  gameboy.instructions = 0;
     46  gameboy.totalInstructions = 250000;
     47 
     48  while (gameboy.instructions <= gameboy.totalInstructions) {
     49    gameboy.run();
     50    GameBoyAudioNode.run();
     51  }
     52 
     53  resetGlobalVariables();
     54 }
     55 
     56 function tearDownGameboy() {
     57  decoded_gameboy_rom = null;
     58  expectedGameboyStateStr = null;
     59 }
     60 
     61 var expectedGameboyStateStr =
     62  '{"registerA":160,"registerB":255,"registerC":255,"registerE":11,' +
     63  '"registersHL":51600,"programCounter":24309,"stackPointer":49706,' +
     64  '"sumROM":10171578,"sumMemory":3435856,"sumMBCRam":234598,"sumVRam":0}';
     65 
     66 // Start of browser emulation.
     67 
     68 var GameBoyWindow = { };
     69 
     70 function GameBoyContext() {
     71  this.createBuffer = function() {
     72    return new Buffer();
     73  }
     74  this.createImageData = function (w, h) {
     75    var result = {};
     76    // The following line was updated since Octane 1.0 to avoid OOB access.
     77    result.data = new Uint8Array(w * h * 4);
     78    return result;
     79  }
     80  this.putImageData = function (buffer, x, y) {
     81    var sum = 0;
     82    for (var i = 0; i < buffer.data.length; i++) {
     83      sum += i * buffer.data[i];
     84      sum = sum % 1000;
     85    }
     86  }
     87  this.drawImage = function () { }
     88 };
     89 
     90 function GameBoyCanvas() {
     91  this.getContext = function() {
     92    return new GameBoyContext();
     93  }
     94  this.width = 160;
     95  this.height = 144;
     96  this.style = { visibility: "visibile" };
     97 }
     98 
     99 function cout(message, colorIndex) {
    100 }
    101 
    102 function clear_terminal() {
    103 }
    104 
    105 var GameBoyAudioNode = {
    106  bufferSize : 0,
    107  onaudioprocess : null ,
    108  connect : function () {},
    109  run: function() {
    110    var event = {outputBuffer : this.outputBuffer};
    111    this.onaudioprocess(event);
    112  }
    113 };
    114 
    115 function GameBoyAudioContext () {
    116  this.createBufferSource = function() {
    117    return { noteOn : function () {}, connect : function() {}};
    118  }
    119  this.sampleRate = 48000;
    120  this.destination = {}
    121  this.createBuffer = function (channels, len, sampleRate) {
    122    return { gain : 1,
    123             numberOfChannels : 1,
    124             length : 1,
    125             duration : 0.000020833333110203966,
    126             sampleRate : 48000}
    127  }
    128  this.createJavaScriptNode = function (bufferSize, inputChannels, outputChannels) {
    129    GameBoyAudioNode.bufferSize = bufferSize;
    130    GameBoyAudioNode.outputBuffer = {
    131        getChannelData : function (i) {return this.channelData[i];},
    132        channelData    : []
    133    };
    134    for (var i = 0; i < outputChannels; i++) {
    135      GameBoyAudioNode.outputBuffer.channelData[i] = new Float32Array(bufferSize);
    136    }
    137    return GameBoyAudioNode;
    138  }
    139 }
    140 
    141 var mock_date_time_counter = 0;
    142 
    143 function new_Date() {
    144  return {
    145    getTime: function() {
    146      mock_date_time_counter += 16;
    147      return mock_date_time_counter;
    148    }
    149  };
    150 }
    151 
    152 // End of browser emulation.
    153 
    154 // Start of helper functions.
    155 
    156 function checkFinalState() {
    157  function sum(a) {
    158    var result = 0;
    159    for (var i = 0; i < a.length; i++) {
    160      result += a[i];
    161    }
    162    return result;
    163  }
    164  var state = {
    165    registerA: gameboy.registerA,
    166    registerB: gameboy.registerB,
    167    registerC: gameboy.registerC,
    168    registerE: gameboy.registerE,
    169    registerF: gameboy.registerF,
    170    registersHL: gameboy.registersHL,
    171    programCounter: gameboy.programCounter,
    172    stackPointer: gameboy.stackPointer,
    173    sumROM : sum(gameboy.fromTypedArray(gameboy.ROM)),
    174    sumMemory: sum(gameboy.fromTypedArray(gameboy.memory)),
    175    sumMBCRam: sum(gameboy.fromTypedArray(gameboy.MBCRam)),
    176    sumVRam: sum(gameboy.fromTypedArray(gameboy.VRam))
    177  };
    178  var expectedState = JSON.parse(expectedGameboyStateStr);
    179  for (var prop in expectedState) {
    180    if (state[prop] !== expectedState[prop]) {
    181      var stateStr = JSON.stringify(state);
    182        alert("Incorrect final state of processor:\n" +
    183              " actual   " + stateStr + "\n" +
    184              " expected " + expectedGameboyStateStr);
    185    }
    186  }
    187 }
    188 
    189 
    190 function resetGlobalVariables () {
    191  //Audio API Event Handler:
    192  audioContextHandle = null;
    193  audioNode = null;
    194  audioSource = null;
    195  launchedContext = false;
    196  audioContextSampleBuffer = [];
    197  resampled = [];
    198  webAudioMinBufferSize = 15000;
    199  webAudioMaxBufferSize = 25000;
    200  webAudioActualSampleRate = 44100;
    201  XAudioJSSampleRate = 0;
    202  webAudioMono = false;
    203  XAudioJSVolume = 1;
    204  resampleControl = null;
    205  audioBufferSize = 0;
    206  resampleBufferStart = 0;
    207  resampleBufferEnd = 0;
    208  resampleBufferSize = 2;
    209 
    210  gameboy = null;           //GameBoyCore object.
    211  gbRunInterval = null;       //GameBoyCore Timer
    212 }
    213 
    214 
    215 // End of helper functions.
    216 
    217 // Original code from Grant Galitz follows.
    218 // Modifications by Google are marked in comments.
    219 
    220 // Start of js/other/base64.js file.
    221 
    222 var toBase64 = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
    223  "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
    224  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+" , "/", "="];
    225 var fromBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    226 function base64(data) {
    227  try {
    228    // The following line was modified for benchmarking:
    229    var base64 = GameBoyWindow.btoa(data);  //Use this native function when it's available, as it's a magnitude faster than the non-native code below.
    230  }
    231  catch (error) {
    232    //Defaulting to non-native base64 encoding...
    233    var base64 = "";
    234    var dataLength = data.length;
    235    if (dataLength > 0) {
    236      var bytes = [0, 0, 0];
    237      var index = 0;
    238      var remainder = dataLength % 3;
    239      while (data.length % 3 > 0) {
    240        //Make sure we don't do fuzzy math in the next loop...
    241        data[data.length] = " ";
    242      }
    243      while (index < dataLength) {
    244        //Keep this loop small for speed.
    245        bytes = [data.charCodeAt(index++) & 0xFF, data.charCodeAt(index++) & 0xFF, data.charCodeAt(index++) & 0xFF];
    246        base64 += toBase64[bytes[0] >> 2] + toBase64[((bytes[0] & 0x3) << 4) | (bytes[1] >> 4)] + toBase64[((bytes[1] & 0xF) << 2) | (bytes[2] >> 6)] + toBase64[bytes[2] & 0x3F];
    247      }
    248      if (remainder > 0) {
    249        //Fill in the padding and recalulate the trailing six-bit group...
    250        base64[base64.length - 1] = "=";
    251        if (remainder == 2) {
    252          base64[base64.length - 2] = "=";
    253          base64[base64.length - 3] = toBase64[(bytes[0] & 0x3) << 4];
    254        }
    255        else {
    256          base64[base64.length - 2] = toBase64[(bytes[1] & 0xF) << 2];
    257        }
    258      }
    259    }
    260  }
    261  return base64;
    262 }
    263 function base64_decode(data) {
    264  try {
    265    // The following line was modified for benchmarking:
    266    var decode64 = GameBoyWindow.atob(data);  //Use this native function when it's available, as it's a magnitude faster than the non-native code below.
    267  }
    268  catch (error) {
    269    //Defaulting to non-native base64 decoding...
    270    var decode64 = "";
    271    var dataLength = data.length;
    272    if (dataLength > 3 && dataLength % 4 == 0) {
    273      var sixbits = [0, 0, 0, 0];  //Declare this out of the loop, to speed up the ops.
    274      var index = 0;
    275      while (index < dataLength) {
    276        //Keep this loop small for speed.
    277        sixbits = [fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++)), fromBase64.indexOf(data.charAt(index++))];
    278        decode64 += String.fromCharCode((sixbits[0] << 2) | (sixbits[1] >> 4)) + String.fromCharCode(((sixbits[1] & 0x0F) << 4) | (sixbits[2] >> 2)) + String.fromCharCode(((sixbits[2] & 0x03) << 6) | sixbits[3]);
    279      }
    280      //Check for the '=' character after the loop, so we don't hose it up.
    281      if (sixbits[3] >= 0x40) {
    282        decode64.length -= 1;
    283        if (sixbits[2] >= 0x40) {
    284          decode64.length -= 1;
    285        }
    286      }
    287    }
    288  }
    289  return decode64;
    290 }
    291 function to_little_endian_dword(str) {
    292  return to_little_endian_word(str) + String.fromCharCode((str >> 16) & 0xFF, (str >> 24) & 0xFF);
    293 }
    294 function to_little_endian_word(str) {
    295  return to_byte(str) + String.fromCharCode((str >> 8) & 0xFF);
    296 }
    297 function to_byte(str) {
    298  return String.fromCharCode(str & 0xFF);
    299 }
    300 function arrayToBase64(arrayIn) {
    301  var binString = "";
    302  var length = arrayIn.length;
    303  for (var index = 0; index < length; ++index) {
    304    if (typeof arrayIn[index] == "number") {
    305      binString += String.fromCharCode(arrayIn[index]);
    306    }
    307  }
    308  return base64(binString);
    309 }
    310 function base64ToArray(b64String) {
    311  var binString = base64_decode(b64String);
    312  var outArray = [];
    313  var length = binString.length;
    314  for (var index = 0; index < length;) {
    315    outArray.push(binString.charCodeAt(index++) & 0xFF);
    316  }
    317  return outArray;
    318 }
    319 
    320 // End of js/other/base64.js file.
    321 
    322 // Start of js/other/resampler.js file.
    323 
    324 //JavaScript Audio Resampler (c) 2011 - Grant Galitz
    325 function Resampler(fromSampleRate, toSampleRate, channels, outputBufferSize, noReturn) {
    326  this.fromSampleRate = fromSampleRate;
    327  this.toSampleRate = toSampleRate;
    328  this.channels = channels | 0;
    329  this.outputBufferSize = outputBufferSize;
    330  this.noReturn = !!noReturn;
    331  this.initialize();
    332 }
    333 Resampler.prototype.initialize = function () {
    334  //Perform some checks:
    335  if (this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0) {
    336    if (this.fromSampleRate == this.toSampleRate) {
    337      //Setup a resampler bypass:
    338      this.resampler = this.bypassResampler;    //Resampler just returns what was passed through.
    339      this.ratioWeight = 1;
    340    }
    341    else {
    342      //Setup the interpolation resampler:
    343      this.compileInterpolationFunction();
    344      this.resampler = this.interpolate;      //Resampler is a custom quality interpolation algorithm.
    345      this.ratioWeight = this.fromSampleRate / this.toSampleRate;
    346      this.tailExists = false;
    347      this.lastWeight = 0;
    348      this.initializeBuffers();
    349    }
    350  }
    351  else {
    352    throw(new Error("Invalid settings specified for the resampler."));
    353  }
    354 }
    355 Resampler.prototype.compileInterpolationFunction = function () {
    356  var toCompile = "var bufferLength = Math.min(buffer.length, this.outputBufferSize);\
    357  if ((bufferLength % " + this.channels + ") == 0) {\
    358    if (bufferLength > 0) {\
    359      var ratioWeight = this.ratioWeight;\
    360      var weight = 0;";
    361  for (var channel = 0; channel < this.channels; ++channel) {
    362    toCompile += "var output" + channel + " = 0;"
    363  }
    364  toCompile += "var actualPosition = 0;\
    365      var amountToNext = 0;\
    366      var alreadyProcessedTail = !this.tailExists;\
    367      this.tailExists = false;\
    368      var outputBuffer = this.outputBuffer;\
    369      var outputOffset = 0;\
    370      var currentPosition = 0;\
    371      do {\
    372        if (alreadyProcessedTail) {\
    373          weight = ratioWeight;";
    374  for (channel = 0; channel < this.channels; ++channel) {
    375    toCompile += "output" + channel + " = 0;"
    376  }
    377  toCompile += "}\
    378        else {\
    379          weight = this.lastWeight;";
    380  for (channel = 0; channel < this.channels; ++channel) {
    381    toCompile += "output" + channel + " = this.lastOutput[" + channel + "];"
    382  }
    383  toCompile += "alreadyProcessedTail = true;\
    384        }\
    385        while (weight > 0 && actualPosition < bufferLength) {\
    386          amountToNext = 1 + actualPosition - currentPosition;\
    387          if (weight >= amountToNext) {";
    388  for (channel = 0; channel < this.channels; ++channel) {
    389    toCompile += "output" + channel + " += buffer[actualPosition++] * amountToNext;"
    390  }
    391  toCompile += "currentPosition = actualPosition;\
    392            weight -= amountToNext;\
    393          }\
    394          else {";
    395  for (channel = 0; channel < this.channels; ++channel) {
    396    toCompile += "output" + channel + " += buffer[actualPosition" + ((channel > 0) ? (" + " + channel) : "") + "] * weight;"
    397  }
    398  toCompile += "currentPosition += weight;\
    399            weight = 0;\
    400            break;\
    401          }\
    402        }\
    403        if (weight == 0) {";
    404  for (channel = 0; channel < this.channels; ++channel) {
    405    toCompile += "outputBuffer[outputOffset++] = output" + channel + " / ratioWeight;"
    406  }
    407  toCompile += "}\
    408        else {\
    409          this.lastWeight = weight;";
    410  for (channel = 0; channel < this.channels; ++channel) {
    411    toCompile += "this.lastOutput[" + channel + "] = output" + channel + ";"
    412  }
    413  toCompile += "this.tailExists = true;\
    414          break;\
    415        }\
    416      } while (actualPosition < bufferLength);\
    417      return this.bufferSlice(outputOffset);\
    418    }\
    419    else {\
    420      return (this.noReturn) ? 0 : [];\
    421    }\
    422  }\
    423  else {\
    424    throw(new Error(\"Buffer was of incorrect sample length.\"));\
    425  }";
    426  this.interpolate = Function("buffer", toCompile);
    427 }
    428 Resampler.prototype.bypassResampler = function (buffer) {
    429  if (this.noReturn) {
    430    //Set the buffer passed as our own, as we don't need to resample it:
    431    this.outputBuffer = buffer;
    432    return buffer.length;
    433  }
    434  else {
    435    //Just return the buffer passsed:
    436    return buffer;
    437  }
    438 }
    439 Resampler.prototype.bufferSlice = function (sliceAmount) {
    440  if (this.noReturn) {
    441    //If we're going to access the properties directly from this object:
    442    return sliceAmount;
    443  }
    444  else {
    445    //Typed array and normal array buffer section referencing:
    446    try {
    447      return this.outputBuffer.subarray(0, sliceAmount);
    448    }
    449    catch (error) {
    450      try {
    451        //Regular array pass:
    452        this.outputBuffer.length = sliceAmount;
    453        return this.outputBuffer;
    454      }
    455      catch (error) {
    456        //Nightly Firefox 4 used to have the subarray function named as slice:
    457        return this.outputBuffer.slice(0, sliceAmount);
    458      }
    459    }
    460  }
    461 }
    462 Resampler.prototype.initializeBuffers = function () {
    463  //Initialize the internal buffer:
    464  try {
    465    this.outputBuffer = new Float32Array(this.outputBufferSize);
    466    this.lastOutput = new Float32Array(this.channels);
    467  }
    468  catch (error) {
    469    this.outputBuffer = [];
    470    this.lastOutput = [];
    471  }
    472 }
    473 
    474 // End of js/other/resampler.js file.
    475 
    476 // Start of js/other/XAudioServer.js file.
    477 
    478 /*Initialize here first:
    479  Example:
    480    Stereo audio with a sample rate of 70 khz, a minimum buffer of 15000 samples total, a maximum buffer of 25000 samples total and a starting volume level of 1.
    481      var parentObj = this;
    482      this.audioHandle = new XAudioServer(2, 70000, 15000, 25000, function (sampleCount) {
    483        return parentObj.audioUnderRun(sampleCount);
    484      }, 1);
    485 
    486  The callback is passed the number of samples requested, while it can return any number of samples it wants back.
    487 */
    488 function XAudioServer(channels, sampleRate, minBufferSize, maxBufferSize, underRunCallback, volume) {
    489  this.audioChannels = (channels == 2) ? 2 : 1;
    490  webAudioMono = (this.audioChannels == 1);
    491  XAudioJSSampleRate = (sampleRate > 0 && sampleRate <= 0xFFFFFF) ? sampleRate : 44100;
    492  webAudioMinBufferSize = (minBufferSize >= (samplesPerCallback << 1) && minBufferSize < maxBufferSize) ? (minBufferSize & ((webAudioMono) ? 0xFFFFFFFF : 0xFFFFFFFE)) : (samplesPerCallback << 1);
    493  webAudioMaxBufferSize = (Math.floor(maxBufferSize) > webAudioMinBufferSize + this.audioChannels) ? (maxBufferSize & ((webAudioMono) ? 0xFFFFFFFF : 0xFFFFFFFE)) : (minBufferSize << 1);
    494  this.underRunCallback = (typeof underRunCallback == "function") ? underRunCallback : function () {};
    495  XAudioJSVolume = (volume >= 0 && volume <= 1) ? volume : 1;
    496  this.audioType = -1;
    497  this.mozAudioTail = [];
    498  this.audioHandleMoz = null;
    499  this.audioHandleFlash = null;
    500  this.flashInitialized = false;
    501  this.mozAudioFound = false;
    502  this.initializeAudio();
    503 }
    504 XAudioServer.prototype.MOZWriteAudio = function (buffer) {
    505  //mozAudio:
    506  this.MOZWriteAudioNoCallback(buffer);
    507  this.MOZExecuteCallback();
    508 }
    509 XAudioServer.prototype.MOZWriteAudioNoCallback = function (buffer) {
    510  //mozAudio:
    511  this.writeMozAudio(buffer);
    512 }
    513 XAudioServer.prototype.callbackBasedWriteAudio = function (buffer) {
    514  //Callback-centered audio APIs:
    515  this.callbackBasedWriteAudioNoCallback(buffer);
    516  this.callbackBasedExecuteCallback();
    517 }
    518 XAudioServer.prototype.callbackBasedWriteAudioNoCallback = function (buffer) {
    519  //Callback-centered audio APIs:
    520  var length = buffer.length;
    521  for (var bufferCounter = 0; bufferCounter < length && audioBufferSize < webAudioMaxBufferSize;) {
    522    audioContextSampleBuffer[audioBufferSize++] = buffer[bufferCounter++];
    523  }
    524 }
    525 /*Pass your samples into here!
    526 Pack your samples as a one-dimenional array
    527 With the channel samplea packed uniformly.
    528 examples:
    529    mono - [left, left, left, left]
    530    stereo - [left, right, left, right, left, right, left, right]
    531 */
    532 XAudioServer.prototype.writeAudio = function (buffer) {
    533  if (this.audioType == 0) {
    534    this.MOZWriteAudio(buffer);
    535  }
    536  else if (this.audioType == 1) {
    537    this.callbackBasedWriteAudio(buffer);
    538  }
    539  else if (this.audioType == 2) {
    540    if (this.checkFlashInit() || launchedContext) {
    541      this.callbackBasedWriteAudio(buffer);
    542    }
    543    else if (this.mozAudioFound) {
    544      this.MOZWriteAudio(buffer);
    545    }
    546  }
    547 }
    548 /*Pass your samples into here if you don't want automatic callback calling:
    549 Pack your samples as a one-dimenional array
    550 With the channel samplea packed uniformly.
    551 examples:
    552    mono - [left, left, left, left]
    553    stereo - [left, right, left, right, left, right, left, right]
    554 Useful in preventing infinite recursion issues with calling writeAudio inside your callback.
    555 */
    556 XAudioServer.prototype.writeAudioNoCallback = function (buffer) {
    557  if (this.audioType == 0) {
    558    this.MOZWriteAudioNoCallback(buffer);
    559  }
    560  else if (this.audioType == 1) {
    561    this.callbackBasedWriteAudioNoCallback(buffer);
    562  }
    563  else if (this.audioType == 2) {
    564    if (this.checkFlashInit() || launchedContext) {
    565      this.callbackBasedWriteAudioNoCallback(buffer);
    566    }
    567    else if (this.mozAudioFound) {
    568      this.MOZWriteAudioNoCallback(buffer);
    569    }
    570  }
    571 }
    572 //Developer can use this to see how many samples to write (example: minimum buffer allotment minus remaining samples left returned from this function to make sure maximum buffering is done...)
    573 //If -1 is returned, then that means metric could not be done.
    574 XAudioServer.prototype.remainingBuffer = function () {
    575  if (this.audioType == 0) {
    576    //mozAudio:
    577    return this.samplesAlreadyWritten - this.audioHandleMoz.mozCurrentSampleOffset();
    578  }
    579  else if (this.audioType == 1) {
    580    //WebKit Audio:
    581    return (((resampledSamplesLeft() * resampleControl.ratioWeight) >> (this.audioChannels - 1)) << (this.audioChannels - 1)) + audioBufferSize;
    582  }
    583  else if (this.audioType == 2) {
    584    if (this.checkFlashInit() || launchedContext) {
    585      //Webkit Audio / Flash Plugin Audio:
    586      return (((resampledSamplesLeft() * resampleControl.ratioWeight) >> (this.audioChannels - 1)) << (this.audioChannels - 1)) + audioBufferSize;
    587    }
    588    else if (this.mozAudioFound) {
    589      //mozAudio:
    590      return this.samplesAlreadyWritten - this.audioHandleMoz.mozCurrentSampleOffset();
    591    }
    592  }
    593  //Default return:
    594  return 0;
    595 }
    596 XAudioServer.prototype.MOZExecuteCallback = function () {
    597  //mozAudio:
    598  var samplesRequested = webAudioMinBufferSize - this.remainingBuffer();
    599  if (samplesRequested > 0) {
    600    this.writeMozAudio(this.underRunCallback(samplesRequested));
    601  }
    602 }
    603 XAudioServer.prototype.callbackBasedExecuteCallback = function () {
    604  //WebKit /Flash Audio:
    605  var samplesRequested = webAudioMinBufferSize - this.remainingBuffer();
    606  if (samplesRequested > 0) {
    607    this.callbackBasedWriteAudioNoCallback(this.underRunCallback(samplesRequested));
    608  }
    609 }
    610 //If you just want your callback called for any possible refill (Execution of callback is still conditional):
    611 XAudioServer.prototype.executeCallback = function () {
    612  if (this.audioType == 0) {
    613    this.MOZExecuteCallback();
    614  }
    615  else if (this.audioType == 1) {
    616    this.callbackBasedExecuteCallback();
    617  }
    618  else if (this.audioType == 2) {
    619    if (this.checkFlashInit() || launchedContext) {
    620      this.callbackBasedExecuteCallback();
    621    }
    622    else if (this.mozAudioFound) {
    623      this.MOZExecuteCallback();
    624    }
    625  }
    626 }
    627 //DO NOT CALL THIS, the lib calls this internally!
    628 XAudioServer.prototype.initializeAudio = function () {
    629  try {
    630    throw (new Error("Select initializeWebAudio case"));  // Line added for benchmarking.
    631    this.preInitializeMozAudio();
    632    if (navigator.platform == "Linux i686") {
    633      //Block out mozaudio usage for Linux Firefox due to moz bugs:
    634      throw(new Error(""));
    635    }
    636    this.initializeMozAudio();
    637  }
    638  catch (error) {
    639    try {
    640      this.initializeWebAudio();
    641    }
    642    catch (error) {
    643      try {
    644        this.initializeFlashAudio();
    645      }
    646      catch (error) {
    647        throw(new Error("Browser does not support real time audio output."));
    648      }
    649    }
    650  }
    651 }
    652 XAudioServer.prototype.preInitializeMozAudio = function () {
    653  //mozAudio - Synchronous Audio API
    654  this.audioHandleMoz = new Audio();
    655  this.audioHandleMoz.mozSetup(this.audioChannels, XAudioJSSampleRate);
    656  this.samplesAlreadyWritten = 0;
    657  var emptySampleFrame = (this.audioChannels == 2) ? [0, 0] : [0];
    658  var prebufferAmount = 0;
    659  if (navigator.platform != "MacIntel" && navigator.platform != "MacPPC") {  //Mac OS X doesn't experience this moz-bug!
    660    while (this.audioHandleMoz.mozCurrentSampleOffset() == 0) {
    661      //Mozilla Audio Bugginess Workaround (Firefox freaks out if we don't give it a prebuffer under certain OSes):
    662      prebufferAmount += this.audioHandleMoz.mozWriteAudio(emptySampleFrame);
    663    }
    664    var samplesToDoubleBuffer = prebufferAmount / this.audioChannels;
    665    //Double the prebuffering for windows:
    666    for (var index = 0; index < samplesToDoubleBuffer; index++) {
    667      this.samplesAlreadyWritten += this.audioHandleMoz.mozWriteAudio(emptySampleFrame);
    668    }
    669  }
    670  this.samplesAlreadyWritten += prebufferAmount;
    671  webAudioMinBufferSize += this.samplesAlreadyWritten;
    672  this.mozAudioFound = true;
    673 }
    674 XAudioServer.prototype.initializeMozAudio = function () {
    675  //Fill in our own buffering up to the minimum specified:
    676  this.writeMozAudio(getFloat32(webAudioMinBufferSize));
    677  this.audioType = 0;
    678 }
    679 XAudioServer.prototype.initializeWebAudio = function () {
    680  if (launchedContext) {
    681    resetCallbackAPIAudioBuffer(webAudioActualSampleRate, samplesPerCallback);
    682    this.audioType = 1;
    683  }
    684  else {
    685    throw(new Error(""));
    686  }
    687 }
    688 XAudioServer.prototype.initializeFlashAudio = function () {
    689  var existingFlashload = document.getElementById("XAudioJS");
    690  if (existingFlashload == null) {
    691    var thisObj = this;
    692    var mainContainerNode = document.createElement("div");
    693    mainContainerNode.setAttribute("style", "position: fixed; bottom: 0px; right: 0px; margin: 0px; padding: 0px; border: none; width: 8px; height: 8px; overflow: hidden; z-index: -1000; ");
    694    var containerNode = document.createElement("div");
    695    containerNode.setAttribute("style", "position: static; border: none; width: 0px; height: 0px; visibility: hidden; margin: 8px; padding: 0px;");
    696    containerNode.setAttribute("id", "XAudioJS");
    697    mainContainerNode.appendChild(containerNode);
    698    document.getElementsByTagName("body")[0].appendChild(mainContainerNode);
    699    swfobject.embedSWF(
    700      "XAudioJS.swf",
    701      "XAudioJS",
    702      "8",
    703      "8",
    704      "9.0.0",
    705      "",
    706      {},
    707      {"allowscriptaccess":"always"},
    708      {"style":"position: static; visibility: hidden; margin: 8px; padding: 0px; border: none"},
    709      function (event) {
    710        if (event.success) {
    711          thisObj.audioHandleFlash = event.ref;
    712        }
    713        else {
    714          thisObj.audioType = 1;
    715        }
    716      }
    717    );
    718  }
    719  else {
    720    this.audioHandleFlash = existingFlashload;
    721  }
    722  this.audioType = 2;
    723 }
    724 XAudioServer.prototype.changeVolume = function (newVolume) {
    725  if (newVolume >= 0 && newVolume <= 1) {
    726    XAudioJSVolume = newVolume;
    727    if (this.checkFlashInit()) {
    728      this.audioHandleFlash.changeVolume(XAudioJSVolume);
    729    }
    730    if (this.mozAudioFound) {
    731      this.audioHandleMoz.volume = XAudioJSVolume;
    732    }
    733  }
    734 }
    735 //Moz Audio Buffer Writing Handler:
    736 XAudioServer.prototype.writeMozAudio = function (buffer) {
    737  var length = this.mozAudioTail.length;
    738  if (length > 0) {
    739    var samplesAccepted = this.audioHandleMoz.mozWriteAudio(this.mozAudioTail);
    740    this.samplesAlreadyWritten += samplesAccepted;
    741    this.mozAudioTail.splice(0, samplesAccepted);
    742  }
    743  length = Math.min(buffer.length, webAudioMaxBufferSize - this.samplesAlreadyWritten + this.audioHandleMoz.mozCurrentSampleOffset());
    744  var samplesAccepted = this.audioHandleMoz.mozWriteAudio(buffer);
    745  this.samplesAlreadyWritten += samplesAccepted;
    746  for (var index = 0; length > samplesAccepted; --length) {
    747    //Moz Audio wants us saving the tail:
    748    this.mozAudioTail.push(buffer[index++]);
    749  }
    750 }
    751 //Checks to see if the NPAPI Adobe Flash bridge is ready yet:
    752 XAudioServer.prototype.checkFlashInit = function () {
    753  if (!this.flashInitialized && this.audioHandleFlash && this.audioHandleFlash.initialize) {
    754    this.flashInitialized = true;
    755    this.audioHandleFlash.initialize(this.audioChannels, XAudioJSVolume);
    756    resetCallbackAPIAudioBuffer(44100, samplesPerCallback);
    757  }
    758  return this.flashInitialized;
    759 }
    760 /////////END LIB
    761 function getFloat32(size) {
    762  try {
    763    return new Float32Array(size);
    764  }
    765  catch (error) {
    766    return new Array(size);
    767  }
    768 }
    769 function getFloat32Flat(size) {
    770  try {
    771    var newBuffer = new Float32Array(size);
    772  }
    773  catch (error) {
    774    var newBuffer = new Array(size);
    775    var audioSampleIndice = 0;
    776    do {
    777      newBuffer[audioSampleIndice] = 0;
    778    } while (++audioSampleIndice < size);
    779  }
    780  return newBuffer;
    781 }
    782 //Flash NPAPI Event Handler:
    783 var samplesPerCallback = 2048;      //Has to be between 2048 and 4096 (If over, then samples are ignored, if under then silence is added).
    784 var outputConvert = null;
    785 function audioOutputFlashEvent() {    //The callback that flash calls...
    786  resampleRefill();
    787  return outputConvert();
    788 }
    789 function generateFlashStereoString() {  //Convert the arrays to one long string for speed.
    790  var copyBinaryStringLeft = "";
    791  var copyBinaryStringRight = "";
    792  for (var index = 0; index < samplesPerCallback && resampleBufferStart != resampleBufferEnd; ++index) {
    793    //Sanitize the buffer:
    794    copyBinaryStringLeft += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000);
    795    copyBinaryStringRight += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000);
    796    if (resampleBufferStart == resampleBufferSize) {
    797      resampleBufferStart = 0;
    798    }
    799  }
    800  return copyBinaryStringLeft + copyBinaryStringRight;
    801 }
    802 function generateFlashMonoString() {  //Convert the array to one long string for speed.
    803  var copyBinaryString = "";
    804  for (var index = 0; index < samplesPerCallback && resampleBufferStart != resampleBufferEnd; ++index) {
    805    //Sanitize the buffer:
    806    copyBinaryString += String.fromCharCode(((Math.min(Math.max(resampled[resampleBufferStart++] + 1, 0), 2) * 0x3FFF) | 0) + 0x3000);
    807    if (resampleBufferStart == resampleBufferSize) {
    808      resampleBufferStart = 0;
    809    }
    810  }
    811  return copyBinaryString;
    812 }
    813 //Audio API Event Handler:
    814 var audioContextHandle = null;
    815 var audioNode = null;
    816 var audioSource = null;
    817 var launchedContext = false;
    818 var audioContextSampleBuffer = [];
    819 var resampled = [];
    820 var webAudioMinBufferSize = 15000;
    821 var webAudioMaxBufferSize = 25000;
    822 var webAudioActualSampleRate = 44100;
    823 var XAudioJSSampleRate = 0;
    824 var webAudioMono = false;
    825 var XAudioJSVolume = 1;
    826 var resampleControl = null;
    827 var audioBufferSize = 0;
    828 var resampleBufferStart = 0;
    829 var resampleBufferEnd = 0;
    830 var resampleBufferSize = 2;
    831 function audioOutputEvent(event) {    //Web Audio API callback...
    832  var index = 0;
    833  var buffer1 = event.outputBuffer.getChannelData(0);
    834  var buffer2 = event.outputBuffer.getChannelData(1);
    835  resampleRefill();
    836  if (!webAudioMono) {
    837    //STEREO:
    838    while (index < samplesPerCallback && resampleBufferStart != resampleBufferEnd) {
    839      buffer1[index] = resampled[resampleBufferStart++] * XAudioJSVolume;
    840      buffer2[index++] = resampled[resampleBufferStart++] * XAudioJSVolume;
    841      if (resampleBufferStart == resampleBufferSize) {
    842        resampleBufferStart = 0;
    843      }
    844    }
    845  }
    846  else {
    847    //MONO:
    848    while (index < samplesPerCallback && resampleBufferStart != resampleBufferEnd) {
    849      buffer2[index] = buffer1[index] = resampled[resampleBufferStart++] * XAudioJSVolume;
    850      ++index;
    851      if (resampleBufferStart == resampleBufferSize) {
    852        resampleBufferStart = 0;
    853      }
    854    }
    855  }
    856  //Pad with silence if we're underrunning:
    857  while (index < samplesPerCallback) {
    858    buffer2[index] = buffer1[index] = 0;
    859    ++index;
    860  }
    861 }
    862 function resampleRefill() {
    863  if (audioBufferSize > 0) {
    864    //Resample a chunk of audio:
    865    var resampleLength = resampleControl.resampler(getBufferSamples());
    866    var resampledResult = resampleControl.outputBuffer;
    867    for (var index2 = 0; index2 < resampleLength; ++index2) {
    868      resampled[resampleBufferEnd++] = resampledResult[index2];
    869      if (resampleBufferEnd == resampleBufferSize) {
    870        resampleBufferEnd = 0;
    871      }
    872      if (resampleBufferStart == resampleBufferEnd) {
    873        ++resampleBufferStart;
    874        if (resampleBufferStart == resampleBufferSize) {
    875          resampleBufferStart = 0;
    876        }
    877      }
    878    }
    879    audioBufferSize = 0;
    880  }
    881 }
    882 function resampledSamplesLeft() {
    883  return ((resampleBufferStart <= resampleBufferEnd) ? 0 : resampleBufferSize) + resampleBufferEnd - resampleBufferStart;
    884 }
    885 function getBufferSamples() {
    886  //Typed array and normal array buffer section referencing:
    887  try {
    888    return audioContextSampleBuffer.subarray(0, audioBufferSize);
    889  }
    890  catch (error) {
    891    try {
    892      //Regular array pass:
    893      audioContextSampleBuffer.length = audioBufferSize;
    894      return audioContextSampleBuffer;
    895    }
    896    catch (error) {
    897      //Nightly Firefox 4 used to have the subarray function named as slice:
    898      return audioContextSampleBuffer.slice(0, audioBufferSize);
    899    }
    900  }
    901 }
    902 //Initialize WebKit Audio /Flash Audio Buffer:
    903 function resetCallbackAPIAudioBuffer(APISampleRate, bufferAlloc) {
    904  audioContextSampleBuffer = getFloat32(webAudioMaxBufferSize);
    905  audioBufferSize = webAudioMaxBufferSize;
    906  resampleBufferStart = 0;
    907  resampleBufferEnd = 0;
    908  resampleBufferSize = Math.max(webAudioMaxBufferSize * Math.ceil(XAudioJSSampleRate / APISampleRate), samplesPerCallback) << 1;
    909  if (webAudioMono) {
    910    //MONO Handling:
    911    resampled = getFloat32Flat(resampleBufferSize);
    912    resampleControl = new Resampler(XAudioJSSampleRate, APISampleRate, 1, resampleBufferSize, true);
    913    outputConvert = generateFlashMonoString;
    914  }
    915  else {
    916    //STEREO Handling:
    917    resampleBufferSize  <<= 1;
    918    resampled = getFloat32Flat(resampleBufferSize);
    919    resampleControl = new Resampler(XAudioJSSampleRate, APISampleRate, 2, resampleBufferSize, true);
    920    outputConvert = generateFlashStereoString;
    921  }
    922 }
    923 //Initialize WebKit Audio:
    924 (function () {
    925  if (!launchedContext) {
    926    try {
    927      // The following line was modified for benchmarking:
    928      audioContextHandle = new GameBoyAudioContext();              //Create a system audio context.
    929    }
    930    catch (error) {
    931      try {
    932        audioContextHandle = new AudioContext();                //Create a system audio context.
    933      }
    934      catch (error) {
    935        return;
    936      }
    937    }
    938    try {
    939      audioSource = audioContextHandle.createBufferSource();            //We need to create a false input to get the chain started.
    940      audioSource.loop = false;  //Keep this alive forever (Event handler will know when to ouput.)
    941      XAudioJSSampleRate = webAudioActualSampleRate = audioContextHandle.sampleRate;
    942      audioSource.buffer = audioContextHandle.createBuffer(1, 1, webAudioActualSampleRate);  //Create a zero'd input buffer for the input to be valid.
    943      audioNode = audioContextHandle.createJavaScriptNode(samplesPerCallback, 1, 2);      //Create 2 outputs and ignore the input buffer (Just copy buffer 1 over if mono)
    944      audioNode.onaudioprocess = audioOutputEvent;                //Connect the audio processing event to a handling function so we can manipulate output
    945      audioSource.connect(audioNode);                        //Send and chain the input to the audio manipulation.
    946      audioNode.connect(audioContextHandle.destination);              //Send and chain the output of the audio manipulation to the system audio output.
    947      audioSource.noteOn(0);                            //Start the loop!
    948    }
    949    catch (error) {
    950      return;
    951    }
    952    launchedContext = true;
    953  }
    954 })();
    955 
    956 // End of js/other/XAudioServer.js file.
    957 
    958 // Start of js/other/resize.js file.
    959 
    960 //JavaScript Image Resizer (c) 2012 - Grant Galitz
    961 function Resize(widthOriginal, heightOriginal, targetWidth, targetHeight, blendAlpha, interpolationPass) {
    962  this.widthOriginal = Math.abs(parseInt(widthOriginal) || 0);
    963  this.heightOriginal = Math.abs(parseInt(heightOriginal) || 0);
    964  this.targetWidth = Math.abs(parseInt(targetWidth) || 0);
    965  this.targetHeight = Math.abs(parseInt(targetHeight) || 0);
    966  this.colorChannels = (!!blendAlpha) ? 4 : 3;
    967  this.interpolationPass = !!interpolationPass;
    968  this.targetWidthMultipliedByChannels = this.targetWidth * this.colorChannels;
    969  this.originalWidthMultipliedByChannels = this.widthOriginal * this.colorChannels;
    970  this.originalHeightMultipliedByChannels = this.heightOriginal * this.colorChannels;
    971  this.widthPassResultSize = this.targetWidthMultipliedByChannels * this.heightOriginal;
    972  this.finalResultSize = this.targetWidthMultipliedByChannels * this.targetHeight;
    973  this.initialize();
    974 }
    975 Resize.prototype.initialize = function () {
    976  //Perform some checks:
    977  if (this.widthOriginal > 0 && this.heightOriginal > 0 && this.targetWidth > 0 && this.targetHeight > 0) {
    978    if (this.widthOriginal == this.targetWidth) {
    979      //Bypass the width resizer pass:
    980      this.resizeWidth = this.bypassResizer;
    981    }
    982    else {
    983      //Setup the width resizer pass:
    984      this.ratioWeightWidthPass = this.widthOriginal / this.targetWidth;
    985      if (this.ratioWeightWidthPass < 1 && this.interpolationPass) {
    986        this.initializeFirstPassBuffers(true);
    987        this.resizeWidth = (this.colorChannels == 4) ? this.resizeWidthInterpolatedRGBA : this.resizeWidthInterpolatedRGB;
    988      }
    989      else {
    990        this.initializeFirstPassBuffers(false);
    991        this.resizeWidth = (this.colorChannels == 4) ? this.resizeWidthRGBA : this.resizeWidthRGB;
    992      }
    993    }
    994    if (this.heightOriginal == this.targetHeight) {
    995      //Bypass the height resizer pass:
    996      this.resizeHeight = this.bypassResizer;
    997    }
    998    else {
    999      //Setup the height resizer pass:
   1000      this.ratioWeightHeightPass = this.heightOriginal / this.targetHeight;
   1001      if (this.ratioWeightHeightPass < 1 && this.interpolationPass) {
   1002        this.initializeSecondPassBuffers(true);
   1003        this.resizeHeight = this.resizeHeightInterpolated;
   1004      }
   1005      else {
   1006        this.initializeSecondPassBuffers(false);
   1007        this.resizeHeight = (this.colorChannels == 4) ? this.resizeHeightRGBA : this.resizeHeightRGB;
   1008      }
   1009    }
   1010  }
   1011  else {
   1012    throw(new Error("Invalid settings specified for the resizer."));
   1013  }
   1014 }
   1015 Resize.prototype.resizeWidthRGB = function (buffer) {
   1016  var ratioWeight = this.ratioWeightWidthPass;
   1017  var weight = 0;
   1018  var amountToNext = 0;
   1019  var actualPosition = 0;
   1020  var currentPosition = 0;
   1021  var line = 0;
   1022  var pixelOffset = 0;
   1023  var outputOffset = 0;
   1024  var nextLineOffsetOriginalWidth = this.originalWidthMultipliedByChannels - 2;
   1025  var nextLineOffsetTargetWidth = this.targetWidthMultipliedByChannels - 2;
   1026  var output = this.outputWidthWorkBench;
   1027  var outputBuffer = this.widthBuffer;
   1028  do {
   1029    for (line = 0; line < this.originalHeightMultipliedByChannels;) {
   1030      output[line++] = 0;
   1031      output[line++] = 0;
   1032      output[line++] = 0;
   1033    }
   1034    weight = ratioWeight;
   1035    do {
   1036      amountToNext = 1 + actualPosition - currentPosition;
   1037      if (weight >= amountToNext) {
   1038        for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
   1039          output[line++] += buffer[pixelOffset++] * amountToNext;
   1040          output[line++] += buffer[pixelOffset++] * amountToNext;
   1041          output[line++] += buffer[pixelOffset] * amountToNext;
   1042        }
   1043        currentPosition = actualPosition = actualPosition + 3;
   1044        weight -= amountToNext;
   1045      }
   1046      else {
   1047        for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
   1048          output[line++] += buffer[pixelOffset++] * weight;
   1049          output[line++] += buffer[pixelOffset++] * weight;
   1050          output[line++] += buffer[pixelOffset] * weight;
   1051        }
   1052        currentPosition += weight;
   1053        break;
   1054      }
   1055    } while (weight > 0 && actualPosition < this.originalWidthMultipliedByChannels);
   1056    for (line = 0, pixelOffset = outputOffset; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {
   1057      outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
   1058      outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
   1059      outputBuffer[pixelOffset] = output[line++] / ratioWeight;
   1060    }
   1061    outputOffset += 3;
   1062  } while (outputOffset < this.targetWidthMultipliedByChannels);
   1063  return outputBuffer;
   1064 }
   1065 Resize.prototype.resizeWidthInterpolatedRGB = function (buffer) {
   1066  var ratioWeight = (this.widthOriginal - 1) / this.targetWidth;
   1067  var weight = 0;
   1068  var finalOffset = 0;
   1069  var pixelOffset = 0;
   1070  var outputBuffer = this.widthBuffer;
   1071  for (var targetPosition = 0; targetPosition < this.targetWidthMultipliedByChannels; targetPosition += 3, weight += ratioWeight) {
   1072    //Calculate weightings:
   1073    secondWeight = weight % 1;
   1074    firstWeight = 1 - secondWeight;
   1075    //Interpolate:
   1076    for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * 3; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
   1077      outputBuffer[finalOffset] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 3] * secondWeight);
   1078      outputBuffer[finalOffset + 1] = (buffer[pixelOffset + 1] * firstWeight) + (buffer[pixelOffset + 4] * secondWeight);
   1079      outputBuffer[finalOffset + 2] = (buffer[pixelOffset + 2] * firstWeight) + (buffer[pixelOffset + 5] * secondWeight);
   1080    }
   1081  }
   1082  return outputBuffer;
   1083 }
   1084 Resize.prototype.resizeWidthRGBA = function (buffer) {
   1085  var ratioWeight = this.ratioWeightWidthPass;
   1086  var weight = 0;
   1087  var amountToNext = 0;
   1088  var actualPosition = 0;
   1089  var currentPosition = 0;
   1090  var line = 0;
   1091  var pixelOffset = 0;
   1092  var outputOffset = 0;
   1093  var nextLineOffsetOriginalWidth = this.originalWidthMultipliedByChannels - 3;
   1094  var nextLineOffsetTargetWidth = this.targetWidthMultipliedByChannels - 3;
   1095  var output = this.outputWidthWorkBench;
   1096  var outputBuffer = this.widthBuffer;
   1097  do {
   1098    for (line = 0; line < this.originalHeightMultipliedByChannels;) {
   1099      output[line++] = 0;
   1100      output[line++] = 0;
   1101      output[line++] = 0;
   1102      output[line++] = 0;
   1103    }
   1104    weight = ratioWeight;
   1105    do {
   1106      amountToNext = 1 + actualPosition - currentPosition;
   1107      if (weight >= amountToNext) {
   1108        for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
   1109          output[line++] += buffer[pixelOffset++] * amountToNext;
   1110          output[line++] += buffer[pixelOffset++] * amountToNext;
   1111          output[line++] += buffer[pixelOffset++] * amountToNext;
   1112          output[line++] += buffer[pixelOffset] * amountToNext;
   1113        }
   1114        currentPosition = actualPosition = actualPosition + 4;
   1115        weight -= amountToNext;
   1116      }
   1117      else {
   1118        for (line = 0, pixelOffset = actualPosition; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetOriginalWidth) {
   1119          output[line++] += buffer[pixelOffset++] * weight;
   1120          output[line++] += buffer[pixelOffset++] * weight;
   1121          output[line++] += buffer[pixelOffset++] * weight;
   1122          output[line++] += buffer[pixelOffset] * weight;
   1123        }
   1124        currentPosition += weight;
   1125        break;
   1126      }
   1127    } while (weight > 0 && actualPosition < this.originalWidthMultipliedByChannels);
   1128    for (line = 0, pixelOffset = outputOffset; line < this.originalHeightMultipliedByChannels; pixelOffset += nextLineOffsetTargetWidth) {
   1129      outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
   1130      outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
   1131      outputBuffer[pixelOffset++] = output[line++] / ratioWeight;
   1132      outputBuffer[pixelOffset] = output[line++] / ratioWeight;
   1133    }
   1134    outputOffset += 4;
   1135  } while (outputOffset < this.targetWidthMultipliedByChannels);
   1136  return outputBuffer;
   1137 }
   1138 Resize.prototype.resizeWidthInterpolatedRGBA = function (buffer) {
   1139  var ratioWeight = (this.widthOriginal - 1) / this.targetWidth;
   1140  var weight = 0;
   1141  var finalOffset = 0;
   1142  var pixelOffset = 0;
   1143  var outputBuffer = this.widthBuffer;
   1144  for (var targetPosition = 0; targetPosition < this.targetWidthMultipliedByChannels; targetPosition += 4, weight += ratioWeight) {
   1145    //Calculate weightings:
   1146    secondWeight = weight % 1;
   1147    firstWeight = 1 - secondWeight;
   1148    //Interpolate:
   1149    for (finalOffset = targetPosition, pixelOffset = Math.floor(weight) * 4; finalOffset < this.widthPassResultSize; pixelOffset += this.originalWidthMultipliedByChannels, finalOffset += this.targetWidthMultipliedByChannels) {
   1150      outputBuffer[finalOffset] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 4] * secondWeight);
   1151      outputBuffer[finalOffset + 1] = (buffer[pixelOffset + 1] * firstWeight) + (buffer[pixelOffset + 5] * secondWeight);
   1152      outputBuffer[finalOffset + 2] = (buffer[pixelOffset + 2] * firstWeight) + (buffer[pixelOffset + 6] * secondWeight);
   1153      outputBuffer[finalOffset + 3] = (buffer[pixelOffset + 3] * firstWeight) + (buffer[pixelOffset + 7] * secondWeight);
   1154    }
   1155  }
   1156  return outputBuffer;
   1157 }
   1158 Resize.prototype.resizeHeightRGB = function (buffer) {
   1159  var ratioWeight = this.ratioWeightHeightPass;
   1160  var weight = 0;
   1161  var amountToNext = 0;
   1162  var actualPosition = 0;
   1163  var currentPosition = 0;
   1164  var pixelOffset = 0;
   1165  var outputOffset = 0;
   1166  var output = this.outputHeightWorkBench;
   1167  var outputBuffer = this.heightBuffer;
   1168  do {
   1169    for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
   1170      output[pixelOffset++] = 0;
   1171      output[pixelOffset++] = 0;
   1172      output[pixelOffset++] = 0;
   1173    }
   1174    weight = ratioWeight;
   1175    do {
   1176      amountToNext = 1 + actualPosition - currentPosition;
   1177      if (weight >= amountToNext) {
   1178        for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
   1179          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
   1180          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
   1181          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
   1182        }
   1183        currentPosition = actualPosition;
   1184        weight -= amountToNext;
   1185      }
   1186      else {
   1187        for (pixelOffset = 0, amountToNext = actualPosition; pixelOffset < this.targetWidthMultipliedByChannels;) {
   1188          output[pixelOffset++] += buffer[amountToNext++] * weight;
   1189          output[pixelOffset++] += buffer[amountToNext++] * weight;
   1190          output[pixelOffset++] += buffer[amountToNext++] * weight;
   1191        }
   1192        currentPosition += weight;
   1193        break;
   1194      }
   1195    } while (weight > 0 && actualPosition < this.widthPassResultSize);
   1196    for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
   1197      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
   1198      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
   1199      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
   1200    }
   1201  } while (outputOffset < this.finalResultSize);
   1202  return outputBuffer;
   1203 }
   1204 Resize.prototype.resizeHeightInterpolated = function (buffer) {
   1205  var ratioWeight = (this.heightOriginal - 1) / this.targetHeight;
   1206  var weight = 0;
   1207  var finalOffset = 0;
   1208  var pixelOffset = 0;
   1209  var pixelOffsetAccumulated = 0;
   1210  var pixelOffsetAccumulated2 = 0;
   1211  var outputBuffer = this.heightBuffer;
   1212  do {
   1213    //Calculate weightings:
   1214    secondWeight = weight % 1;
   1215    firstWeight = 1 - secondWeight;
   1216    //Interpolate:
   1217    pixelOffsetAccumulated = Math.floor(weight) * this.targetWidthMultipliedByChannels;
   1218    pixelOffsetAccumulated2 = pixelOffsetAccumulated + this.targetWidthMultipliedByChannels;
   1219    for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels; ++pixelOffset) {
   1220      outputBuffer[finalOffset++] = (buffer[pixelOffsetAccumulated + pixelOffset] * firstWeight) + (buffer[pixelOffsetAccumulated2 + pixelOffset] * secondWeight);
   1221    }
   1222    weight += ratioWeight;
   1223  } while (finalOffset < this.finalResultSize);
   1224  return outputBuffer;
   1225 }
   1226 Resize.prototype.resizeHeightRGBA = function (buffer) {
   1227  var ratioWeight = this.ratioWeightHeightPass;
   1228  var weight = 0;
   1229  var amountToNext = 0;
   1230  var actualPosition = 0;
   1231  var currentPosition = 0;
   1232  var pixelOffset = 0;
   1233  var outputOffset = 0;
   1234  var output = this.outputHeightWorkBench;
   1235  var outputBuffer = this.heightBuffer;
   1236  do {
   1237    for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
   1238      output[pixelOffset++] = 0;
   1239      output[pixelOffset++] = 0;
   1240      output[pixelOffset++] = 0;
   1241      output[pixelOffset++] = 0;
   1242    }
   1243    weight = ratioWeight;
   1244    do {
   1245      amountToNext = 1 + actualPosition - currentPosition;
   1246      if (weight >= amountToNext) {
   1247        for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
   1248          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
   1249          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
   1250          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
   1251          output[pixelOffset++] += buffer[actualPosition++] * amountToNext;
   1252        }
   1253        currentPosition = actualPosition;
   1254        weight -= amountToNext;
   1255      }
   1256      else {
   1257        for (pixelOffset = 0, amountToNext = actualPosition; pixelOffset < this.targetWidthMultipliedByChannels;) {
   1258          output[pixelOffset++] += buffer[amountToNext++] * weight;
   1259          output[pixelOffset++] += buffer[amountToNext++] * weight;
   1260          output[pixelOffset++] += buffer[amountToNext++] * weight;
   1261          output[pixelOffset++] += buffer[amountToNext++] * weight;
   1262        }
   1263        currentPosition += weight;
   1264        break;
   1265      }
   1266    } while (weight > 0 && actualPosition < this.widthPassResultSize);
   1267    for (pixelOffset = 0; pixelOffset < this.targetWidthMultipliedByChannels;) {
   1268      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
   1269      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
   1270      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
   1271      outputBuffer[outputOffset++] = Math.round(output[pixelOffset++] / ratioWeight);
   1272    }
   1273  } while (outputOffset < this.finalResultSize);
   1274  return outputBuffer;
   1275 }
   1276 Resize.prototype.resizeHeightInterpolatedRGBA = function (buffer) {
   1277  var ratioWeight = (this.heightOriginal - 1) / this.targetHeight;
   1278  var weight = 0;
   1279  var finalOffset = 0;
   1280  var pixelOffset = 0;
   1281  var outputBuffer = this.heightBuffer;
   1282  while (pixelOffset < this.finalResultSize) {
   1283    //Calculate weightings:
   1284    secondWeight = weight % 1;
   1285    firstWeight = 1 - secondWeight;
   1286    //Interpolate:
   1287    for (pixelOffset = Math.floor(weight) * 4; pixelOffset < this.targetWidthMultipliedByChannels; pixelOffset += 4) {
   1288      outputBuffer[finalOffset++] = (buffer[pixelOffset] * firstWeight) + (buffer[pixelOffset + 4] * secondWeight);
   1289      outputBuffer[finalOffset++] = (buffer[pixelOffset + 1] * firstWeight) + (buffer[pixelOffset + 5] * secondWeight);
   1290      outputBuffer[finalOffset++] = (buffer[pixelOffset + 2] * firstWeight) + (buffer[pixelOffset + 6] * secondWeight);
   1291      outputBuffer[finalOffset++] = (buffer[pixelOffset + 3] * firstWeight) + (buffer[pixelOffset + 7] * secondWeight);
   1292    }
   1293    weight += ratioWeight;
   1294  }
   1295  return outputBuffer;
   1296 }
   1297 Resize.prototype.resize = function (buffer) {
   1298  return this.resizeHeight(this.resizeWidth(buffer));
   1299 }
   1300 Resize.prototype.bypassResizer = function (buffer) {
   1301  //Just return the buffer passsed:
   1302  return buffer;
   1303 }
   1304 Resize.prototype.initializeFirstPassBuffers = function (BILINEARAlgo) {
   1305  //Initialize the internal width pass buffers:
   1306  this.widthBuffer = this.generateFloatBuffer(this.widthPassResultSize);
   1307  if (!BILINEARAlgo) {
   1308    this.outputWidthWorkBench = this.generateFloatBuffer(this.originalHeightMultipliedByChannels);
   1309  }
   1310 }
   1311 Resize.prototype.initializeSecondPassBuffers = function (BILINEARAlgo) {
   1312  //Initialize the internal height pass buffers:
   1313  this.heightBuffer = this.generateUint8Buffer(this.finalResultSize);
   1314  if (!BILINEARAlgo) {
   1315    this.outputHeightWorkBench = this.generateFloatBuffer(this.targetWidthMultipliedByChannels);
   1316  }
   1317 }
   1318 Resize.prototype.generateFloatBuffer = function (bufferLength) {
   1319  //Generate a float32 typed array buffer:
   1320  try {
   1321    return new Float32Array(bufferLength);
   1322  }
   1323  catch (error) {
   1324    return [];
   1325  }
   1326 }
   1327 Resize.prototype.generateUint8Buffer = function (bufferLength) {
   1328  //Generate a uint8 typed array buffer:
   1329  try {
   1330    return this.checkForOperaMathBug(new Uint8Array(bufferLength));
   1331  }
   1332  catch (error) {
   1333    return [];
   1334  }
   1335 }
   1336 Resize.prototype.checkForOperaMathBug = function (typedArray) {
   1337  typedArray[0] = -1;
   1338  typedArray[0] >>= 0;
   1339  if (typedArray[0] != 0xFF) {
   1340    return [];
   1341  }
   1342  else {
   1343    return typedArray;
   1344  }
   1345 }
   1346 
   1347 // End of js/other/resize.js file.
   1348 
   1349 // Remaining files are in gbemu-part2.js, since they run in strict mode.