RのSkyrim Mod開発日記

SkyrimのMod開発を中心に、備忘録などを載せていきます。

Skyrim Mod ファイルフォーマット解説 - 1

はじめに

SkyrimのModファイルフォーマットについて解説?していきたいと思います。
Modのファイルフォーマットを知れば、プログラムを組んでデータを自動生成したり、Mod上の欲しい情報を取り出したりなどが行え、Mod製作にかかる時間を短縮できます。
また、プログラムを組まないにしても、バイナリエディタなどで直接情報を確認することも出来ます。

なお、Mod製作をはじめて4ヶ月ほどで、主にプログラムでModを生成することを進めていたので、逆に基本的なことを知らない部分もありますので、ご了承ください。
また、PS4で動作することを基本条件としていますので、PC版でなら簡単に出来ることを難しくやっている場合もあるかと思います。

ファイルフォーマットの情報は次のサイトで詳しく解説されていますので、自分なりに解析してきたことなどを書いてきたいと思います。
Tes5Mod:Mod File Format - The Unofficial Elder Scrolls Pages (UESP)
(※以降この際とのことはUESPと略します)

項目名の名称などは主にSSEEditでの名称を使っていきます。

使うツールなど

  • Skyrim Special Edition Creation Kit Version 1.3.9.0
  • SSEEdit 3.2
  • Microsoft Visual Studio Community 2017 ( C# )
  • BinaryEditor Stirling Version 1.31
  • Binary Editor BZ Version 1.9.8.5

最小限の状態

Skyrim Special Edition Creation Kit(以降 CK)を起動し、Skyrim.esmにのみチェックを入れて読込み、ファイル保存したespファイルをバイナリエディタで表示すると次のようになっています。
f:id:rrryutaro:20170403220210p:plain

ファイルの先頭4文字が"TES4"となっています。(その後の"I"はたまたま文字コードで"I"に一致しているだけです)
全てのModファイルはこの情報からはじまります。(※SkyrimはTES=The Elder Scrolls シリーズの5作目なのでTES5なのですが、ファイルフォーマットはTES4(Oblivion)の頃から変わらないということでしょうか。この辺はよくわかりません)

このModファイルは一切のデータがないため、"TES4"からファイルの終わりまで、Modファイルの基本情報のみが記載されています。この"TES4"からファイルの終わりまでの情報をファイッルヘッダーと呼ぶことにします。

では、これをSSEEditで読込んでみます。
f:id:rrryutaro:20170403221222p:plain

SSEEditのウィンドウの右側に情報が表示されています。
まずは、Record Headerと書かれていて、その下にぶら下がっている項目を見てきます。

表示名 内容 バリナリ上での表示
Signature TES4 f:id:rrryutaro:20170403221809p:plain
DataSize 73 f:id:rrryutaro:20170403221848p:plain
Record Flags f:id:rrryutaro:20170403222015p:plain
FormID 0 f:id:rrryutaro:20170403222124p:plain
Version Control Info 1 0 f:id:rrryutaro:20170403222300p:plain
Form Version 44 f:id:rrryutaro:20170403222344p:plain
Version Control Info 2 0 f:id:rrryutaro:20170403222621p:plain

このレコードヘッダーはModのファイルフォーマットを理解する上で重要な要素です。
一部例外がありますが、Modのファイルフォーマットの構造は次のようになっています。

Modファイル
 + ファイルヘッダー (レコード形式)
 + グループ
   - グループヘッダー
   + レコード
     - レコードヘッダー
     + フィールド
       - フィールドヘッダー
       - フィールドデータ 

今解説している部分がファイルヘッダーで、これはレコード形式となっています。
レコードとは武器であれば1つ分の武器情報を扱う情報の固まりです。
グループとはレコードを包括した情報の固まりです。

レコードはレコードヘッダーから始まり、その後に続く情報がフィールドとなります。
フィールドは武器であれば、ダメージの値など、レコードで扱われる各種の情報の固まりです。

SSEEditで続きの情報を見て行きます。
Record Header の後に、 HEDR - Header とあります。これがフィールドです。

SSEEdit上では一部わかりませんが、フィールドはまず、次のフィールドヘッダーがあります。

名称 データ型(サイズ) 説明
Signature char[4] フィールドがどのようなデータであるかを示す4文字の名称。フィールド名が同じでもレコードが異なれば扱う情報は異なる可能性があるので注意
Data Size ushor フィールドデータのサイズフィールドヘッダーのサイズは含まないので注意

※ushorのサイズは2byte、(別名uint16など)

実際のデータを見てみます。

表示名 内容 バリナリ上での表示
Signature HEDR f:id:rrryutaro:20170403223820p:plain
DataSize 12 f:id:rrryutaro:20170403223843p:plain

"TES4"レコードの"HEDR"フィールドの型情報など

名称 データ型(サイズ) 説明
Version float UESPでの説明では「ほとんどのファイルで0.94; Update.esmの最近のバージョンでは1.7です。」とのこと
Number of Records uint 全レコード数。(解析しきれていませんが、単純にカウントしても数が合いません・・・)
Next Object ID uint 次のオブジェクトID(FormID)、CK上で何かを追加した際に使われるかと思われます。

実際のデータです。

表示名 内容 バリナリ上での表示
Version float f:id:rrryutaro:20170403223952p:plain
Number of Records uint f:id:rrryutaro:20170403224104p:plain
Next Object ID uint f:id:rrryutaro:20170403224144p:plain

UESPでは、Number of Records(UESPでの表記はnumRecords)はint32、Next Object ID(UESPでの表記はnextObjectId)はulongとなっていますが、ここではC#を中心として扱うので、C#での表現とします。また、C#でのlong, ulongは8byte、C++は4byteのようなので、混乱なきよう。なお、レコードカウントについてはマイナスにはならないはずなので、一応uintとしています)

では、SSEEditで以降の項目を見て行きますが、次の"OFST"と"DELE"は灰色になっています。
これは、"TES4"レコードで扱われるフィールドで、かつファイル上での順番的にはこの順番になっているであろうフィールドですが、ファイル上ではデータが無いため、表示されていません。必須項目ではないということですね。どの項目が必須かなどはUESPのサイトで確認するのがいいかと思われます。

次のデータです。

表示名 内容 バリナリ上での表示
Signature CNAM f:id:rrryutaro:20170403225742p:plain
DataSize 8 f:id:rrryutaro:20170403225819p:plain
CNAM - Autohr DEFAULT f:id:rrryutaro:20170403225853p:plain

※フィールドで扱う文字列は終端がNULLになるものと、そうでないものがあるため注意が必要です。基本的にはDataSizeを参照すれば大丈夫ですが、プログラムで扱う際には、終端がNULLかそうでないかのチェックが必要になります。

Master Files - MAST

表示名 内容 バリナリ上での表示
Signature MAST f:id:rrryutaro:20170403230120p:plain
DataSize 11 f:id:rrryutaro:20170403230140p:plain
MAST - Filename DEFAULT f:id:rrryutaro:20170403230206p:plain

Master Files - DATA

表示名 内容 バリナリ上での表示
Signature DATA f:id:rrryutaro:20170403231026p:plain
DataSize 8 f:id:rrryutaro:20170403231051p:plain
DATA - Unknown 0 f:id:rrryutaro:20170403231113p:plain

※マスターファイルはそのModでの前提ファイルです。フィールドはHEDRのように、フィールドデータ内で複数の情報を扱っているケースと、このMaster Filesのように、複数のフィールドが連続してある固まりとしていることもあります。例えば、マスターファイルが複数ある場合には次のようになっています。

f:id:rrryutaro:20170403231321p:plain
f:id:rrryutaro:20170403231355p:plain
※このような形式は多数あるため、プログラムでデータを取り込んで書きかえたい場合、並び順に注意する必要があります。例えば、コンテナなどは、コンテナ何に格納されているアイテムを示すのに、コンテナ内のアイテム数を示すフィールドがあり、それに続いて、コンテナのアイテムを示すフィールドがアイテム数分続きます。

INTV - Unknwon

表示名 内容 バリナリ上での表示
Signature INTV f:id:rrryutaro:20170403231707p:plain
DataSize 4 f:id:rrryutaro:20170403231725p:plain
DATA - Unknown 1 f:id:rrryutaro:20170403231746p:plain

と、このように管理されています。
通常はこの後に"GRUP"の4文字があり、その後4byteにグループのデータサイズがあり、データサイズ分読み飛ばすと、また次の"GRUP"の4文字があります。
それについては次回解説していきたいと思います。以上。