JavaScript
HTML5
flash
zlib
swf2js

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

More than 3 years have passed since last update.


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;
}
}
}