JavaScript でSWFを分解してみる
今年は JavaScript に真面目に取り組もうと、大晦日からプログラミングしてました。
JavaScript で SWF を分解するのは実質3度目ですが、今までを反省しつつ、かつ今までのやり方を全てご破算にして、一から考えて作ってます。
とりあえずブロックの分解は出来たので、以下にプログラムを晒します。(Chrome と Safari で動きます。他は自信無し)
JavaScript のコンソールには以下のように表示されます。
(このスクリーンショットは、Chrome の右メニュー>「要素の検証」>「Console」タブを辿って表示させました)
あ。僕は JavaScript 初心者なので参考にし過ぎないで下さい。むしろ、こんなコードふざけんなって所を見つけたらお叱り頂けると幸いです。
index.html
- http://diary.awm.jp/~yoya/data/2012/01/01/swfed/index.html
- swf_url と canvas_id を渡して SWFEditor を起動します。
<canvas id="mycanvas" width="240" height="240"> canvas is here </canvas> <script type="text/javascript""> // var url = 'saitama.swf'; var swf_url = 'ffxi2.swf'; var canvas_id = 'mycanvas' var swfpl = new SWFEditor(swf_url, canvas_id); </script>
editor.js
- http://diary.awm.jp/~yoya/data/2012/01/01/swfed/editor.js
- parser を loader に渡して起動します。(通信中は SWFLoader に主導権を渡す感じで)
- 最後に main に戻ってくるので、ここで何かやりたい処理を埋める予定。今は分解したデータを dump してます。
var SWFEditor = function(url, canvas_id) { var parser = new SWFParser(this); var loader = new SWFLoader(url, parser); this.main = function(swfheader, swftags) { this.swfheeader = swfheader; this.swftags = swftags; console.debug("SWFEditor::run"); console.debug(swfheader); for (var i = 0, n = swftags.length ; i < n ; i++) { swftag = swftags[i]; console.debug('code:'+swftag.code+' length:'+swftag.length+' data:'); if (swftag.data) { console.debug(swftag.data); } }
loader.js
- http://diary.awm.jp/~yoya/data/2012/01/01/swfed/loader.js
- XMLHttpRequest から onreadystatechange を通知される度に、それまでに取得出来たデータを parser に渡します。
req.onreadystatechange = function(hoge) { if (req.readyState > 1) { if (req.status == 200) { if (req.readyState < 4) { this.parser.input(req.responseText); // read partial } else { this.parser.input(req.responseText); // read completed this.parser.finish(); } } else {
parser.js
- http://diary.awm.jp/~yoya/data/2012/01/01/swfed/parser.js
- 渡されたデータを SWF のデータフォーマットに従って分解します。(途中までしか無い可能性があるので、二度目以降は続きを処理)。処理が終わったら editor の main に処理を渡します。
this.input = function(data) { bitstream.input(data); if (bitstream.byte_offset < 8) { this.parseHeader(bitstream); } this.parseTags(bitstream); <略> this.finish = function() { this.editor.main(this.swfheader, this.swftags); } this.parseHeader = function(bs) { // console.debug('parseHeader'); this.swfheader = new SWFHeader(bs); }
object.js
- http://diary.awm.jp/~yoya/data/2012/01/01/swfed/object.js
- SWF データフォーマットの情報要素の処理をまとめました。今のところ SetBackgroundColor と DefineBitsJPEG2 のみです。
/* Header */ var SWFHeader = function(bs) { if (bs) { this.Signature = bs.getData(3), this.Version = bs.getUI8(), this.FileLength = bs.getUI32LE(), this.FrameSize = new SWFRECT(bs), this.FrameRate = bs.getUI16LE(), this.FrameCount = bs.getUI16LE() } }
bitstream.js
- http://diary.awm.jp/~yoya/data/2012/01/01/swfed/bitstream.js
- バイナリを byte や bit で分解するルーチンです。IO_Bit を真似してます。
var Bitstream = function() { this.data = null; this.byte_offset = 0; this.bit_offset = 0; this.input = function(data) { this.data = data; } this.byteAlign = function(n) { if (this.bit_offset) { this.byte_offset += ((this.bit_offset+7)/8) | 0; this.bit_offset = 0; } } this.getData = function(n) { this.byteAlign(); bo = this.byte_offset; ret = this.data.substr(bo, n); this.byte_offset = bo + n; return ret; } this.getUI8 = function() { this.byteAlign(); return this.data.charCodeAt(this.byte_offset++) & 0xff; }