flash
(速報)SWF SpecificationがOpenになりました
以前、「SWFファイルフォーマットとライセンス 」で、SWFのファイルフォーマット仕様書の利用許諾に、SWFファイルを読み込むプログラムを作成してはいけないと書かれていることを紹介しました。
今日、Adobeから「Adobe and Industry Leaders Establish Open Screen Project」というプレスリリースがあり、その中に以下の一文がありました。
- Removing restrictions on use of the SWF and FLV/F4V specifications
ということで、SWFの仕様書を制限無しに読むことが可能になったようです。
他にも、Flash Playerの移植レイヤが公開され、Flash Playerを移植してもライセンス料が取られなくなる等が発表されているようです。私もまだ流し読みしかしていないのですが、とても大きなニュースなのでとりいそぎ紹介させていただきました。
追記:
SWFとFLVの仕様書がこちらからダウンロードできるようになっています。
Open Screen Project / For developers
Flex SDKのswfutilsをハックしてみる
Flex SDKのswfutilsでswfとXMLの相互変換を試してみる で、swfxc.jarを作成し、SWFXからSWFへの変換を試してみたところ、上手くいきませんでした。 swfxc.jarのMain-ClassであるSwfxParserクラスを中心にFlex SDKのソースを読んで、swfxcが失敗する理由を探ってみました。
eclipseの準備
まず、eclipseの準備をします。準備の手順は %FLEX_SDK%/development/eclipse/readme.txt に書いてあります。importするプロジェクトを選択する画面で、flex-swfutilsを選択してください。他にcompiler等を選ぶことができます。
SwfxParser.javaの概観
SwfxParser#main()は、SwfxParserクラスのインスタンスを生成してparse()メソッドを呼んでいます。SwfxParser#parse()は、SAXParserのインスタンスを生成して、自身をイベントハンドラとしてSAXParser#parse()を呼んでいます。SwfxParserは、DefaultHandlerクラスのstartElement(), endElement(), endDocument()メソッドをオーバーライドして、SAXのイベントを処理しています。
startElement()は以下のようになっており、XMLエレメントの名前と同じ名前で、引数にAttributesクラスを取るメソッドを探して実行します。endElement()も、引数が空のメソッドを探す以外はstartElement()と同じです。例えば、<DefineSprite>タグを見つけるとSwfxParser#DefineSprite(Attributes attributes)が実行され、</DefineSprite>を見つけるとSwfxParser#DefineSprite()が実行されることになります。
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { try { Method method = getClass().getMethod(qName, new Class[]{Attributes.class}); method.invoke(this, new Object[]{attributes}); } catch (NoSuchMethodException e) { warning(new SAXParseException("no start handler for " + qName, locator)); }
これらのイベントハンドラは、基本的に、そのタグに対応するオブジェクトを生成してtagHandlerに渡します。SwfxParser#mainはtagHandlerにTagEncoderというswfバイナリを出力するクラスのオブジェクトを登録しているので、swfファイルが出力される事になります。
他に重要な部分に、SwfxParserのメンバ変数のdictとstackがあります。Define系タグを処理するときにdictにidとオブジェクトを記憶させておいて、後にPlaceObject等を処理するときにid経由で参照先を探すのに利用します。stackは、DefineShapeとlineやcurveのようにタグとデータ構造がツリー状になっているときに使います。親タグの開始時にstack.push()し、子データを処理するときにstack.peek()で参照した親にデータを登録し、親タグの終了時にstack.pop()します。
klab.swfxをswfに変換させてみる
と、SwfxParserが理解できたところで、何でklab.swfxからklab.swfが生成できなかったかを調べてみます。 例外ハンドラを幾つか追加して調べてみたところ、dictからid=3のオブジェクトを探して見つからないために、そこで終了していました。klab.swfxのなかでid=3のオブジェクトを探したところ、DefineTextでした。SwfxParser#DefineText()メソッドが無いために、dictにid=3のDefineTextオブジェクトが登録されておらず、エラーになっています。DefineTextの部分のswfxは次のようになっています。
<DefineText id='3' bounds='(106,414),(2570,1654)' matrix='t0,0'> <textRecord font='Verdana' height='1620' yOffset='1620' color='#00000000'> 1+1152 </textRecord> </DefineText>
DefineText以外にも大量にタグのハンドラが足りてない気がするのは無視して、とりあえずこのswfxのparseに挑戦します。
まず、<textRecord>に対応するSwfxParser#textRecord()を作ります。楽勝・・・と思いきや、font='Verdana'
で引っかかりました。フォントは、DefineTextの前にDefineFont2タグで定義されており、id='2'が割り当てられています。idが一意なのに対してフォント名は一意ではないので、この部分はフォント名ではなくidを参照するべきです。とりあえず、その場しのぎでごまかしたものが次のコードになります。
public void textRecord(Attributes attributes) throws SAXParseException { TextRecord tr = new TextRecord(); String fontname = getAttribute(attributes, "font"); tr.setFont(dict.getFontFace(fontname, true, false)); // set dummy value to bold and italic. tr.setHeight(parseInt(getAttribute(attributes, "height"))); tr.setY(parseInt(getAttribute(attributes, "yOffset"))); tr.setColor(parseColor(getAttribute(attributes, "color"))); DefineText dt = (DefineText)stack.peek(); dt.records.add(tr); } public void textRecord() { }
次はSwfxParser#DefineText()です。これは特に引っかかることなく次のように実装しました。
public void DefineText(Attributes attributes) throws SAXException { DefineText dt = new DefineText(stagDefineText); int id = parseInt(getAttribute(attributes, "id")); dt.bounds = parseRect(getAttribute(attributes, "bounds")); dt.matrix = parseMatrix(getAttribute(attributes, "matrix")); createCharacter(id, dt); stack.push(dt); } public void DefineText() { DefineText dt = (DefineText)stack.pop(); tagHandler.defineText(dt); }
これで動くか?と思ったら、まだダメでした。引っかかったのはparseRect()
です。swfxを生成するときは、Rect#toString()を利用していて、swfxを読み込むときにはSwfxParser#parseRect()を利用しているのですが、Rect#toString()が状況によって2種類のフォーマットを使い分けて出力していて、SwfxParser#parseRect()はその片方にしか対応していないために、parseに失敗していました。
SwfxParser#parseRect()をRect#toString()にあわせて拡張したらまた先に進めるハズです。が、ここでキレました。そもそも、Rect#toString()があまりイケてません。しかも、rect.equals(parseRect(rect.toString())) == true
を満たすようなparseRect()がSwfxParserのメソッドというのもどうかと思います。SwfxParser#parseRect()じゃなくてRect#fromString()であるべきでは無いでしょうか?せめて同じpackageにあるべきですよね?swfxのフォーマットも含めていろいろ修正したいです。
でも、Flex SDK プロジェクトと離れたところで頑張って修正するのももったいないです。ということで、Flex SDKを使ってSWF⇔XML相互変換は一旦あきらめます。これからは、swf弄りは今までどおりswfmillを使いつつ、Flex SDKに対してはプロジェクトのforumで議論してパッチ作成という形で参加して行こうと考えています。
結局こんなオチになってしまってすみません。Flex SDKプロジェクトでswfutils.jarへのAdobe外部からの貢献が多くなれば、AdobeもSWF File Format SpecificationのEULA(swfutils.jarの修正すら不可能)を変更してくれる気になるかもしれないので、swf関係者の皆さんも一緒にFlex SDKをハックしてみましょう。
Flex SDKのswfutilsでswfとXMLの相互変換を試してみる
SWFファイルフォーマットとライセンス(その2) で、swfutils.jarがSWFとSWFXというXMLフォーマットで相互変換できそうだとお伝えしましたが、その後相互変換に挑戦してみた経過をまとめます。
Flex SDKの用意
svn trunkのFlex SDKを用意します。(多分、sdk/branches/3.0.x を利用してもこの記事で行っている範囲では同じだと思います)
今回の例では、Windows XPで、C:\usr\flexsdk にFlex SDKをインストールしますが、皆さんの好みで適当に読み替えてください。SubversionとJDKとantはインストールされて環境変数(%PATH%, %JAVA_HOME%, %ANT_HOME%等)は適当に設定されているものとします。
C:\>cd usr\ C:\usr>svn co http://opensource.adobe.com/svn/opensource/flex/sdk/trunk flexsdk C:\usr>cd flexsdk C:\usr\flexsdk>ant C:\usr\flexsdk>set FLEX_SDK=C:\usr\flexsdk C:\usr\flexsdk>set PATH=%FLEX_SDK%\bin;%PATH%
SWFからSWFXへの変換
この段階で、SWFからSWFXへの変換はできます。ためしに、弊社のTOPページで使われているFlashを解析してみます。http://www.klab.org/images/main.swf をダウンロードして、C:\work ディレクトリ内に保存してください。
C:\usr\flexsdk>cd C:\work C:\work>swfdump -out klab.swfx main.swf
これで、klab.swfxという名前のXMLファイルができました。
ちなみに、ここでは%FLEX_SDK%/bin/swfdump.exeという実行ファイルを利用しましたが、同じディレクトリにシェルスクリプト版のswfdumpも用意されています。シェルスクリプトのソースを読めば、swfdumpで実際に実行されるのが、java -jar %FLEX_SDK%/lib/swfdump.jar
であることが判ります。
swfxc.jarとswfxc.batの作成
今度はSWFXからSWFへの変換をしたいのですが、こちら側のコマンドはありません。ソースコードを読むと、SwfxParser.javaを使えばできそうなので、SWFXからSWFへの変換用のjarを作ってみます。
jarの名前は、SWFX Compilerの略で、swfxc.jarにしました。%FLEX_SDK%/modules/swfutils/build.xmlを修正して、swfxc.jarを作成するようにします。次のpatchを当ててから、もう一度antを実行すれば、%FLEX_SDK%/lib/swfxc.jarができあがります。
Index: build.xml =================================================================== --- build.xml (revision 751) +++ build.xml (working copy) @@ -60,13 +60,24 @@ <attribute name="Class-Path" value="asc.jar swfutils.jar"/> </manifest> </jar> - + <echo message="Building lib/swfxc.jar"/> + <jar file="${lib.dir}/swfxc.jar" basedir="${module.src}" includes="flash/swf/tools/swfx.xsd"> + <manifest> + <attribute name="Sealed" value="${manifest.sealed}"/> + <attribute name="Implementation-Title" value="${manifest.Implementation-Title} - SWFx Compiler"/> + <attribute name="Implementation-Version" value="${manifest.Implementation-Version}.${build.number}"/> + <attribute name="Implementation-Vendor" value="${manifest.Implementation-Vendor}"/> + <attribute name="Main-Class" value="flash.swf.tools.SwfxParser"/> + <attribute name="Class-Path" value="asc.jar swfutils.jar"/> + </manifest> + </jar> </target> <target name="clean" description="clean"> <delete failonerror="false" includeEmptyDirs="true"> <fileset file="${lib.dir}/swfutils.jar"/> <fileset file="${lib.dir}/swfdump.jar"/> + <fileset file="${lib.dir}/swfxc.jar"/> <fileset dir="${module.classes}"> <include name="**/*"/> </fileset>
このjarを直接叩いても良いのですが、面倒なので、batファイルを作成する事にします。次のようなbatファイルを、%FLEX_SDK%/bin/swfxc.batという名前で作成してください。
java -jar %FLEX_SDK%\lib\swfxc.jar -aspath . %1 %2 %3 %4 %5 %6 %7 %8 %9
swfxc.batができたので、ためしに先ほど作成したklab.swfxをswfに戻してみます。swfxcは引数無しで実行してもusageを出したりしてくれないのですが、 SwfxParser.java を読むと、入力ファイル名を引数で指定し、その拡張子を.swfに変更したものを出力することがわかります。ということで、実行してみましょう。
C:\work>swfxc klab.swfx
・・・klab.swfができません。失敗です。
(続く)
SWFファイルフォーマットとライセンス(その2)
少し前の話になりますが、とうとうFlex 3 SDKとAIR SDKが公開されました。既にお使いの方も多いかと思います。 さて、以前「SWFファイルフォーマットとライセンス」という記事を書いたのですが、Flex 3 SDKが公開されてどうなったかの続報をお伝えします。
まず、大きな変更として、オープンソースな部分が増えました。Flex 2 SDKのときはFlex FrameworkのみがMPL(Mozilla Public License)だったのですが、Flex 3 SDKになって、コンパイラ、デバッガ、SWFライブラリ等もMPLになりました。Adobe Open Source に Flex SDK プロジェクト があるので、詳しくはそちらをご覧下さい。「プロジェクトページなんてどうでも良い、早くソースを見せろ」という方は、Flex SDK のsvnリポジトリが <http://opensource.adobe.com/svn/opensource/flex/sdk/> にあるので、svn coして下さい。
で、このリポジトリを探してみたのですが、残念ながらSWF Fileformat Specificationは含まれていませんでした。その代わりswfutils.jarが想像以上にすごい事が判りました。勝手にswfを生成するためのライブラリだと思っていたのですが、swfを読み込むこともできるのです。
flexsdkのbin/ディレクトリの中には、swfdumpという、swfファイルをswfxというXMLフォーマットでダンプするツールが含まれています。この実体は flash.swf.tools.SwfPrinter#main()になります。なんか、swfのリバースエンジニアリング大歓迎っていう感じのツールですね(笑)
flexsdkのbin/ディレクトリに含まれてはいないのですが、swfxからswfへの変換もswfutils.jarに含まれています。flash.swf.tools.SwfParser#main()が実体なので、
$ java -cp $FLEX_HOME/lib/swfutils.jar flash.swf.tools.SwfxParser test.swfxとすると実行することができます。が、実行してみようとしてみたところ、swfx.xsdというリソースの取得に失敗して止まってしまいます。swfx.xsd自体はflexsdk/modules/swfutils/src/java/flash/swf/tools/swfx.xsd にあるので、これからJavaとJarとAntを勉強してSwfxParserを使えるjarの作成に挑戦してみます。
Flex SDKの中には、コンパイラやディスアセンブラ等色々含まれていますので、swf関係者(?)な方はぜひ覗いて見てください
SWFファイルフォーマットとライセンス
今回は、SWFを弄るときに必ず理解しておかないといけない、ライセンスに関する注意点をまとめてみます。
まず、公式なSWFファイルフォーマットの資料として、Adobe Systems Inc. (以降、Adobe) が"SWF and FLV File Format Specification" (以降、公式仕様)を公開されています。 (http://www.adobe.com/licensing/developer/)
この公式仕様の利用許諾が "SWF and FLV File Format Specification License Agreement" になるのですが、この中に次のような文があります。
3. Restrictions続きを読む
a. You may not use the Specification in any way to create or develop a runtime, client, player, executable or other program that reads or renders SWF files.
ActionScript Debugger を公開します
昨日のConverterにつづいて、Debuggerの方も公開します。お試し版のWebアプリも用意しましたので、ぜひ触ってみてください。
続きを読むActionScript Converter '3to2' を公開します
少し遅くなってしまいましたが、去る 11 月 27 日に開催された Tech-mobi2007 でお話させていただいた ActionScript Converter "3to2 (サントゥニー)" の講演資料とソースコードを公開させていただきます。
続きを読む