マリフラ改造メモ - Data

このページの内容は、以下を前提に書かれています。

  • SMF2のSWFファイルを編集(改造)する方法のメモです。
  • 無改造のSWFを前提に説明しています。
  • 管理人が独自に解析して作成したメモなので、説明が分かりにくくてもご了承ください。
  • 使用するソフトの詳しい使い方は説明していません。

使用するソフト

JPEXS Free Flash Decompiler (FFDec)
SWFを編集するソフト。

FFDecの設定

  • 「Settings」の「Automatic deobfuscation」(難読化されたスクリプトを自動解読する設定)を有効にする。
  • 「Advanced Settings」の「Resolve constants in AS1/2 p-code」(Pコード内の定数を視覚化する設定)を無効にする。この設定は、FFDec右ペイン(Pコード)上部の一番右のボタン(Resolve constants)からでも変更できます。

Flashゲーム講座&アクションスクリプトサンプル集
ActionScriptを編集する時に便利なサイト。
SWF and AMF Technology Center | Adobe Developer Connection
SWFの仕様が公開されているAdobe公式のページ。
SWF · uupaa/Archive Wiki · GitHub
SWFの仕様が詳しく解説されているページ。
SWFバイナリ編集のススメ第七回 (Shape基本構造) | GREE Engineers' Blog
DefineShapeの構造が詳しく解説されているページ。
Matrixって何だお? (1) | にゃあプロジェクト
画像を反転させる仕組みが直感的に分かるページ。

Flash Player プロジェクターを使う

Flash Player プロジェクターは、SWFを開く事ができるソフトです。FFDecで使えば、SWFを保存する事なくSWFをテストする事ができます。

  1. このページからFlash Player プロジェクターをダウンロードする。ファイル名が「flashplayer_X_sa.exe」(Xはバージョン)のやつです。
  2. FFDecを開き、「Advanced Settings」の「Paths」タブ内の「1) Flash Player projector path」に、ダウンロードしたFlash Player プロジェクターへのパスを入れる。

これで、FFDecのメニューの「File」の「Start」から「Run」すれば、開いている編集中のSWFをFlash Player プロジェクターで開けるようになります。

Flash Player プロジェクターコンテンツデバッガーを使う

Flash Player プロジェクターコンテンツデバッガーは、SWFをデバッグするためのソフトです。FFDecで使えば、ActionScriptのtrace()関数を使ったログの出力などができるようになります。

  1. このページからFlash Player プロジェクターコンテンツデバッガーをダウンロードする。ファイル名が「flashplayer_X_sa_debug.exe」(Xはバージョン)のやつです。
  2. FFDecを開き、「Advanced Settings」の「Paths」タブ内の「2) Flash Player projector content debugger path」に、ダウンロードしたFlash Player プロジェクターコンテンツデバッガーへのパスを入れる。

これで、FFDecのメニューの「File」の「Start」から「Debug」すれば、開いている編集中のSWFをFlash Player プロジェクターコンテンツデバッガーで開けるようになります。

swfmillは、SWFをXMLに変換するソフトです。SWFをXMLに変換し、変換したXMLを編集し、編集したXMLをSWFに戻す事で、FFDecでは不可能な改造が可能になります。

SWFをswfmillで使用可能にする

そのままのSWFだとスクリプトが難読化されていてswfmillでXMLに変換する事ができないので、FFDecを使用して難読化を復号する必要があります。

  1. FFDecでSWFを開き、「Tools」の「Rename invalid identifiers」をクリック。
  2. 出てきたダイアログをそのまま2回「OK」する。
  3. しばらく待ってダイアログが出てきたら「OK」して、SWFを保存する。

swfmillの使用方法

swfmillはコマンドラインツールです。コマンドプロンプトで以下のように入力します。

SWFをXMLに変換(swf2xml):swfmill swf2xml filename.swf filename.xml

XMLをSWFに変換(xml2swf):swfmill xml2swf filename.xml filename.swf

  • FFDecでDoActionを開くと「ActionScript」と「P-code」の2種類のコードが表示されますが、SWFに入っているコードはP-codeだけです。ActionScriptはFFDecがP-codeを変換して表示している物で、実際にはSWFには入っていません。P-codeを編集した場合は編集した内容がそのままSWFに入りますが、ActionScriptを編集した場合はFFDecがP-codeに変換した物がSWFに入ります。
  • P-codeでは、文字例は定数として1行目の「ConstantPool」にまとめて格納されます。文字例をP-code内で使う場合は「constantX」(XはConstantPoolのインデックス)のようにして呼び出します。ただし、ConstantPoolに存在しない文字例をコード内に直接書くと、書いた行にそのまま文字例が追加され、ConstantPoolには追加されません。その場合、後からその文字例をConstantPoolに追加すると、行に直接書かれていた文字例はConstantPoolに格納されます。
  • ActionScriptとP-codeではコードの書き順が異なる場合がありますが、これはFFDecがP-codeの順番通りにActionScriptを生成しないからだと思ってください。

ActionScriptをP-codeに変換する

FFDecではActionScriptを編集する事ができますが、沢山コードが書かれているActionScriptを直接編集するとコードがバグる事があります。それを避けるためにはP-codeを編集すれば良いのですが、自力でP-codeを書くのは難しいので、FFDecを使ってActionScriptをP-codeに変換する必要があります。

と言っても、FFDecは自動でActionScriptをP-codeに変換するので、単に必要な分だけ編集すればバグる可能性が低いというだけです。

  1. 「SWF/DefineSprite 17 (Fade_in1)/フレーム17/DoAction」を開く。
  2. ActionScriptの編集エリアに必要な分だけコードを書いて保存する。P-codeも自動で変換して保存されます。
  3. P-codeを「Resolve constants」が有効な状態でコピーする。1行目(ConstantPool)はコピーする必要はありません。
  4. コピーしたP-codeを、使いたい場所に書く。
  5. 作業が終わったら「SWF/DefineSprite 17 (Fade_in1)/フレーム17/DoAction」の内容を作業前の「stop();」とだけ書かれた状態に戻す。
  • 上記方法でも完全にバグを防ぐ事はできません。もし上記方法でもコードがバグる場合、バグらない書き方に変えるか自力でP-codeを編集する必要があります。
  • ローカル変数が使われている関数内のコードを書く場合、関数内のコードだけを書いた場合はローカル変数は文字例としてP-code内に書かれますが、関数そのものを含めて書いた場合はローカル変数は文字例ではなく「Register」という形式でP-code内に書かれます。
  • 「_root」や「this」もローカル変数と同様ですが、「this」が関数内に存在しない場合は「_root」が「register1」になるのに対し、「this」が関数内に存在する場合は「this」が「register1」となり「_root」が「register2」となるので、関数内のコードを部分的に編集するような場合は注意が必要です。
  • 条件分岐を使っている場合、コピーしたP-codeをそのまま他のP-code内で使うとバグる事があります。これはコピーしたP-code内の条件分岐のID「locXXXX」(Xは16進数)が他のP-code内の条件分岐のIDと被るためで、それを避けるためにはテキストエディターでP-code内のIDを被らないように置換する必要があります。大抵の場合はコピーしたP-code内の「loc0XXX」を「locfXXX」にするといった具合に置換するだけで解決します。

  • 使い方などの詳細はダウンロードファイル内に書いています。
  • 使用する際は事前に許可を取る必要はありません。クレジットは付けても付けなくてもOKです。
  • ソースコードは更新する事があります。更新した場合はバージョンを上げます。
カスタム音楽機能 Ver.7
ダウンロード (smf2vc-sc-custom-music.as)
ドラッグ配置機能 Ver.3
ダウンロード (smf2vc-sc-drag-place.as)
エディターの背景のランダム化 Ver.2
ダウンロード (smf2vc-sc-editor-random-bg.as)

SWF/フレーム2

DoAction (ActionScript)

52行目 (変数「_root.Coin」の値)
コイン数。
53行目 (変数「_root.Live」の値)
残機数。
54行目 (変数「_root.Reserve」の値)
ストックアイテム。「0」で無し、「1」でスーパーキノコ、「2」でファイアフラワー、「3」以降でマント羽根になる。
55行目 (変数「_root.Points」の値)
スコア。

SWF/DefineSprite 772 (Front)

タイトル画面のスプライト。

フレーム1/DoAction (ActionScript)

26行目~32行目 (Button1.func)
「GAME INFO」内のリンクや「CHECK UPDATED VERSION!」をクリックした際に表示されるテキストウィンドウ内の「COPY」ボタンの動作。
31行目(System.setClipboard)が、クリックした際にクリップボードにコピーされる文字例です。
33行目~54行目 (this.link.onPress)
「CHECK UPDATED VERSION!」をクリックした際の動作。
35行目(getURL)が、クリックした際にブラウザーで開かれるURLです。
49行目(_loc2_.Input.push)が、クリックした際に現れるテキストウィンドウに表示される文字例です。

フレーム2

「OPTION」の内容。

フレーム3

「OPTION」の「SHOW CONTROLS」の内容。

フレーム4/DoAction (ActionScript)

「GAME INFO」の内容。

12行目~33行目 (this.link.onPress)
「GAME INFO」内のリンクをクリックした際の動作。
14行目(getURL)が、クリックした際にブラウザーで開かれるURLです。
28行目(_loc2_.Input.push)が、クリックした際に現れるテキストウィンドウに表示される文字例です。

フレーム6

ベータ版で使用されていたフレーム。タイトル画面の「LEVEL EDITOR」をクリックした際に表示される内容。

フレーム5

ベータ版で使用されていたフレーム。SWFが置かれている場所がPouetpu-games.comではない場合のタイトル画面の内容。

SWF/フレーム2

DoAction (ActionScript)

96行目 (Site_ID = Site_ID.substr(7,21);)
URLの文字の切り取り。「7」は「http://」の文字数。「21」は「CHECK UPDATED VERSION!」を表示しないサイトのURLの文字数。
97行目の「www.pouetpu-games.com」
「CHECK UPDATED VERSION!」を表示しないサイトのURL。

SWF/DefineSprite 1863 (Editor_sprites)

  • エディター上で使用される、スプライトの画像が各フレームに存在するDefineSprite。
  • このDefineSpriteのフレームが、エディター上でスプライトの画像として使用されています。

SWF/DefineSprite 1875

  • エディットメニューの「TILES」と「SPRITES」のページに使用されるDefineSprite。
  • フレーム1~フレーム8が「TILES」のページで、フレーム9~フレーム14が「SPRITES」のページです。
  • フレーム1とフレーム9のActionScriptの1行目~12行目が、「NEXT」ボタンや「PREV」ボタンでページを切り替える為のスクリプトです。その中にある3つの同じ数値は合計ページ数で、2つの同じ数値はこのスクリプトが書かれているフレームの番号です。
  • CharacterIdに「DefineSprite 1863 (Editor_sprites)」を指定しているPlaceObject2が存在するフレームのActionScript内の「gotoAndStop」には、「DefineSprite 1863 (Editor_sprites)」のフレーム番号を指定します。

SWF/DefineSprite 1902 (Editor)

フレーム1/DoAction (ActionScript)

12行目 (関数「Load_level」内の変数「Srites_Mode」の値)
スプライトのAI数が「Tyle300」から順番通りに指定されています。
13行目 (関数「Load_level」内の変数「Srites_ID」の値)
エディター上のスプライトの画像が「Tyle300」から順番通りに指定されています。
指定する数値は「DefineSprite 1863 (Editor_sprites)」のフレーム番号です。
スーパーキノコなどをエディターで表示するとバグる現象は、ここにフレーム番号が指定されていないために起こります。
168行目 (関数「Select_Sprite」内の変数「Definition」の値)
スプライト名が「Tyle300」から順番通りに指定されています。

フレーム4

  • エディットメニュー上の「SPRITES」ボタンを押した際に切り替わるフレーム。
  • ActionScript内の「gotoAndStop」には「DefineSprite 1875」の最初の「SPRITES」ページのフレーム番号を指定します。

SWF/DefineSprite 1494 (Text_window)

フレーム1/DoAction (ActionScript)

49行目「flash.display.BitmapData」の第4引数(0)
テキストウィンドウの背景色。
10行目「_loc6_.backgroundColor」の値
テキストウィンドウ内のテキストエリアの背景色。
17行目「_loc6_.bold」の値
テキストウィンドウ内のテキストエリアの文字の太さ。この値を「false」にするかこの行自体を削除すると、文字の太さを細くできます。
18行目「_loc6_.size」の値
テキストウィンドウ内のテキストエリアの文字サイズ。
19行目「_loc6_.font」の値
テキストウィンドウ内のテキストエリアのフォント。
_loc6_.color
16行目~21行目の適当な場所に「_loc6_.color」を追加すると、テキストウィンドウ内のテキストエリアの文字色を設定できます。

背景色や文字色は「0xRRGGBB」のように16進トリプレットで指定可能です。例えば「0xFF0000」と指定すると、保存後は色番号「16711680」に自動で変換されます。

  1. swfmillでSWFをXMLに変換し、XMLをテキストエディターで開く。
  2. XMLの1732073行目~1732082行目(Music 14のデータ)をコピーし、1732214行目と1732215行目の間に追加する。
  3. 追加したデータの「objectID」(2個)の値を「2850」から「2852」に、「name」(1個)の値を「music14」から「music19」に変更する。
  4. 編集したXMLをswfmillでSWFに戻す。
  5. SWFをFFDecで開き、「DefineSound (2852: music19)」を任意の音声ファイルに変更する。
  6. FFDecの左ペインから「scripts/DefineSprite (1902: Editor)/frame 7/DoAction」を開く。
  7. P-codeの393行目の「17」を「19」に書き換える。これでエディターでMusic 19が選択できるようになります。
  8. FFDecの左ペインから「scripts/frame 2/DoAction」を開く。
  9. ActionScriptの66行目の配列(Music 0~18のループ位置の数値)にMusic 19のループ位置の数値を追加する。

TILE 1と同様のタイルをTILE 407として追加する例です。

  1. swfmillでSWFをXMLに変換し、XMLをテキストエディターで開く。
  2. XMLの81453行目~81503行目(TILE 1のデータ)をコピーし、1732214行目と1732215行目の間に追加する。
  3. 追加したデータの「objectID」の数値を「100」は「2852」に、「101」は「2853」に、「102」は「2854」にすべて変更する。
  4. 同じように、追加したデータの「name」の「Tyle1」を「Tyle407」に変更する。
  5. 編集したXMLをswfmillでSWFに戻す。
  6. SWFをFFDecで開き、「DefineBitsLossless2 (2852)」を任意の画像に変更する。
  7. FFDecの左ペインから「scripts/frame 1/DoAction」を開く。
  8. P-codeの1052行目の先頭(「Push」と「406」の間)に「407」と追加し、1068行目の末尾の「407」を「408」に変更する。
  9. P-codeの1048行目の先頭(「Push」と「36」の間)に「1」と追加し、末尾の「407」を「408」に変更する。
  10. P-codeを保存する。
  11. FFDecの左ペインから「scripts/DefineSprite (2725: tyle372)/frame 1/DoAction」を開く。
  12. P-codeの1174行目の先頭(「Push constant70」と「36」の間)に「1」と追加し、末尾の「407」を「408」に変更する。
  13. 同様に、P-codeの1177行目の先頭(「Push constant72」と「406」の間)に「407」と追加し、1193行目の末尾の「407」を「408」に変更する。
  14. P-codeを保存する。
  15. FFDecの左ペインから「scripts/DefineSprite (2734: tyle374)/frame 1/DoAction」を開く。
  16. P-codeの554行目の先頭(「Push constant67」と「36」の間)に「1」と追加し、末尾の「407」を「408」に変更する。
  17. P-codeを保存する。

補足説明

  • 2でコピーするタイルのデータによっては、画像が反転したタイルやアニメーションするタイルなどを作成可能です。
  • 9で追加する数値が、タイルの当たり判定になります。
  • 12で追加する数値が、Pスイッチを押した際のタイルの当たり判定になります。
  • 16で追加する数値が、ビックリスイッチを押した際のタイルの当たり判定になります。
  • 当たり判定の数値は「1」でTILE 1、「35」でTILE 35と同様の当たり判定になります。
  • 15~17は省略しても問題ないかも。

エディットメニューにTILE 240を追加する例です。

  1. swfmillでSWFをXMLに変換し、XMLをテキストエディターで開く。
  2. XMLの1104266行目をコピーし、1104266行目と1104267行目の間に追加する。
  3. 追加したデータの「depth」の値を「82」に変更する。
  4. XMLの1104220行目~1104224行目(エディットメニュー上のTILE 385のデータ)をコピーし、1104224行目と1104225行目の間に追加する。
  5. 追加したデータの「depth」の値を「82」に、「objectID」の値を「1179」(TILE 240のスプライト)に、「name」の値を「a31」に、「transX」(エディットメニュー上の横の位置)の値を「3600」に、「transY」(エディットメニュー上の縦の位置)の値を「400」に変更する。
  6. XMLを保存し、swfmillでSWFに戻す。
  7. SWFをFFDecで開く。
  8. FFDecの左ペインから「shapes/DefineShape2 (1844)」を開く。
  9. ActionScriptの14行目(Tile_index)の配列に「240」(タイル番号)を追加する。
  10. ActionScriptの131行目~134行目をコピーし、135行目に追加する。
  11. 追加したコードの「a30」を「a31」に、「Tile_index[30]」を「Tile_index[31]」に変更する。

補足説明

  • 1~6はエディットメニューにタイルの画像を追加する作業で、7~11は追加したタイルを選択できるようにするための作業です。
  • 3・5で変更する値は、FFDecでも「sprites/DefineSprite (1875)」から変更可能です。
  • 5で変更するobjectIDがエディットメニュー上に表示される画像になります。タイルのobjectIDはFFDecの左ペインの「sprites」から確認できます。
  • 5で「transX」と「transY」に指定する値は「20」で1px分になります。
  • 9で追加する値(タイル番号)が、画像をクリックした時に選択されるタイルになります。

エディットメニューの「TILES」ページの8枚目をコピーして9枚目にする例です。コピーして作成するため、8枚目と9枚目は全く同じページになります。

  1. swfmillでSWFをXMLに変換し、XMLをテキストエディターで開く。
  2. XMLの1120683行目~1123442行目(「TILES」ページの8枚目のデータ)をコピーし、1123442行目と1123443行目の間に追加する。
  3. 追加したデータの「<RemoveObject2 depth="67"/>」と「<UnknownTag id="0xFD">」の間に、下記「コード」を追加する。
  4. 追加したデータの「PlaceObject2」の「morph」の値をすべて「7」から「8」に、「name」の値の中の「h」をすべて「i」に変更する。
  5. XMLの1101944行目の「frames」の値を「15」に変更する。
  6. 編集したXMLをswfmillでSWFに戻す。
  7. SWFをFFDecで開く。
  8. FFDecの左ペインから「sprites/DefineSprite (1875)」を開く。
  9. 「frame 10」内のすべての「PlaceObject2」の「ratio : UI16」の値を「8」から「9」に、「frame 11」内のすべての「PlaceObject2」の「ratio : UI16」の値を「9」から「10」に、「frame 12」内のすべての「PlaceObject2」の「ratio : UI16」の値を「10」から「11」に、「frame 13」内のすべての「PlaceObject2」の「ratio : UI16」の値を「11」から「12」に、「frame 14」内のすべての「PlaceObject2」の「ratio : UI16」の値を「12」から「13」に、「frame 15」内のすべての「PlaceObject2」の「ratio : UI16」の値を「13」から「14」に変更する。
  10. FFDecの左ペインから「scripts/DefineSprite (1875)/frame 1/DoAction」を開く。
  11. P-codeの16行目、18行目、39行目の「8」を「9」に変更する。
  12. FFDecの左ペインから「scripts/DefineSprite (1875)/frame 9/DoAction」を開く。
  13. 定数の「h1」~「h39」を「i1」~「i39」に変更する。
  14. FFDecの左ペインから「scripts/DefineSprite (1875)/frame 10/DoAction」を開く。
  15. P-codeの23行目、44行目の「9」を「10」に変更する。
  16. FFDecの左ペインから「scripts/DefineSprite (1902: Editor)/frame 4/DoAction」を開く。
  17. P-codeの1行目の「9」を「10」に変更する。

コード

<RemoveObject2 depth="69"/>
<RemoveObject2 depth="71"/>
<RemoveObject2 depth="73"/>
<RemoveObject2 depth="75"/>
<RemoveObject2 depth="77"/>

補足説明

  • 3で追加するコードの「RemoveObject2」の「depth」には、前のページ(この例の場合は8枚目)内の「PlaceObject2」の「depth」を指定します。
  • 9で変更する値は、4のようにXMLからでも変更できます。
  • 11で変更する値は、エディットメニューの「TILES」のページ数になります。
  • 15・17で変更する値は、エディットメニューの「TILES」のページ数+1になります。

SWF内には削減可能なデータがいくつか含まれており、それを削除する事でファイルサイズを削減できます。

DefineShape2 1955
DefineShape2 2180
DefineSprite 2793
DefineSprite 2851
どこにも使われていない未使用のオブジェクト。そのまま削除できます。
Unknown (ID=253)
Unknown (ID=255)
サードパーティ製ソフト(恐らくSWF Encrypt)がSWFに追加したと思われる不要なデータ。そのまま削除できます。
XMLでは、「Unknown (ID=253)」は「UnknownTag (0xFD)」、「Unknown (ID=255)」は「UnknownTag (0xFF)」と表示されます。
DefineBitsLossless2 96
「DefineBitsLossless2 48」と同じ画像。そのままでは削除できません。
この画像を使用しているオブジェクトで画像を「DefineBitsLossless2 48」に変更する事で削除できます。
DefineBitsLossless2 1498
「DefineBitsLossless2 744」と同じ画像。そのままでは削除できません。
この画像を使用しているオブジェクトで画像を「DefineBitsLossless2 744」に変更する事で削除できます。
各DefineShapeに存在する「bitmapId」に「65535」と指定された「fillStyle[0]」
「65535」という存在しないIDの画像が指定されているため必要ありませんが、そのままでは削除できません。
このデータを削除するDefineShape内の「fillStyle」(「fillStyle0」または「fillStyle1」)と「numFillBits」の数値を「1」に変更する事で削除できます。
削除するのが面倒な割にあまりファイルサイズを削減できないので、削除しない方がいいかもしれません。