はじめに
この記事は、SkyrimというゲームソフトのModファイルのバリナリ解析とそれを利用してModを生成するC#のコードについて記載していくシリーズ物です。
ファイルフォーマットについての詳しい情報は以下のサイトがあります。(以降UESP)
Tes5Mod:Mod File Format - The Unofficial Elder Scrolls Pages (UESP)
これまでの内容で大体の必要なクラスの説明が出来たと思いますので、今回は実際にそれらを使ってModの読み取りを行ってみます。
コレまでの記事はコードは一部抜粋で、説明もわかりづらいものでしたので、はずかしいものの、一通りのソースコードをGitHubにアップしました。
これまで紹介していた内容とは一部異なっていますのでご注意ください。
なお、ほとんどコメントをいれていませんので意図がわかりづらい点があるかもしれませんが、まだ試行錯誤している部分やテストコード的な部分も含まれています。また、例外対処もほとんどいれていませんのでご了承ください。
ワールド情報から特定のオブジェクトの情報を抜き出してみる
この例では、ワールド情報内になるマップマーカー(0x10
)を抜き出し、そのFormIDと英語名称と日本語名称を出力してみます。
前提として、RTesLib
を参照しているものとします。
public void OutputAllMapMarker()
{
TesTableStrings tblEN = new TesTableStrings(@"C:\Program Files (x86)\Steam\steamapps\common\Skyrim Special Edition\Data\strings\skyrim_english.strings");
TesTableStrings tblJP = new TesTableStrings(@"C:\Program Files (x86)\Steam\steamapps\common\Skyrim Special Edition\Data\strings\skyrim_japanese.strings");
string path = @"C:\Program Files (x86)\Steam\steamapps\common\Skyrim Special Edition\Data\Skyrim.esm";
TesFile esp = new TesFile(path, new List<string>(new string[] { "WRLD" }));
TesWorldspace wrld = (TesWorldspace)esp["WRLD"];
foreach (var x in wrld.Records)
{
if (x.Main.Cell != null)
{
foreach (var x2 in x.Main.Cell.CellMain.Subs)
{
foreach (var x3 in x2.Records)
{
if (x3.Contains("NAME"))
{
uint name = x3["NAME"][0].ToUInt32();
if (name == 0x10)
{
uint id = x3["FULL"][0].ToUInt32();
System.Diagnostics.Debug.Print(string.Format("{0}\t{1}\t{2}", x3.Header.FormID.Value.ToString("x8"), tblEN[id], tblJP[id]));
}
}
}
}
}
}
}
-
Skyrim.esm
より、WRLD
のみ読み取る -
WRLD
の全てのセルデータをチェックする - セルデータのフィールドに
NAME
があり、それがマップマーカー(0x10
)なら出力対象 -
FULL
にあるストリングテーブルのIDを取得する - セルデータのレコードヘッダーの
FormID
と英語文字列と日本語文字列をタブ区切で出力ウィンドウに出力する
といった感じです。
武器データを改造したModを作ってみる
次の例は、バニラの全ての武器データのダメージを+100するチートModです。
public void CreateDamagePlus100Weapons()
{
TesTableStrings tblEN = new TesTableStrings(@"C:\Program Files (x86)\Steam\steamapps\common\Skyrim Special Edition\Data\strings\skyrim_english.strings");
TesTableStrings tblJP = new TesTableStrings(@"C:\Program Files (x86)\Steam\steamapps\common\Skyrim Special Edition\Data\strings\skyrim_japanese.strings");
string path1 = @"C:\Program Files (x86)\Steam\steamapps\common\Skyrim Special Edition\Data\Skyrim.esm";
string path2 = @"C:\Program Files (x86)\Steam\steamapps\common\Skyrim Special Edition\Data\DamagePlus100Weapons.esp";
TesFile esp1 = new TesFile(path1, new List<string>(new string[] { "WEAP" }));
TesFile esp2 = new TesFile(path2);
TesGroup WEAP = esp1.Groups["WEAP"];
esp2.OutputItems.Add(WEAP);
foreach (var x in WEAP.Records)
{
if (x.Contains("FULL"))
{
TesBytes full = x["FULL"][0];
uint id = full.ToUInt32();
full.Clear();
full.AddRange(new TesBytes(tblJP[id], true));
}
TesBytes b = x["DATA"][0];
ushort dmg = (new TesBytes(b.GetRange(8, 2).ToArray())).ToUInt16();
dmg += 100;
b.RemoveRange(8, 2);
b.AddRange((new TesUInt16(dmg)).ToBytes());
}
esp2.Save(path2);
}
-
Skyrim.esm
からWEAP
のみ読み取る - ファイルヘッダーのみのModを読み取り、
WEAP
グループを追加する - 武器データを順次読込む
- フィールドに
FULL
がある場合、日本語文字列にした内容に書きかえる(idのままなので、FULL
のTesBytes
の中身をクリアしてストリングテーブルから取得した文字列をNULL終端としてバイト配列を追加する) -
DATA
フィールドより、ダメージ情報のある部分を読み取る - ダメージの値に
100
を加算する -
DATA
フィールドのダメージの値を書きかえる(ダメージの値のある部分を削除して、バイト配列を追加(バイト配列内の最後なので追加でよい)) - 同名でファイル出力する
試しにSSEEditで確認すると確かに100追加されています。
といった感じで、読み取りやデータの変更などが行えるようになりました。
以上
前回 | 次回 |
---|---|
7 - ストリングテーブルを扱う |