MSCとは
MSCとは、スマブラforにおいてキャラクターの動作を制御するプログラムのことである。ゲームシステム解析のほか、トレーニングModパックにも利用されている。各キャラクターごとに個別のMSCが存在し、ファイルは
fighter>[キャラ名]>script>msc
に格納されている。拡張子は「.mscsb」。weaponを持つキャラは、同フォルダの下層に各weaponのMSCファイルがある。
スマブラforからの新しいシステムで、ユーザーの間で解明されはじめたのは2017年秋頃からである。スクリプト数は1キャラにつき2200~2500個、うち2200番くらいまでが全キャラ共通のコードである。スクリプト文法や動作との対応など未解明の部分が多い。現状ではACMDファイル(≒Hitboxスクリプト)よりも敷居が高いと言える。本当ならばこのページで読み方やスクリプトの対応関係などをじっくり解説したい所なのだが、量が膨大すぎるのと不明な点が多すぎるのとで諦めた。
MSC Note Sheet
とにらめっこしながら、丁寧に読み解いていくのが基本となる。
解読のヒント
解読の一番の肝は「どのスクリプトがどの行動に対応しているのか」を特定することである。一つの動作に複数のスクリプトが紐づいていることも多く、スクリプト間の呼び出しも頻繁に行われるので、かなり面倒くさい。既に正体が分かっているスクリプトは
MSCNoteSheet_CmnScript
にリストアップされている。
ある行動のスクリプトを探したい場合は、それに関係のあるパラメータのVariableIDで検索するのが有効である。例えば走行のスクリプトを探したければ、キャラクターの走行加速度が関係しているはずなので、fighter_paramを参考に"0x03000009"などを検索すると良い。検索に使えるIDは以下のシートから探そう。
実機でのスクリプトの動き方、変数が取る値などが知りたければ、MSC Debuggerが役に立つ。
導入
必要な環境
- .mscsbファイル
- Python3 のインストール
- プログラミング言語。pymscを実行するのに必要。
- Pythonの知識はほぼ必要ない(あるに越したことはない)が、コマンドプロンプトは最低限扱える必要がある。
ツール
jam1garner氏がリリースしている。管理人が現在導入しているのは以下の4つ。ツール名をクリックでgithubのページに飛び、緑色の「Clone or download」からダウンロードできる。
mscsbファイルの解凍・コンパイルを行う、最も基本のツール。簡単なMSCコードを実行できるエミュレータプログラムも同梱。本記事で扱うプログラムファイルは以下の3つ。
- disasm.py:mscsbファイルの解凍。
- asm.py:mscsbファイルへのコンパイル。
- emu.py:MSCエミュレーター。
mscsbファイルを、より読みやすい形で解凍するツール。pymscとの違いは、
- C言語のソースコードとして1つのファイルに解凍する
- コンパイルはできない
作業効率が大幅に上がるので、是非導入しておきたい。
実機でMSCスクリプトをデバッグできるツール。実機のハック環境と別のツール:
Diibugger
の準備が必要。
C言語で書かれた簡易なMSCソースコードをmscsbファイルにコンパイルするツール。機能としてはmscdecと対になるが、キャラクターに実際に使われているコードは複雑すぎてコンパイルできない様子。管理人も殆ど活用できていないので、大幅な改良が来るまではこのページでは紹介しない。
役立つサイト
現在判明しているMSCのコマンドや内容をまとめたシート。
MSCスクリプトを閲覧できるサイト。pymsc版とmscdec版、両方の書式を選べる。同サイトの
MSC script searcher
は、全キャラのスクリプトに対し簡易的な構文検索も可能。データ量の都合上どうしても動作が重たいが、便利。
pymsc製作者によるMSCの文法解説。このページでも要約を掲載する。
トレモModパック製作者による、PyMSCツールの実践向け使い方解説。
MSCファイル編集に便利なエディタ設定ファイル。フリーの
Atomエディタ
に導入すると、MSCの文法に反応しテキストに色を付けて表示してくれる。Atomエディタはmscdecで解凍したC言語ファイルにも色付けが出来るので、是非ともインストールするべき。
pymsc
一番基本のツール。編集したmscsbファイルを実機で動かすにはこのツールを使うしかない。
解凍
シェルスクリプトで
python disasm.py [mscsb file]
を実行する。[]内にはmscsbファイルのパスを入力。解凍されたスクリプトファイル群は、"output"フォルダの中に展開される。出力されるのは
- Scripts (拡張子なし)
- globals.txt
- script_[n].txt
コンパイル
"Scripts"ファイル、スクリプトの.txtファイルが存在するフォルダで
python asm.py
を実行する。同フォルダに"test.mscsb"が出力される。出力ファイル名は、Scriptsファイルに
|example.mscsb
のように「|」(バー)から始まる一行を任意の位置に追加することで変更が可能(後述)。
asm.pyはmsc.pyをインポートするので、msc.pyを同フォルダに入れるか
sys.path.append( msc.pyのパス )
などを追加するなどしてパスを通さないと動作しないことに注意。
出力ファイルについて
Scripts
.txtのスクリプト群をリスト化・管理しているファイル。
行頭に「:」(コロン)があるスクリプトが対戦開始前に最初に読み込まれ、キャラクターの基本的な設定が行われる。
asm.pyでコンパイルするファイル名を変えたい場合は、"script"に
|example.mscsb
のように、「|」(バー)から始まる一行を追加すればよい。
任意の位置に追加可能で、複数個追加すれば同じ内容のファイルが(別名で)保存される。
globals.txt
文字のエイリアス設定をするファイル。
.string [string]
これで、行数が文字列のエイリアスになる。
同梱のバッチファイルについて
作業効率化のためのバッチファイルのサンプルが、DLフォルダ内にzip形式で同梱されている。対応する".py"ファイルと同階層に置いて実行すれば、シェルスクリプトを使わずに解凍・コンパイルなどが可能。
※あくまでサンプルなので、disasm.bat など引数が足りないものは自分でbatファイルを編集する必要アリ。
emu.py
emu.pyは、シェルスクリプト上でMSCコードを仮想的に実行するエミュレータープログラムである。実行方法は
python emu.py
と打ったあとにコードを入力するか、
puthon emu.py [mscsb file]
で直接実行するかのいずれか。ゲームで使われるコマンドの中にはemu.pyが対応していないものも多くあるので、後者もあくまで自作のコードを動かすための機能だと考えるべきであろう。
以下では主にemu.pyで動作するMSCの構文を学習する。
MSC構文
+
|
長いので折り畳み |
プログラミング言語としての構造は非常にシンプルである。基本的にスタックとスクリプトで数値をやり取りして
end
でプログラム終了するだけである。
スタック
MSCの数値はスタックの形式で管理される。スタックとは、データの管理方式の一つである。変数をストックする際は上に積み上げていき、逆に使用する際は上から取っていくという形式を取る。上に積む作業を"Push"、上から取り除く作業を"Pop"と呼ぶ。
MSCでは、コマンド末尾に「.」(ピリオド)を打つことで入力をスタックにpushすることを示す。
例:
pushInt. 0x30 #0x30をスタック一番上にpush
プリント
printf [表示するstack数]
stackの文字列を表示するコマンド。
表示するstack数に2以上の数を入れた場合は、上から指定数のstackを取り出し、下に積んであったものから順に表示する。数値を直接printすることはできず、文字列中に「%i」などと打つことでのみ、その位置に置き換わる形で表示できる。配置する順番等については例Bを参考に。
整数は「%i」、浮動小数は「%f」と表記する。
例A:
pushInt. 1
pushInt. 2
addi. #スタックの上2つをpop&加算、結果をpush
pushInt. "1 + 2 = %i" #文字列をpush。%1は、ここにintを挿入せよの意。
printf 2 #上2つを print.
例B:
pushInt. 5
pushInt. 10
pushInt. "入力は順に %i, %i です."
printf 3
>> 入力は順に 5, 10 です.
↑ 「5,10,文字列」の順に読み込み、文字列に含まれる「%i」に整数が入る。
整数と浮動小数
浮動小数を入力するときは、
pushInt. 2.5f
のように、最後に「f」をつけて小数を判別する。
整数を入力するときは何もつけなくてよい。
エイリアス
文字列に数値または文字列を紐づけする。
.alias 3,stockCount #stockCount = 3
PushInt. 4
setVar 0,stockCount #実際は3がセットされる。
実際にはglobals.txtにグローバルエイリアス設定がある。
変数
MSCの変数には
global
,
local
の2タイプがある。
前者は全スクリプト、後者は単一のスクリプト内でのみ有効。
begin [argument count], [variable count]
変数の個数を宣言。実際のMSCファイルでは先頭行に必ず書かれている。
エミュレーター上では書かなくてもよい。
- argument = local valのうち、既に値が代入されているものの数。
- variable = そのスクリプトで扱うlocal valの総数。
エミュレーター上でargument countを1以上に設定すると、実行時にその数の分だけ代入する数値の入力を求められる。
pushVar. [type], [varnum]
setVar [type], [varnum]
- type : 0=local, 1=global
- varnum :変数番号。「0,5」なら、「localの5番目の変数」。
- pushVar.:変数をstackに積む。「.(ピリオド)」がpushすることを示す。
- setVar:stackから変数に代入。
例
begin 0,3 #localは3個。今、0個がストックされた状態からスタート。
pushInt. 3 #まず3をpush。
setVar 0, 0 #localの0番目に3を代入。3はpopped
pushInt. 43 #43をpush。
setVar 1, 10 #globalの10番目に代入。43はpopped
pushVar. 0,0 #local_0をpush
pushVar. 1,10 #global_10をpush
pushInt. #"localVar0 = %i globalVar10 = %i" 文字をpush
printf 3 #3個をprint。上3つ(3,10,文字列)をprint
end
論理
数字(0,1)がそのまま論理に対応し、0はfalse, 1はTrueを表す。
論理記号
equals.
は、stackの上2つの数値をpopし、等しければ1を、異なれば0をpushする。
同様に、''notEquals, lessThan, lessOrEqual, greater, greaterOrEqual'' も存在する。
論理演算子
stackの上2つの論理(0,1)をpopし、論理判定をpushする。
''bitOr, bitAnd, bitXor''が存在。
if文
if
stackをpopし、trueの時のみif以下のコードを読む。
例:
pushInt. 1 #true
if endOfIf #trueなので読む
pushInt. "The if was run" #
printf 1 #The if was runと表示
endOfIf: #読むのはここまで
pushInt. "This was run"
printf 1
else
ifで場合分けをしたい場合に使う。使用方法が少し特殊。
コマンドの効果は、「else B」で「Bまでジャンプする」というもの。ifと同時に使うことを想定されているようだが、通常のプログラムコードと違って、ifとelseの対応関係はないらしい。
例
pushInt. 0 or 1
if A
pushInt. "Was true"
printf 1
else B
A:
pushInt. "Was false"
printf 1
B:
pushInt. "If/Else blocks over"
printf 1
end
Trueのとき
Aが実行される。Aブロックの最終行「else B」で「B:」までジャンプ。
結果的に、A:とB:の間に位置するfalse時のコマンドは実行されない。
Falseのとき
Aブロックはスキップされ、A:の次の行(false時のコマンド)から読み込む。
B: はTrue時に読まない領域を区切っているだけであり、
ifコマンドそのものとは対応していない。
演算
計算を行うときは、演算子に整数なら「i」を、浮動小数なら「f」をつけて計算する。
演算子には以下のようなものがある(全て浮動小数版)。
addf |
加算 |
subf |
減算 |
divf |
除算 |
multf |
乗算 |
negf |
符号反転 |
また、local変数計算を簡潔に行う演算子もいくつかある。
Int |
Float |
Parameters |
Description |
i++ |
f++ |
varType,varNum |
adds 1 to variable |
i-- |
f-- |
varType,varNum |
subtract 1 from variable |
i+= |
float+= |
varType,varNum |
adds X value from stack to variable |
i-= |
float-= |
varType,varNum |
subtracts X value from stack to variable |
i*= |
float*= |
varType,varNum |
multiplies variable by X value from stack |
i/= |
float/= |
varType,varNum |
divides variable by X value from stack |
例
i+= 0,1 #local_1に、stack一番上にある数値を加算する
syscall
syacallは、数値の処理を行うコマンド(の群)。
内部システムにおける様々な数値処理の実態であり、それぞれのコマンドに番号が振られている。
- 処理とコマンド番号の例
- ボタン入力を取得する: 0xe
- Hitbox関連の何か:0x2e
- 乱数を発生させる:0x9
- etc...
構文は
sys [stackから取得する値の個数] [コマンド番号]
sys. [stackから取得する値の個数] [コマンド番号] ←stackにpushする場合
コマンドによって必要な引数の個数が違うので、適切な数が「取得する値の個数」で指定される。
例1:コマンド 0x9
処理:2つの引数a,bを取り、a以上b未満の整数をランダムに生成する
pushInt. 0
pushInt. 10
sys. 2, 0x9 #「0,10」の2つが引数。「.」があるので、作った乱数はpushされる。
pushInt. "Random number: %i"
printf 2
>> Random number: 7
例2:コマンド 0x16
処理:ID(例:0x1E00000A)に割り当てられた数値にアクセスする。
引数は「処理タイプ, (数値,) 変数ID」
処理タイプが「0x6」の場合、IDに格納されている数値を呼び出す。
処理タイプが「0x7」の場合、IDに数値を書き込む。
第二引数の「数値」は、後者の場合のみ必要になる。(前者では入力不要)
pushInt. 7
pushInt. 5000
pushInt. 0x2000000
sys 0x3, 0x16 #値"5000"をID"0x2000000"に書き込む。
pushInt. 6
pushInt. 0x2000000
sys. 0x2, 0x16 #ID"0x2000000"の数値をstackにpushする。
注:PyMSC Emuで動作するsyscallは、ここで紹介したコマンド0x9, 0x16のみである。
他のスクリプトの呼び出し
callFunc [int]
スタック最上段に置いたスクリプトを呼び出す。
intにはスクリプトの引数の個数を指定し、その個数分を追加でスタックから取得する。
例1.
pushInt. script_0
callFunc 0
script_0を呼び出す。引数なし。
例2
try. label
pushInt. 1
pushInt. 2
pushInt. 3
pushInt. script_200
callFunc 3
label:
script_200を、引数"1,2,3"とともに渡す。
try. コマンドはこの場合、"label"に囲われたスクリプトからの出力をstackに積むという処理である。
例3
呼び出されるスクリプトの例。
begin 1,1 #1個のlocal変数があり、うち1個は既に値が代入されている
pushVar. 0,0
pushInt. 2
multi. #localの0番目と2を乗算
return_6 # stackの一番上をpopしながら出力する。
end
新規に作成したファイルを呼び出すときも、そのファイル名を指定すれば問題なく呼び出せる。また"script_[n]"の形式で呼び出したファイルの名前が変更されていた場合でも、代わりに"インデックスnのスクリプト"を参照するために特に不都合は起こらない(らしい)。
|
mscdec
MSCをC言語形式で解凍するツール。C言語に通じていなくとも直感的に読めるはず。少なくともpymsc版よりは遥かに読みやすい。Atomエディタなどのハイライト機能を使えばなおよし。1つのファイルにまとまるため、変数IDなどの検索も簡単である。
解凍
.mscsbファイルを、mscdec.batにドラッグ&ドロップ。あるいは、 "mscdec.py filename"。同フォルダに「.c」ファイルが生成される。
読み方
msc"dec"の名の通り、スクリプト内の実数は10進法で表示される。IDは16進数のまま。
mscdec版の
sys_16(0x3, 0xX, 0xYYYYYYYY)
は、pymsc版のsyscallコマンドの
pushInt. 0x3
pushInt. 0xX
pushInt. 0xYYYYYYYY
sys 0x3, 0x16
に対応する。
↓めっちゃ見やすい。
MSC Debugger
MSCスクリプトを実機でデバッグするツール。
非常にクラッシュしやすい
ので心して使うこと。ツールがクラッシュしたらWiiUも再起動しなければならない。
準備
SD:wiiu/apps/diibugger/diibugger.elf
のように配置する。
次に、
WiiUのインターネット接続方法をPCのものと揃える。
管理人のPCは無線LAN接続なので、WiiUも無線接続に切り替えている。WiiU有線-PC無線だと通信が上手く行かない。有線-有線は未確認。
接続設定を終えたら、
WiiUのIPアドレスを確認
する。WiiUのインターネットブラウザーを起動し、画面右上から「設定>デベロッパーツールの有効化」。表示される数字:"AAA.BBB.CCC.DDD:XXXX"のうち、AAA.BBB.CCC.DDDがIPアドレス。
起動
- HomeBrewLauncherを開いて、diibugger.elfをロード。
- 真・解析の手引きの通りにHaxchiを導入している場合、「Haxchi起動>Aボタン押しっぱなし>diibugger.elfを選択」と進めばよい。
- Miiスタジオが起動するのですぐ終了。
- スマブラを起動する。
- PC上で、MSC Debuggerフォルダ内の「run.bat」をダブルクリック。
- スマブラ起動後ならばどのタイミングで起動しても良い。
- 起動したソフトの画面下、Wii U IP: にWiiUのIPアドレスを入力してconnectを選択。上手く行っていれば、すぐに一番下に"Connected"の表示が出る。
- 失敗する場合はWiiUのIPアドレス、ネット接続設定などを見直す。
使用方法
まずデバッグしたいMSCスクリプトを決める。以下ではフォックスのscript_796, 致命エフェクトを生成するスクリプトを動かしてみる。
MSC Debuggerフォルダに対象キャラの.mscsbファイルをコピーする。コマンドプロンプトで、
python PrintMscScript.py [character name].mscsb [script number]
を実行する。今回の場合は
python PrintMscScript.py fox.mscsb 796
を実行。
↓実行した図。
実行すると、pymscで解凍したものと同じコードがコマンドプロンプト上に表示される。行の先頭の8ケタの16進数が、内部処理で使われている行番号になっている。ここからブレークポイントを設定したい行の番号を選び、ツール内の Breakpointタブ>add に打ち込む。
今回は上から3行目の「sys 0x0, 0x21」にブレークポイントを設定する。行番号:0002790C をツールに打ち込んで登録。ポイントは複数登録および削除も可能。
↓ブレークポイント登録。"0x"はつけなくてよい。
設定が完了したら実際にゲームをプレイし、
そのスクリプトが読み込まれるような状況
を再現する。成功すれば、ブレークポイントを置いた行を読み込んだところでゲームが"一時停止"する。
一時停止を活用し、MSCスクリプトの動作を調べていくのがこのツールの本領である。
↓一時停止した様子。キャプチャーボードでWiiUの画面を右に表示している。
ツールのコマンド
MSC Dissassemblyタブ
スクリプトの進行・停止に関するコマンドを実行できる。
- Continue:一時停止解除。
- Step:1行進む。
- Break:一時停止していないときに使う。次に読み込まれた任意のMSCスクリプトの最初の行で一時停止。MSCスクリプトのエントリーポイントを調べるのに有用。
↓stepを押すと1行ずつ進む。
MSC dataタブ
Local Variablesでは、ローカル変数、MSCのスタック変数が表示される。Global Variablesではグローバル変数が表示される。変数をダブルクリックすると、値をPC上で編集することも出来る(ローカル変数は編集できない?)。
その他
- Memoryタブ:WiiUのメモリの、特定のアドレスの数値を表示する。活用法不明。
- Threadタブ:不明。多分重要ではない。
コメント
最終更新:2018年05月17日 22:10