swfのバージョン6に対応したい。
手始めに圧縮されたswfをJSで解凍する。
課題
- 画像データは基本zlib圧縮されている
- バージョン6からはswf本体をzlib圧縮して出力ができる
完成品
参考にしたサイト
分解する時の注意点
* 先頭の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;
}
}
}