swf2jsでswfを直接分解してcanvasに出力する

swfのバイナリをJavaScriptで読み込んでバイナリ分解してcanvasに出力したい。

## 完成品

[ienaga/swf2js](https://github.com/ienaga/swf2js)


## SWF仕様書

SWFの仕様書が公開されているので仕様書を見ながら分解する。

[SWF File Format Specification](http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/swf/pdf/swf_file_format_spec_v10.pdf)

## SWF Investigator

分解された状態を確認できる。便利。

[Adobe SWF Investigator](http://labs.adobe.com/technologies/swfinvestigator/)


## swfを読み込む

~~~js
var xmlHttpRequest = new XMLHttpRequest();

xmlHttpRequest.open('GET', url);

// XMLHttpRequest2が使えるなら使う
isXHR2 = typeof xmlHttpRequest.responseType != 'undefined';

if (isXHR2) {
    xmlHttpRequest.responseType = 'arraybuffer';
} else {
    xmlHttpRequest.overrideMimeType(
        'text/plain; charset=x-user-defined'
    );
}

xmlHttpRequest.send(null);

xmlHttpRequest.onreadystatechange = function()
{

    var readyState = xmlHttpRequest.readyState;

    if (readyState == 4) {

        var status = xmlHttpRequest.status;

        switch (status) {

            case 200:

                // 分解の実装
                parse(isXHR2 ? xmlHttpRequest.response : xmlHttpRequest.responseText, player.parent);
                ...省略
                break;

            case 404:
                alert('Swf Data Not Found');
                break;

            case 500:
                alert('Internal Server Error');
                break;

        }
    }
}
~~~




## バイナリの分解

取得できたバイナリを分解・・・

いやいや、こんなの無知な状態から勉強開始しても全然意味不明すぎる・・・

って事で、バイナリの達人yoyaさんの「swfpl」の「bitio.js」を使って勉強させて頂きます！

[yoya/swfpl](https://github.com/yoya/swfpl)


XMLHttpRequest2に対応していなければバイナリのデータを0から255の数値に変換する
型付き配列(Typed Array)に対応している端末ならTyped Arrayを使用する。

~~~js:bitio.js
    /**
     * BitIO
     * @constructor
     */
    var BitIO = function()
    {
        this.data = null;
        this.bit_offset = 0;
        this.byte_offset = 0;
        this.bit_buffer = null;
    };

    /**
     * init
     * @param binary
     */
    BitIO.prototype.init = function(binary)
    {
        var _this = this;
        var length = binary.length;
        var array = _this.createArray(length);
        var key = 0;
        for (; length--;) {
            array[key] = binary.charCodeAt(key++) & 0xff;
        }
        this.data = array;
    };

    /**
     * createArray
     * @param length
     * @returns {Array}
     */
    BitIO.prototype.createArray = function(length)
    {
        return (window.ArrayBuffer) ? new Uint8Array(length) : [];
    };

    ...省略
~~~

分からない所は使いながらコネコネしながらひたすら学ぶしかない。。。


## SwfTag

バイナリの勉強を進めながら、SWF仕様書に書いてある通りに分解していく。

tagの分解はSWF Investigatorのソースがとても参考になりました。

[sourceforge/SWFInvestigator](http://sourceforge.net/adobe/swfinvestigator/code/17/tree/trunk)



## ベクターの分解と描画

個人的に一番の苦労したのは、ベクターの分解と描画。

SWFのベクターの描画は一回の描画で二色塗るという実装がされている。

![image01.png](https://qiita-image-store.s3.amazonaws.com/0/80502/d2e1ec26-1eea-d29d-7169-56186082bc7f.png)

[参考資料](http://codeazur.com.br/fitc/HackingSWF.pdf)

canvasだとそんなの無理じゃないかな・・・orz

なので、一回描画のデータを2つに分解して描画の座標を結合する事にした。

![image02.png](https://qiita-image-store.s3.amazonaws.com/0/80502/96813b1a-6124-378e-657e-0e101425f416.png)

※ s=開始点、e=終点

次にぶつかった問題は中抜きの描画

![image03.png](https://qiita-image-store.s3.amazonaws.com/0/80502/b59cdf68-8962-ef09-705f-44084c9ee47b.png)

でも・・・

![image04.png](https://qiita-image-store.s3.amazonaws.com/0/80502/eac22fc7-9451-c66c-b320-d5bd0c3296f3.png)

はて？？？

## non-zero rule

いろいろ調べた結果、中抜きするにはnon-zero ruleを使用するしかない。

[参考ページ](http://blogs.adobe.com/webplatform/2013/01/30/winding-rules-in-canvas/)

ようするに0の部分は描画しないらしい。

前提として、時計回りの描画を+1
反時計回りを-1とする。

時計回り(+1)のデータが重なる部分は2となり、描画される。

![image05.png](https://qiita-image-store.s3.amazonaws.com/0/80502/58577b92-850b-3cd2-2738-449857892665.png)

時計回り(+1)の描画と反時計回り(-1)の描画が重なる分は0となり描画されない

![image06.png](https://qiita-image-store.s3.amazonaws.com/0/80502/69fe9e2a-5014-8ef2-9cf6-cff8f34f8f9b.png)

個人的にはこの部分にハマって何回も作るのを断念しそうになった。。。


## Action Script

[2015/08/10-追記]
http://qiita.com/ienaga/items/fc5b25df3b3f2b2f191d

タイムラインのMovieClipの作られた順にアクションを実行していく。

実行中のアクションでフレームの移動があれば移動先のアクションを予約。

移動先の描画前にアクションを実行

これを繰り返えす。

概ねSWF仕様書の通りでActionScriptは実行できている。

が、きっと例外的な挙動もあると思うので、その際は是非コメントください。


## WebGL

まだまだ勉強中・・・

そのうち対応する予定・・・

## flash lite 2.x

現在、地道に実装中☆彡

目標としてバージョン8には対応したい。

※現在はバージョン5か6までなら対応出来てるはず・・・

## デモサイトはこちらから

https://swf2js.wordpress.com/demos/