Flash SWFファイル仕様 – ActionScript の仕様詳細(SWF3,SWF4)

Flashlite 1.x で採用している ActionScript 1.0 (SWF3,SWF4) の SWF 仕様についてメモ。
シンプルなつくりですが、スタックなど後継の ActionScript2, AS3 の実装の根幹になる部分なので最新のバージョンの AS を理解する上でも参考になると思います。
 
これさえ読めばバイナリエディタや swfmill で ActionScript を1から記述できる、かもしれません。
 

SWF 仕様書ではチャンクの事をさして「タグ」と書いてあるのですが、XMLタグとの混同を避けるため、ここではチャンクと記述することにしました。
 
なお、swfmill で利用する際にタグ名とチャンク名で名前が変わっている場合があるため、swfmill で名前が変わるチャンクについては、End (swfmill:<EndAction>)のように、カッコ内にswfmillでのタグ名を表記しています。
 
 
■ アクション(ActionScript)はすべて DoAction チャンクの Actions フィールド(swfmill:<Actions>)に定義する。
SWF3, SWF4 では他の場所にアクションは書けません。
逆に DoAction の中にはアクションしか書けません。
分離されていて分かりやすいですね。
 
■ Actions フィールド(swfmill:<Actions>)は1バイトのヌル文字で終わる。
Actions フィールドはアクションの連続で構成され、0 個以上の任意の数のアクションを記述できます。
End フラグ(swfmill: <EndAction>)をあらわす1 バイトのヌル文字で終わります。
例:

DoAction チャンク(tag-type:12)
  Actions フィールド(0個以上のアクションを指定)
  {Action1}
  {Action2}
  {Action3}
  …
  End フラグフィールド(ヌル文字)

swfmill 例:

<DoAction>
 <Actions>
   <!– Action1 –>
   <!– Action2 –>
   <!– Action3 –>
   <!– : –>
   <EndAction />
 </Actions>
</DoAction>

 
■ アクションが実行されるのはフレームが表示される時。
どこで DoAction を定義したとしても、カレントムービークリップの現在のフレームが表示されるタイミングで実行されます。
つまり、DoAction で定義した時に処理されるわけではなく、その後最初に ShowFrame タグが現れた時にたまっていたアクションが一気に実行されます。
これはアクションスクリプト内でムービークリップ等を参照する場合、ShowFrame までに配置されていればよく、DoAction タグが現れた時点では何も存在してなくても問題ない、という事を意味します。
 
■ アクションは全てスタック方式で記述する。
SWF4 以降、アクション中の関数、式、およびそれらで使う式の左辺、式の右辺、関数の引数、メソッドの実行インスタンスなど全ての要素は LIFO(Last In First Out) のスタック方式で記述します。
例: 「 a=123; b = a + 15; 」 という2個の式を SWF4 のアクションチャンクで記述する。

DoAction
  ActionPush(Type:0) a // “a” をスタックに積む…stack1
  ActionPush(Type:0) 123 // “123” をスタックに積む…stack2
  ActionSetVariable // stack1,stack2 を取り出して stack1 = stack2 として評価( a = “123”;)
  ActionPush(Type:0) b // “b” をスタックに積む…stack1
  ActionPush(Type:0) a // “a” をスタックに積む…stack2
  ActionGetVariable // stack2 を取り出して変数 a の値を参照してスタックに積みなおす(a -> “123”) …stack2
  ActionPush(Type:0) 15 // “15” をスタックに積む…stack3
  ActionAdd // 直近の2スタック(stack2,stack3)を合算し、結果をスタックに積む(123 + 15 -> 138) … stack2
  ActionSetVariable // stack1, stack2 を取り出して stack1 = stack2 を評価(b = 138)
  End // アクション記述終了

swfmill の例:

<DoAction>
  <actions>
    <PushData>
      <items>
        <StackString value=”a”/>
      </items>
    </PushData>
    <PushData>
      <items>
        <StackString value=”123″/>
      </items>
    </PushData>
    <SetVariable/>
    <PushData>
      <items>
        <StackString value=”a”/>
      </items>
    </PushData>
    <PushData>
      <items>
        <StackString value=”b”/>
      </items>
    </PushData>
    <GetVariable/>
    <PushData>
      <items>
        <StackString value=”10″/>
      </items>
    </PushData>
    <AddCast/>
    <SetVariable/>
    <EndAction/>
  </actions>
</DoAction>

 
■SWF チャンク書式
SWF のチャンクはヘッダ部と本体から構成されます。
ヘッダ部は全てのチャンクで共通で、
・種類(Tag type)
・ヘッダを除くチャンクの長さ
の2要素からなります。
 
ヘッダを除いたチャンクの長さが63バイト以内の場合、以下の短縮書式で記述し、ヘッダのサイズは2バイトになります。
・種類(Tag type): 10ビット
・ヘッダを除くチャンクの長さ: 6ビット
 
ヘッダを除いたチャンクの長さが 64 バイトを越える場合、以下の書式で記述し、ヘッダのサイズは6バイトになります。
・種類(Tag type): 2バイト(UI16)
・ヘッダを除くチャンクの長さ: 4バイト(UI32)
 
■アクション書式
DoAction の Actions フィールド内に記述するアクションもチャンクの書式にならっていて、ヘッダ部とその他の部分に分かれています。
ヘッダ部の構成要素もチャンク同様
・種類(ActionCode/アクションコード): 1バイト(UI8)
・ヘッダを除くアクションの長さ: 2バイト(UI16)
の2要素からなります。ヘッダ部のサイズは 3バイトです。
 
ただしアクションコードが 0x80 未満のアクションはヘッダしか持たないため、長さを記述しません。
このときはヘッダのサイズは1バイトになります。
 


アクション一覧
 
■SWF3
SWF3 ではフレームの移動などムービークリップの基本操作が実装されました。
※SWF3 はスタック方式ではないため、引数が必要なアクションについてはフィールド部に記述します。
 
○ ActionNextFrame
次のフレームを再生後停止する。
ActionScript: nextFrame()
ActionCode: 0x04
 
○ ActionPreviousFrame
前のフレームを再生後停止する。
ActionScript: previousFrame()
ActionCode: 0x05
 
○ ActionPlay
現在の(次の)フレームから再生する。
ActionScript: play()
ActionCode: 0x06
 
○ ActionStop
現在のフレームの処理完了後停止する。
ActionScript: stop()
ActionCode: 0x07
  
○ ActionToggleQuality
表示品質を高⇔低に相互に切り替える。
ActionCode: 0x08
 
○ ActionStopSounds
再生している音をすべて停止する。
ActionScript: stopSounds()
ActionCode: 0x09
 
○ ActionGotoFrame
指定したフレームに移動する。
gotoAndPlay(), gotoAndStop() に数値を指定した場合に呼ばれる。
gotoAndPlay() と同じ動作を望む場合は ActionGotoFrame の後に ActionPlay を実行すればよい。
ActionScript: gotoAndPlay()
ActionCode: 0x81
Length: 2 固定
フィールド: frame=移動先フレーム番号(UI16)
 
○ ActionGotoLabel
指定したフレームに移動する。
gotoAndPlay() に文字列を指定した場合に呼ばれる。
ActionScript: gotoAndPlay()
ActionCode: 0x8C
フィールド: label=移動先フレーム名(文字列)
 
○ ActionWaitForFrame
ActionCode: 0x8A
Length: 3 固定
フィールド: フレーム番号(UI16), スキップするアクションの数(UI8)
 
○ ActionGetURL
指定したターゲットの内容を URL 先のデータに置き換える(HTML, SWF など)。
ActionScript: getURL()
ActionCode: 0x83
フィールド: URL(文字列), ターゲット(文字列)
 
○ ActionSetTarget
以降のすべてのアクションを指定したインスタンスで実行するよう変更する(例 SetTarget “../foo”)。
SetTarget “” のように空文字を指定すると現在のムービークリップに戻します。
ActionScript: tellTarget(){ … }
ActionCode: 0x8B
フィールド: ターゲット名(文字列)
 


■SWF4で追加されたタグ
SWF4 からスタック構造になり、条件分岐や演算子など基本的なプログラミング言語の機能が実装されました。
ただ型についてはまだまだ未実装な部分が多く、boolean は数値、数値は文字列として処理し、スタックに入れられます。文字列連結演算子が + ではなく add なのはこのためです。
 
□算術演算子
○ ActionAdd (swfmill:<AddCast>)
ActionScript: + 演算子
※SWF5 以降は ActionAdd2 (swfmill:<AddTyped>) に置き換え
 
○ ActionDivide (swfmill:<Divide>)
ActionScript: / 演算子
 
○ ActionMultiply (swfmill:<Multiply>)
ActionScript: * 演算子
 
○ ActionSubtract (swfmill:<Subtract>)
ActionScript: – 演算子
 
□関係演算子(数値比較)
○ ActionEquals
ActionScript: == 演算子
 
○ ActionLess
ActionScript: < 演算子
 
□論理演算子
○ ActionAnd
ActionScript: && 演算子
 
○ ActionNot
ActionScript: ! 演算子
 
○ ActionOr
ActionScript: || 演算子
 
□文字列操作
○ ActionStringAdd
※SWF5 以降は ActionAdd2 に置き換え
ActionScript: add 演算子
 
○ ActionStringEquals

○ ActionStringExtract
○ ActionStringLength
○ ActionMBStringExtract
○ ActionMBStringLength
○ ActionStringLess
 
□スタック操作
○ ActionPop
○ ActionPush
 
□型変換
○ ActionAsciiToChar
○ ActionCharToAscii
○ ActionToInteger
○ ActionMBAsciiToChar
○ ActionMBCharToAscii
 
□制御構造
○ ActionCall
 
○ ActionIf (swfmill: <BranchIfTrue>)
if 文の実装。条件に一致していたら指定バイト数アクション処理を飛ばす。
ActionScript: if( … ){ }
ActionCode: 0x9D
フィールド: BranchOffset=移動バイト数(-32768 から 32767 までの間の数値。ActionIf の次のアクションを0とする)
例: if( n < 5 ){ 処理1; }else{ 処理2 }; 処理3; をアセンブラで記述。

DoAction
  ActionPush “n”
  ActionGetVariable // 変数 n を展開
  ActionPush “5”
  ActionLess // n < 5 を判定し、結果をスタックに入れる
  ActionNot // スタックにいれた真偽値を「反転」
  ActionIf 123 // スタックに入れた値が真なら処理を ActionJump の次(123バイト先)まで飛ばす
  // (処理1 のアセンブラ) if(){ … } 内
  ActionJump 23 // 処理1の最後に到達する。ここにきたら処理2(23バイト分)を飛ばす。
  // (処理2 のアセンブラ) // else{ … } 内
  // (処理3 のアセンブラ) // 条件文の外の共通処理。
  End フラグ

例: swfmill での記述例

<DoAction>
 <actions>
  <PushData><items><StackString value=”n” /></items></PushData>
  <GetVariable/>
  <PushData><items><StackString value=”5″ /></items></PushData>
  <LessThanCast/>
  <LogicalNOT/>
  <BranchIfTrue byteOffset=”123″ />
  <!– 処理1 –>
  <BranchAlways byteOffset=”23″ />
  <!– 処理2 –>
  <!– 処理3 –>
  <EndAction/>
 </actions>
</DoAction>

 
○ ActionJump (swfmill:<BranchAlways>)
if 文のelse 部の実装。このアクションに到達した場合、無条件で指定バイト数アクション処理を飛ばす。
ActionScript: if .. else{ }
ActionCode: 0x99
フィールド: BranchOffset=移動バイト数(-32768 から 32767 までの間の数値。ActionJump の次のアクションを0とする)
 
 
□変数
○ ActionGetVariable
○ ActionSetVariable