text: /**
  * @author       Richard Davey <rich@photonstorm.com>
  * @copyright    2016 Photon Storm Ltd.
  * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
  */
  
  /**
  * Phaser.LoaderParser parses data objects from Phaser.Loader that need more preparation before they can be inserted into the Cache.
  *
  * @class Phaser.LoaderParser
  */
  Phaser.LoaderParser = {
  
      /**
      * Alias for xmlBitmapFont, for backwards compatibility.
      *
      * @method Phaser.LoaderParser.bitmapFont
      * @param {object} xml - XML data you want to parse.
      * @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
      * @param {number} [xSpacing=0] - Additional horizontal spacing between the characters.
      * @param {number} [ySpacing=0] - Additional vertical spacing between the characters.
      * @param {Phaser.Frame} [frame] - Optional Frame, if this font is embedded in a texture atlas.
      * @param {number} [resolution=1] - Optional game resolution to apply to the kerning data.
      * @return {object} The parsed Bitmap Font data.
      */
      bitmapFont: function (xml, baseTexture, xSpacing, ySpacing, frame, resolution)
      {
  
          return this.xmlBitmapFont(xml, baseTexture, xSpacing, ySpacing, frame, resolution);
  
      },
  
      /**
      * Parse a Bitmap Font from an XML file.
      *
      * @method Phaser.LoaderParser.xmlBitmapFont
      * @param {object} xml - XML data you want to parse.
      * @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
      * @param {number} [xSpacing=0] - Additional horizontal spacing between the characters.
      * @param {number} [ySpacing=0] - Additional vertical spacing between the characters.
      * @param {Phaser.Frame} [frame] - Optional Frame, if this font is embedded in a texture atlas.
      * @param {number} [resolution=1] - Optional game resolution to apply to the kerning data.
      * @return {object} The parsed Bitmap Font data.
      */
      xmlBitmapFont: function (xml, baseTexture, xSpacing, ySpacing, frame, resolution)
      {
  
          if (resolution == null)
          {
              resolution = 1;
          }
  
          var data = {};
          var info = xml.getElementsByTagName('info')[0];
          var common = xml.getElementsByTagName('common')[0];
  
          data.font = info.getAttribute('face');
          data.size = parseInt(info.getAttribute('size'), 10);
          data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) + ySpacing;
          data.chars = {};
  
          var letters = xml.getElementsByTagName('char');
  
          var x = (frame) ? frame.x : 0;
          var y = (frame) ? frame.y : 0;
  
          for (var i = 0; i < letters.length; i++)
          {
              var charCode = parseInt(letters[i].getAttribute('id'), 10);
  
              data.chars[charCode] = {
                  x: x + parseInt(letters[i].getAttribute('x'), 10),
                  y: y + parseInt(letters[i].getAttribute('y'), 10),
                  width: parseInt(letters[i].getAttribute('width'), 10),
                  height: parseInt(letters[i].getAttribute('height'), 10),
                  xOffset: parseInt(letters[i].getAttribute('xoffset'), 10) / resolution,
                  yOffset: parseInt(letters[i].getAttribute('yoffset'), 10) / resolution,
                  xAdvance: (parseInt(letters[i].getAttribute('xadvance'), 10) + xSpacing) / resolution,
                  kerning: {}
              };
          }
  
          var kernings = xml.getElementsByTagName('kerning');
  
          for (i = 0; i < kernings.length; i++)
          {
              var first = parseInt(kernings[i].getAttribute('first'), 10);
              var second = parseInt(kernings[i].getAttribute('second'), 10);
              var amount = parseInt(kernings[i].getAttribute('amount'), 10) / resolution;
  
              data.chars[second].kerning[first] = amount;
          }
  
          return this.finalizeBitmapFont(baseTexture, data);
  
      },
  
      /**
      * Parse a Bitmap Font from a JSON file.
      *
      * @method Phaser.LoaderParser.jsonBitmapFont
      * @param {object} json - JSON data you want to parse.
      * @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
      * @param {number} [xSpacing=0] - Additional horizontal spacing between the characters.
      * @param {number} [ySpacing=0] - Additional vertical spacing between the characters.
      * @param {Phaser.Frame} [frame] - Optional Frame, if this font is embedded in a texture atlas.
      * @param {number} [resolution=1] - Optional game resolution to apply to the kerning data.
      * @return {object} The parsed Bitmap Font data.
      */
      jsonBitmapFont: function (json, baseTexture, xSpacing, ySpacing, frame, resolution)
      {
  
          if (resolution == null)
          {
              resolution = 1;
          }
  
          var data = {
              font: json.font.info._face,
              size: parseInt(json.font.info._size, 10),
              lineHeight: parseInt(json.font.common._lineHeight, 10) + ySpacing,
              chars: {}
          };
  
          var x = (frame) ? frame.x : 0;
          var y = (frame) ? frame.y : 0;
  
          json.font.chars.char.forEach(
  
              function parseChar (letter)
              {
  
                  var charCode = parseInt(letter._id, 10);
  
                  data.chars[charCode] = {
                      x: x + parseInt(letter._x, 10),
                      y: y + parseInt(letter._y, 10),
                      width: parseInt(letter._width, 10),
                      height: parseInt(letter._height, 10),
                      xOffset: parseInt(letter._xoffset, 10) / resolution,
                      yOffset: parseInt(letter._yoffset, 10) / resolution,
                      xAdvance: (parseInt(letter._xadvance, 10) + xSpacing) / resolution,
                      kerning: {}
                  };
              }
  
          );
  
          if (json.font.kernings && json.font.kernings.kerning)
          {
              json.font.kernings.kerning.forEach(
  
                  function parseKerning (kerning)
                  {
  
                      data.chars[kerning._second].kerning[kerning._first] = parseInt(kerning._amount, 10) / resolution;
  
                  }
  
              );
          }
  
          return this.finalizeBitmapFont(baseTexture, data);
  
      },
  
      /**
      * Finalize Bitmap Font parsing.
      *
      * @method Phaser.LoaderParser.finalizeBitmapFont
      * @private
      * @param {PIXI.BaseTexture} baseTexture - The BaseTexture this font uses.
      * @param {object} bitmapFontData - Pre-parsed bitmap font data.
      * @return {object} The parsed Bitmap Font data.
      */
      finalizeBitmapFont: function (baseTexture, bitmapFontData)
      {
  
          Object.keys(bitmapFontData.chars).forEach(
  
              function addTexture (charCode)
              {
  
                  var letter = bitmapFontData.chars[charCode];
  
                  letter.texture = new PIXI.Texture(baseTexture, new Phaser.Rectangle(letter.x, letter.y, letter.width, letter.height));
  
              }
  
          );
  
          return bitmapFontData;
  
      },
  
      /**
      * Extract PVR header from loaded binary
      *
      * @method Phaser.LoaderParser.pvr
      * @param {ArrayBuffer} arrayBuffer
      * @return {object} The parsed PVR file including texture data.
      */
      pvr: function (arrayBuffer)
      {
  
          // Reference: http://cdn.imgtec.com/sdk-documentation/PVR+File+Format.Specification.pdf
          // PVR 3 header structure
          // ---------------------------------------
          // address: 0, size: 4 bytes version
          // address: 4, size: 4 bytes flags
          // address: 8, size: 8 bytes pixel format
          // address: 16, size: 4 bytes color space
          // address: 20, size: 4 bytes channel type
          // address: 24, size: 4 bytes height
          // address: 28, size: 4 bytes width
          // address: 32, size: 4 bytes depth
          // address: 36, size: 4 bytes number of surfaces
          // address: 40, size: 4 bytes number of faces
          // address: 44, size: 4 bytes number of mipmaps
          // address: 48, size: 4 bytes meta data size
          // ---------------------------------------
          var uintArray = new Uint32Array(arrayBuffer.slice(0, 52)),
              byteArray = new Uint8Array(arrayBuffer),
              pvrHeader = null,
              pixelFormat = (uintArray[3] << 32 | uintArray[2]),
              compressionAlgorithm,
              glExtensionFormat = 0;
  
          if (uintArray[0] === 0x03525650 &&
              [ // Validate WebGL Pixel Format
                  0, 1, 2, 3,
                  6, 7, 9, 11
              ].indexOf(pixelFormat) >= 0
          )
          {
              if (pixelFormat >= 0 && pixelFormat <= 3)
              {
                  compressionAlgorithm = 'PVRTC';
              }
              else if (pixelFormat >= 7 && pixelFormat <= 11)
              {
                  compressionAlgorithm = 'S3TC';
              }
              else if (pixelFormat === 6)
              {
                  compressionAlgorithm = 'ETC1';
              }
  
              switch (pixelFormat)
              {
                  case 0:
                      glExtensionFormat = 0x8C01;
                      break;
                  case 1:
                      glExtensionFormat = 0x8C03;
                      break;
                  case 2:
                      glExtensionFormat = 0x8C00;
                      break;
                  case 3:
                      glExtensionFormat = 0x8C02;
                      break;
                  case 6:
                      glExtensionFormat = 0x8D64;
                      break;
                  case 7:
                      glExtensionFormat = 0x83F1;
                      break;
                  case 9:
                      glExtensionFormat = 0x83F2;
                      break;
                  case 11:
                      glExtensionFormat = 0x83F3;
                      break;
                  default:
                      glExtensionFormat = -1;
              }
  
              pvrHeader = {
                  complete: true,
                  fileFormat: 'PVR',
                  compressionAlgorithm: compressionAlgorithm,
                  flags: uintArray[1],
                  pixelFormat: pixelFormat,
                  colorSpace: uintArray[4],
                  channelType: uintArray[5],
                  height: uintArray[6],
                  width: uintArray[7],
                  depth: uintArray[8],
                  numberOfSurfaces: uintArray[9],
                  numberOfFaces: uintArray[10],
                  numberOfMipmaps: uintArray[11],
                  metaDataSize: uintArray[12],
                  textureData: byteArray.subarray(52 + uintArray[12], byteArray.byteLength),
                  glExtensionFormat: glExtensionFormat
              };
          }
  
          return pvrHeader;
  
      },
  
      /**
      * Extract DDS header from loaded binary
      *
      * @method Phaser.LoaderParser.dds
      * @param {ArrayBuffer} arrayBuffer
      * @return {object} The parsed DDS file including texture data.
      */
      dds: function (arrayBuffer)
      {
  
          // Reference at: https://msdn.microsoft.com/en-us/library/windows/desktop/bb943982(v=vs.85).aspx
          // DDS header structure
          // ---------------------------------------
          // address: 0, size: 4 bytes Identifier 'DDS '
          // address: 4, size: 4 bytes size
          // address: 8, size: 4 bytes flags
          // address: 12, size: 4 bytes height
          // address: 16, size: 4 bytes width
          // address: 20, size: 4 bytes pitch or linear size
          // address: 24, size: 4 bytes depth
          // address: 28, size: 4 bytes mipmap count
          // address: 32, size: 44 bytes reserved space 1
          // address: 76, size: 4 pixel format size
          // address: 80, size: 4 pixel format flag
          // address: 84, size: 4 pixel format four CC
          // address: 88, size: 4 pixel format bit count
          // address: 92, size: 4 pixel format R bit mask
          // address: 96, size: 4 pixel format G bit mask
          // address: 100, size: 4 pixel format B bit mask
          // address: 104, size: 4 pixel format A bit mask
          // address: 108, size: 4 caps 1
          // address: 112, size: 4 caps 2
          // address: 116, size: 4 caps 3
          // address: 120, size: 4 caps 4
          // address: 124, size: 4 reserved 2
          // -- DXT10 extension
          // address: 130, size: 4 DXGI Format
          // address: 134, size: 4 resource dimension
          // address: 138, size: 4 misc flag
          // address: 142, size: 4 array size
          // address: 146, size: 4 misc flag 2
          // ---------------------------------------
          var byteArray = new Uint8Array(arrayBuffer),
              uintArray = new Uint32Array(arrayBuffer),
              ddsHeader = null;
  
          if (byteArray[0] === 0x44 &&
              byteArray[1] === 0x44 &&
              byteArray[2] === 0x53 &&
              byteArray[3] === 0x20)
          {
              ddsHeader = {
                  complete: true,
                  fileFormat: 'DDS',
                  compressionAlgorithm: 'S3TC',
                  size: uintArray[1],
                  flags: uintArray[2],
                  height: uintArray[3],
                  width: uintArray[4],
                  pitch: uintArray[5],
                  depth: uintArray[6],
                  mipmapCount: uintArray[7],
                  formatSize: uintArray[19],
                  formatFlag: uintArray[19],
                  formatFourCC: [
                      String.fromCharCode(byteArray[84]),
                      String.fromCharCode(byteArray[85]),
                      String.fromCharCode(byteArray[86]),
                      String.fromCharCode(byteArray[87])
                  ].join(''),
                  formatBitCount: uintArray[21],
                  formatRBitMask: uintArray[22],
                  formatGBitMask: uintArray[23],
                  formatBBitMask: uintArray[24],
                  formatABitMask: uintArray[25],
                  caps1: uintArray[26],
                  caps2: uintArray[27],
                  caps3: uintArray[28],
                  caps4: uintArray[29],
                  reserved2: uintArray[30],
                  DXGIFormat: null,
                  resourceDimension: null,
                  miscFlag: null,
                  arraySize: null,
                  textureData: byteArray.subarray(uintArray[1] + 4, byteArray.byteLength)
              };
              if (ddsHeader.formatFourCC === 'DX10')
              {
                  ddsHeader.DXGIFormat = uintArray[31];
                  ddsHeader.resourceDimension = uintArray[32];
                  ddsHeader.miscFlag = uintArray[33];
                  ddsHeader.arraySize = uintArray[34];
                  ddsHeader.miscFlag = uintArray[35];
              }
              else if(ddsHeader.formatFourCC === 'DXT5'){
                  ddsHeader.glExtensionFormat = 0x83F3;
              }
              else if(ddsHeader.formatFourCC === 'DXT3'){
                  ddsHeader.glExtensionFormat = 0x83F2;
              }
          }
  
          return ddsHeader;
  
      },
  
      /**
      * Extract KTX header from loaded binary
      *
      * @method Phaser.LoaderParser.ktx
      * @param {ArrayBuffer} arrayBuffer
      * @return {object} The parsed KTX file including texture data.
      */
      ktx: function (arrayBuffer)
      {
  
          // Reference: https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
          // KTX header structure
          // ---------------------------------------
          // address: 0, size 12 bytes: Identifier '«KTX 11»\r\n\x1A\n'
          // address: 12, size 4 bytes: endianness
          // address: 16, size 4 bytes: GL type
          // address: 20, size 4 bytes: GL type size
          // address: 24, size 4 bytes: GL format
          // address: 28, size 4 bytes: GL internal format
          // address: 32, size 4 bytes: GL base internal format
          // address: 36, size 4 bytes: pixel width
          // address: 40, size 4 bytes: pixel height
          // address: 44, size 4 bytes: pixel depth
          // address: 48, size 4 bytes: number of array elements
          // address: 52, size 4 bytes: number of faces
          // address: 56, size 4 bytes: number of mipmap levels
          // address: 60, size 4 bytes: bytes of key value data
          // address: 64, size 4 bytes: key and value bytes size
          // address: X, size 1 byte : key and value
          // address: X, size 1 byte : value padding
          // address: X, size 4 byte : image size
          // ---------------------------------------
          var byteArray = new Uint8Array(arrayBuffer),
              uintArray = new Uint32Array(arrayBuffer),
              ktxHeader = null,
              imageSizeOffset = 16 + (uintArray[15] / 4) | 0,
              imageSize = uintArray[imageSizeOffset],
              glInternalFormat = uintArray[7],
              compressionAlgorithm = 0;
  
          if (byteArray[0] === 0xAB && byteArray[1] === 0x4B &&
              byteArray[2] === 0x54 && byteArray[3] === 0x58 &&
              byteArray[4] === 0x20 && byteArray[5] === 0x31 &&
              byteArray[6] === 0x31 && byteArray[7] === 0xBB &&
              byteArray[8] === 0x0D && byteArray[9] === 0x0A &&
              byteArray[10] === 0x1A && byteArray[11] === 0x0A &&
  
              // Check if internal GL format is supported by WebGL
              [
                  // ETC1
                  0x8D64,
  
                  // PVRTC
                  0x8C00, 0x8C01, 0x8C02, 0x8C03,
  
                  // DXTC | S3TC
                  0x83F0, 0x83F1, 0x83F2, 0x83F3
              ].indexOf(glInternalFormat) >= 0)
          {
              switch (glInternalFormat)
              {
                  case 0x8D64:
                      compressionAlgorithm = 'ETC1';
                      break;
                  case 0x8C00:
                  case 0x8C01:
                  case 0x8C02:
                  case 0x8C03:
                      compressionAlgorithm = 'PVRTC';
                      break;
                  case 0x83F0:
                  case 0x83F1:
                  case 0x83F2:
                  case 0x83F3:
                      compressionAlgorithm = 'S3TC';
                      break;
              }
  
              ktxHeader = {
                  complete: true,
                  fileFormat: 'KTX',
                  compressionAlgorithm: compressionAlgorithm,
                  endianness: uintArray[3],
                  glType: uintArray[4],
                  glTypeSize: uintArray[5],
                  glFormat: uintArray[6],
                  glInternalFormat: uintArray[7],
                  glBaseInternalFormat: uintArray[8],
                  width: uintArray[9],
                  height: uintArray[10],
                  pixelDepth: uintArray[11],
                  numberOfArrayElements: uintArray[12],
                  numberOfFaces: uintArray[13],
                  numberOfMipmapLevels: uintArray[14],
                  bytesOfKeyValueData: uintArray[15],
                  keyAndValueByteSize: uintArray[16],
                  imageSize: imageSize,
                  textureData: byteArray.subarray((imageSizeOffset + 1) * 4, imageSize + 100)
              };
          }
  
          return ktxHeader;
  
      },
  
      /**
      * Extract PKM header from loaded binary
      *
      * @method Phaser.LoaderParser.pkm
      * @param {ArrayBuffer} arrayBuffer
      * @return {object} The parsed PKM file including texture data.
      */
      pkm: function (arrayBuffer)
      {
  
          // PKM header structure
          // ---------------------------------------
          // address: 0, size 4 bytes: for 'PKM '
          // address: 4, size 2 bytes: for version
          // address: 6, size 2 bytes: for type
          // address: 8, size 2 bytes: for extended width
          // address: 10, size 2 bytes: for extended height
          // address: 12, size 2 bytes: for original width
          // address: 14, size 2 bytes: for original height
          // address: 16, texture data
          // ---------------------------------------
          var byteArray = new Uint8Array(arrayBuffer),
              pkmHeader = null;
  
          if (byteArray[0] === 0x50 &&
              byteArray[1] === 0x4B &&
              byteArray[2] === 0x4D &&
              byteArray[3] === 0x20)
          {
  
              pkmHeader = {
                  complete: true,
                  fileFormat: 'PKM',
                  compressionAlgorithm: 'ETC1',
                  format: ((byteArray[6] << 8 | byteArray[7])) & 0xFFFF,
                  width: ((byteArray[8] << 8 | byteArray[9])) & 0xFFFF,
                  height: ((byteArray[10] << 8 | byteArray[11])) & 0xFFFF,
                  originalWidth: ((byteArray[12] << 8 | byteArray[13])) & 0xFFFF,
                  originalHeight: ((byteArray[14] << 8 | byteArray[15])) & 0xFFFF,
                  textureData: byteArray.subarray(16, byteArray.length)
              };
          }
  
          return pkmHeader;
  
      }
  
  };
  