swflib

日本語、使うには気をつけよう。

なんか、やっていることがマイナーなせいか、googleで検索かけると自分のページが直ぐ出てくる。
うざいねん。もっと、まともな文章を書くように心がけよう。


さて、swflibで画像をswfファイルに埋め込むことが出来るようになりました。
今回は、一つ、アクションスクリプトを埋め込んでみようかと思います。


まずは、a=1っていうのを埋め込んでみましょう。


用意するもの

parafla! フリーのswf作成アプリケーション
listswf libmingについてきた、swfファイルを見るコマンドラインプログラム
Flash Decompliler フラッシュのswfファイルをデコンパイルする。
         アクションスクリプトデコンパイルに便利。
FLAGSTONEのswf用ライブラリ Javaからswfを出力できる。アクションスクリプトも気合で作れる。
              なんとなく、これに慣れてしまったので使ってます。
swflib ocaml用のswf用ライブラリ ActionScript2.0用のコンパイラMTASCの内部で使われている。
swfファイルの仕様書 マクロメディアのホームページから拾ってきます。

まずは、parafla!でアクションスクリプトを実行するswfファイルを生成します。
次に、listswfを使って中身を見ます。以下のようになります。

listswf test.swf > test.txt

File size: 50
Frame size: (0,12800)x(0,9600)
Frame rate: 30.000000 / sec.
Total frames: 1

Offset: 21 (0x000015)
Block type: 12 (DoAction)
Block length: 23

0	   Declare Dictionary: a0
8	   Push "a0"
13	   Push 1
21	   Set Variable
22	
Offset: 46 (0x00002e)
Block type: 1 (ShowFrame)
Block length: 0


Offset: 48 (0x000030)
Block type: 0 (End)
Block length: 0

JavaのFLAGSTONEでACTIONSCRIPT入りのプログラムを生成するには以下のようにやります。

import com.flagstone.transform.*;
import com.flagstone.transform.util.*;
import java.io.*;
import java.util.*;

public class test {
	public static void main(String[] args)
		throws Exception {
		FSMovie movie = new FSMovie();
		movie.setFrameRate(30);
		movie.setFrameSize(new FSBounds(0, 0, 640 * 20, 480 * 20));
		{
			ArrayList actions = new ArrayList();

			// 変数テーブル設定
			FSTable literals = new FSTable(new ArrayList());
			literals.add("a");
			actions.add(literals);

			{
				actions.add(new FSPush(new FSTableIndex(0)));
				actions.add(new FSPush(1));
				actions.add(FSAction.SetVariable());
			}
			movie.add(new FSDoAction(actions));

			movie.add(new FSShowFrame());
		}
		movie.setSignature("FWS");
		movie.encodeToFile("test.swf");

	}
}

DoActionタグの中で、変数名の登録、変数名をプッシュ、値をプッシュ、値設定とやってあげれば良いようです。
さて、ocamlで対応しそうなものを探します。


swflib/actionScript.ml
mtasc/genSwf.ml


が参考になります。
うーととととと、
{ tdata = TDoAction _ }
ってのがあります。これをつかうのでしょう。
swf.mlでは、

type tag_data =
 | TEnd
 ...
 | TDoAction of actions

とあります。

type action =
	| AEnd

	| ANextFrame
	| APrevFrame
	| APlay
	| AStop
	| AToggleHighQuality
	| AStopSounds
	| AAddNum
	| ASubtract
	| AMultiply
	| ADivide
	| ACompareNum
	| AEqualNum
	| ALogicalAnd
	| ALogicalOr
	| ANot
	| AStringEqual
	| AStringLength
	| ASubString
	| APop
	| AToInt
	| AEval
	| ASet
	| ATellTarget
	| AStringAdd
	| AGetProperty
	| ASetProperty
	| ADuplicateMC
	| ARemoveMC
	| ATrace
	| AStartDrag
	| AStopDrag
	| AThrow
	| ACast
	| AImplements
	| ARandom
	| AMBStringLength
	| AOrd
	| AChr
	| AGetTimer
	| AMBStringSub
	| AMBOrd
	| AMBChr
	| ADeleteObj
	| ADelete
	| ALocalAssign
	| ACall
	| AReturn
	| AMod
	| ANew
	| ALocalVar
	| AInitArray
	| AObject
	| ATypeOf
	| ATargetPath
	| AEnum
	| AAdd
	| ACompare
	| AEqual
	| AToNumber
	| AToString
	| ADup
	| ASwap
	| AObjGet
	| AObjSet
	| AIncrement
	| ADecrement
	| AObjCall
	| ANewMethod
	| AInstanceOf
	| AEnum2
	| AAnd
	| AOr
	| AXor
	| AShl
	| AShr
	| AAsr
	| APhysEqual
	| AGreater
	| AStringGreater
	| AExtends

	| AGotoFrame of int
	| AGetURL of string * string
	| ASetReg of int
	| AStringPool of string list
	| AWaitForFrame of int * int
	| ASetTarget of string
	| AGotoLabel of string
	| AWaitForFrame2 of int
	| AFunction2 of function_decl2
	| ATry of try_block
	| AWith of int
	| APush of push_item list
	| AJump of action_count
	| AGetURL2 of int
	| AFunction of function_decl
	| ACondJump of action_count
	| ACallFrame (* no data *)
	| AGotoFrame2 of bool * int option

	| AUnknown of int * unknown

type actions = action DynArray.t

とありますので、DynArray.tにアクションを突っ込んであげればよさそうです。

	| AStringPool of string list
	| APush of push_item list
	| ASet

type push_item =
	| PString of string
	| PFloat of int32
	| PNull
	| PUndefined
	| PReg of int
	| PBool of bool
	| PDouble of float
	| PInt of int32
	| PStack of int
	| PStack2 of int

あたりを使えば良さそうです。
この3つの定義を見てみましょう。
actionScript.mlでは

	| AStringPool _ -> 0x88
	| APush _ -> 0x96
	0x1D => (ASet,"SET");

って言う風にあるのがわかります。
swfの仕様書を見ると

ActionConstantPool	UI8							Action = 0x88
ActionPush			UI8							Action = 0x96
ActionSetVariable	UI8							Action = 0x1D

とありますので、間違いないようです。

TDoAction [(AStringPool["a"]);(APush[(PStack 1);(PInt (Int32.of_int 1))]);ASet]
って感じで作ってあげたらよいことになります。
あとは、DynArray.を使えばよいと。

出来上がったソースは以下のとおりです。

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 ["a"]);;
DynArray.add ass (APush [(PStack 0);(PInt (Int32.of_int 1))]);;
DynArray.add ass (ASet);;

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

これを、Flash Decompilerで見てみると、、、。
a = 1
ってかいてあります。成功です。