swflib 3 Hello World

さて、何にもでない物作ってても面白くないので今日は一つ、
Hello Worldと画面に出力するActionScript入りのswfファイルを出力してみようと思います。

ActionScriptで書くと、

createTextField("tf", 1, 0, 0, 640, 480);
tf.text = "hello world";

をやりたいということです。

まずは、parafla!でさらっと作ってlistswfしてみます。

Offset: 26 (0x00001a)
Block type: 12 (DoAction)
Block length: 95

0	   Declare Dictionary: tf, createTextField, text, Hello World
41	   Push 480
   Push 640
   Push 0
   Push 0
   Push 0
   Push "tf"
   Push 6
   Push "createTextField"
78	   Call Function
79	   Pop
80	   Push "tf"
85	   Get Variable
86	   Push "text"
   Push "Hello World"
93	   Set Member
94	

Push,Pop以外に4つの命令が実行されています。
Declare Dictionary
Call Function
Get Variable
Set Member
です。

0	   Declare Dictionary: tf, createTextField, text, Hello World

これは、使う文字列をプールに保存ってことですね。

次が、 createTextField("tf", 0, 0, 0, 640, 480);
に対応するところです。

ActionCallFunctionの定義をswfの仕様書で見てみると以下のようになっています。

1.ファンクション名をPop
2.引数の数をPop
3.引数をPop
4.関数を実行
5.関数の戻り値をPush

これを見ると、

41 Push 480 <- 引数6
Push 640 <- 引数5
Push 0 <- 引数4
Push 0 <- 引数3
Push 0 <- 引数2
Push "tf" <- 引数1
Push 6 <- 引数の数
Push "createTextField" <-関数名
78 Call Function <- 関数呼び出し
79 Pop <- 戻り値を捨てる

ってことがわかります。

tf.text = "Hello World";はGetVariableとSetMemberを使ってます。

ActionGetVariable
1.名前をPop
2.値をスタックにPush
ActionSetMember
1新しい値をPop
2オブジェクトの名前をPop
3オブジェクトをPop

80 Push "tf" <- 名前をPush
85 Get Variable <- tfをPopして、値(オブジェクト)をPush
86 Push "text" <- 名前をPush
Push "Hello World" <- 新しい値をPush
93 Set Member <- 新しい値"Hello World"をPop, オブジェクトの名前"text"をPop,オブジェクト"tf"をPopして、tf.textに"Hello World"を入れる。

ってことになってます。

mlに落とすと以下のようになります。

open Swf
open SwfParser
open SwfZip

(* タグ生成 *)
let tag ?(ext=false) d = {
	tid = 0;
	textended = ext;
	tdata = d;
	}

(* 領域データ生成 *)
let bounds x y w h = {
		rect_nbits = if (max w h) >= 820 then 16 else 15;
		left = x;
		top = y;
		right = w * 20;
		bottom = h * 20;
	}

let w = 640
let h = 480
let fps = 40.0
let out = "out.swf"
let bgcolor = ref 0xffffff


let header = {
		h_version = 8;
		h_size = bounds 0 0 w h;
		h_frame_count = 1;
		h_fps = to_float16 fps;
		h_compressed = false;
	}
;;

let ass : Swf.action DynArray.t = DynArray.create();;
DynArray.add ass (AStringPool ["tf"; "createTextField"; "text"; "Hello World"]);;
DynArray.add ass (APush [
	(PInt (Int32.of_int 480));
	(PInt (Int32.of_int 640));
	(PInt (Int32.of_int 0));
	(PInt (Int32.of_int 0));
	(PInt (Int32.of_int 0));
	(PStack 0); (* tf *)
	(PInt (Int32.of_int 6));
	(PStack 1); (* createTextField *)
	]);;
DynArray.add ass (ACall);;
DynArray.add ass (APop);;
DynArray.add ass (APush [
	(PStack 0); (* tf *)
	]);;
DynArray.add ass (AEval);;
DynArray.add ass (APush [
	(PStack 2); (* text *)
	(PStack 3); (* text *)
	]);;
DynArray.add ass (AObjSet);;

let data = [
	tag (TSetBgColor { cr = 0xFF; cg =  0xFF; cb =  0xFF });
	tag (TDoAction ass);
	tag (TShowFrame)];;

SwfParser.init SwfZip.inflate SwfZip.deflate;;
Swf.warnings := true;;
let ch = IO.output_channel (open_out_bin out);;
Swf.write ch (header, data);;
IO.close_out ch;;

実行して出来たout.swfを実際に見てみると、、、ちゃんとHello Worldと出ました。
ocamlでアクションスクリプトでテキストを出力するswfを生成する。
わけわからん話しですけど、こんな感じでできます。