.ico decompiler

by sii_side

HTML

<input type="file" id="ico">

<h1>Canvasで読み込み(1つだけ)</h1>
<canvas id="c" width="300" height="300"></canvas>

<h1>imgで読み込み(全部)</h1>
<div id="op"></div>

CSS

body {
  background-color: #999;
}

JavaScript

var ipt = document.querySelector("#ico");

ipt.addEventListener("change", function() {
  /*--------------------
    .icoファイルを読み込んで解析する
  --------------------*/
  var ico = ipt.files[0];

  var fr = new FileReader();
  fr.addEventListener("load", function() {
    var u   = new Uint8Array(fr.result);

    var num = u[4];
    //5バイト目:.icoに含まれるアイコンの数

    var mIcon = [];
    /*
      icon解析用オブジェクト
      [
        {
          header: (IconInfoHeader),
          icon  : {
            header: (BitmapInfoHeader),
            imageData: (画像データ)
          }
        }, ...
      ]
    */

    for(var i = 0; i < num; i++) {
      var hStart = 6 + i * 16;
      //各IconInfoHeaderの始点(.ico先頭7バイト目から16バイト単位で続く)

      var iconObj = {};

      iconObj.header = {};
      iconObj.header.binary       = u.slice(hStart, hStart + 16);
      iconObj.header.width        = u[hStart     ] || 256;
      iconObj.header.height       = u[hStart +  1] || 256;
      iconObj.header.icoDIBSize   = u[hStart +  8] + u[hStart +  9] * 256 + u[hStart + 10] * 256 * 256 +  + u[hStart + 11] * 256 * 256 * 256;
      //IconInfoHeader09-12バイト目:サイズ(容量)
      iconObj.header.icoDIBOffset = u[hStart + 12] + u[hStart + 13] * 256 + u[hStart + 14] * 256 * 256 +  + u[hStart + 15] * 256 * 256 * 256;
      //IconInfoHeader13-16バイト目:位置(.icoファイル内でアイコンのデータ(ヘッダ含む)が何バイト目から始まっているか)

      iconObj.icon = {};
      iconObj.icon.binary    = u.slice(iconObj.header.icoDIBOffset, iconObj.header.icoDIBOffset + iconObj.header.icoDIBSize);
      iconObj.icon.header    = u.slice(iconObj.header.icoDIBOffset, iconObj.header.icoDIBOffset + 40);
      //BitmapInfoHeader(とりあえず使わないのでUint8Arrayのまま)
      iconObj.icon.imageData = u.slice(iconObj.header.icoDIBOffset + 40, iconObj.header.icoDIBOffset + 40 + iconObj.header.width * iconObj.header.height * 4);
      //ImageData(MaskDataは無視)
      //(得られるバイナリはRGBAではなくBGRAの順(エンディアンの関係?))

      mIcon.push(iconObj);
    }

    //サイズ順にソート(降順なので先頭が最大)
    //(バイト数で比較しているので必ずしも解像度順とは限らない)
   ...