LoginSignup
2
2

More than 5 years have passed since last update.

swf2jsでswf内でzlib形式で圧縮されたデータを解凍する

Posted at

swfのバージョン6に対応したい。

手始めに圧縮されたswfをJSで解凍する。

課題

  • 画像データは基本zlib圧縮されている
  • バージョン6からはswf本体をzlib圧縮して出力ができる

完成品

ienaga/swf2js

参考にしたサイト

分解する時の注意点

* 先頭の2bytesはzlibヘッダなので無視する。
* swfの解凍時は2bytesがヘッダーで8bytesがswfのヘッダー情報。(ここで結構時間がかかったorz)

メタデータに関する符号
型付き配列(Typed Array)に対応している端末ならTyped Arrayを使用する。


var ORDER = 
    [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];

var LEXT = [
    0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
    3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
];

 var LENS = [
    3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
    35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
 ];

var DEXT = [
    0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
    7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
];

var DISTS = [
    1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
    257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
    8193, 12289, 16385, 24577
];

if (isArrayBuffer) {
    ORDER = new Uint8Array(ORDER);
    CODE_EXT = new Uint8Array(CODE_EXT);
    CODE_LENGTH = new Uint16Array(CODE_LENGTH);
    DIST_EXT = new Uint8Array(DIST_EXT);
    DIST_CODE = new Uint16Array(DIST_CODE);
}

BitIOを拡張

※yoyaさんの「bitio.js」を使わせていただきました。
yoya/swfpl

/**
 * readUB
 * @param length
 * @returns {number}
 */
BitIO.prototype.readUB = function(length)
{
    var _this = this;
    var value = 0;
    for (var i = 0; i < length; i++) {
        if (_this.bit_offset == 8) {
            _this.bit_buffer = _this.readNumber(1);
            _this.bit_offset = 0;
        }
        value |= (_this.bit_buffer & (0x01 << _this.bit_offset++) ? 1 : 0) << i;
    }
    return value;
};

/**
 * readNumber
 * @returns {number}
 */
BitIO.prototype.readNumber = function(n)
{
    var _this = this;
    var value = 0;
    var o = _this.byte_offset;
    var i = o + n;
    for(; i > o; ){
        value = (value << 8) | _this.data[--i];
    }
    _this.byte_offset += n;
    return value;
};

解凍

    for (; !done; ) {
        var done = zBitio.readUB(1);
        var type = zBitio.readUB(2);

        var distTable = {};
        var litTable = {};
        var fixedDistTable = false;
        var fixedLitTable = false;

        if (type) {
            if (type == 1) {
                distTable = fixedDistTable;
                litTable = fixedLitTable;

                if (!distTable) {
                    bitLengths = [];
                    for(i = 32; i--;){
                        bitLengths[bitLengths.length] = 5;
                    }
                    distTable = fixedDistTable = _buildHuffTable(bitLengths);
                }

                if (!litTable) {
                    bitLengths = [];
                    i = 0;

                    for(; i < 144; i++){
                        bitLengths[bitLengths.length] = 8;
                    }

                    for(; i < 256; i++){
                        bitLengths[bitLengths.length] = 9;
                    }

                    for(; i < 280; i++){
                        bitLengths[bitLengths.length] = 7;
                    }

                    for(; i < 288; i++){
                        bitLengths[bitLengths.length] = 8;
                    }

                    litTable = fixedLitTable = _buildHuffTable(bitLengths);
                }
            } else {
                var numLitLengths = zBitio.readUB(5) + 257;
                var numDistLengths = zBitio.readUB(5) + 1;
                var numCodeLengths = zBitio.readUB(4) + 4;
                var codeLengths = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
                if (isArrayBuffer) {
                    codeLengths = new Uint8Array(codeLengths);
                }

                for(i = 0; i < numCodeLengths; i++){
                    codeLengths[ORDER[i]] = zBitio.readUB(3);
                }

                var codeTable = _buildHuffTable(codeLengths);
                var litLengths = [];
                var prevCodeLen = 0;
                var maxLengths = numLitLengths + numDistLengths;
                for (; litLengths.length < maxLengths; ) {
                    sym = _decodeSymbol(zBitio, codeTable);
                    switch (sym) {
                        case 16:
                            i = zBitio.readUB(2) + 3;
                            for (; i--; ) {
                                litLengths[litLengths.length] = prevCodeLen;
                            }
                            break;
                        case 17:
                            i = zBitio.readUB(3) + 3;
                            for (; i--; ) {
                                litLengths[litLengths.length] = 0;
                            }
                            break;
                        case 18:
                            i = zBitio.readUB(7) + 11;
                            for (; i--; ) {
                                litLengths[litLengths.length] = 0;
                            }
                            break;
                        default:
                            if(sym <= 15){
                                litLengths[litLengths.length] = sym;
                                prevCodeLen = sym;
                            }
                            break;
                    }
                }
                distTable = _buildHuffTable(
                    litLengths.splice(numLitLengths, numDistLengths)
                );
                litTable = _buildHuffTable(litLengths);
            }

            sym = 0;
            for (; sym != 256; ) {
                sym = _decodeSymbol(zBitio, litTable);


                if (sym < 256) {
                    buff[buff.length] = sym;
                    test++;
                } else if(sym > 256){
                    var mapIdx = sym - 257;
                    var len = LENS[mapIdx] + zBitio.readUB(LEXT[mapIdx]);
                    var distMap = _decodeSymbol(zBitio, distTable);
                    var dist = DISTS[distMap] + zBitio.readUB(DEXT[distMap]);
                    i = buff.length - dist;
                    for (; len--; ) {
                        buff[buff.length] = buff[i++];
                        test++;
                    }
                }
            }
        } else {
            zBitio.bit_offset = 8;
            zBitio.bit_buffer = null;

            var len = zBitio.readNumber(2);
            var nlen = zBitio.readNumber(2);
            for (; len--; ) {
                buff[buff.length] = zBitio.readNumber(1);
                test++;
            }

        }
    }

    return buff;
}





    /**
     * buildHuffTable
     * @param bitLengths
     * @returns {{}}
     */
    function buildHuffTable(bitLengths)
    {
        var numLengths = bitLengths.length;
        var blCount = [];
        var maxBits = _max.apply(Math, bitLengths) + 1;
        var nextCode = [];
        var code = 0;
        var table = {};
        var i = numLengths;
        var len = 0;

        for (; i--; ) {
            len = bitLengths[i];
            blCount[len] = (blCount[len] || 0) + (len > 0);
        }

        for (i = 1; i < maxBits; i++) {
            len = i - 1;
            if (!(len in blCount)) {
                blCount[len] = 0;
            }

            code = (code + blCount[len]) << 1;
            nextCode[i] = code;
        }

        for (i = 0; i < numLengths; i++) {
            len = bitLengths[i];
            if (len) {
                table[nextCode[len]] = {
                    length: len,
                    symbol: i
                };
                nextCode[len]++;
            }
        }
        return table;
    }

    /**
     * decodeSymbol
     * @param bitio
     * @param table
     * @returns {*}
     */
    function decodeSymbol(bitio, table)
    {
        var code = 0;
        var len = 0;
        for (;;) {
            code = (code << 1) | bitio.readUB(1);
            len++;
            if (!(code in table)) {
                continue;
            }

            var entry = table[code];
            if (entry.length == len) {
                return entry.symbol;
            }
        }
    }
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2