プログラミング・動画編集 備忘録

プログラミングや動画編集についての備忘録です

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文字があります。
それについては次回解説していきたいと思います。以上。

Visual Studio 2017 Community の導入

SkyrimのMod製作を補助するプログラムを作っていますが、作り直すことにしたので、ついでにVisual Studioの最新版を入れてそちらで行うことにします。

www.visualstudio.com
www.visualstudio.com

インストーラーを実行すると次の画面が表示されます。
f:id:rrryutaro:20170401205809p:plain

各タブの内容はこんな感じになっていました。
f:id:rrryutaro:20170401220144p:plainf:id:rrryutaro:20170401220153p:plainf:id:rrryutaro:20170401220203p:plain

[ワークロード]で好みの開発形態にチェックを入れれば、[個別のコンポーネント]には必要な項目が自動でチェックされます。
さらに必要ならチェックを付ければよいでしょう。

今回必要なのは.NETデスクトップ開発ですが、ついでなので、ユニバーサルWindowsプラットフォーム開発にもチェックを付けてインストールしたところ、およそ1時間ほどかかりましたが、特につまづくことも無くインストールが行えました。


以上

SkyrimのModのファイルフォーマット

どこかに絶対あるとは思っていましたが、これまで検索方法が悪くて見つけられませんでした・・・

Modのファイルフォーマットが解説されています。
Tes5Mod:Mod File Format - The Unofficial Elder Scrolls Pages (UESP)

最初からコレみていれば、開発ももっと楽だったろうけど、まぁ自己流で少しずつ解析するのも楽しいので。
ちなみに、次のように検索しました。
https://www.google.co.jp/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=skyrim+GRUP&*

以上

Skyrim Creaion Ki でesp読込み時にクラッシュする

Skyrim CKを久々に使おうとしたところ、次のエラーがでて、クラッシュしました。


f:id:rrryutaro:20170330225802p:plain

---------------------------
Assertion
---------------------------
Assert

File: ..\TES Shared\misc\BGSLocalizedString.cpp

Line: 2871

LOCALIZATION: Error opening or reading strings file.

f:id:rrryutaro:20170330225811p:plain

---------------------------
Warning
---------------------------
MASTERFILE: LOCALIZATION: Zero entries or empty block size read from strings file STRINGS/HearthFires_ENGLISH.STRINGS. Strings will be missing.

"Yes to all" will disable all Warnings for this context.

f:id:rrryutaro:20170330225951p:plain
f:id:rrryutaro:20170330225849p:plain


調べてみたところ次の記事を見つけました。
http://alfortmod.blog.fc2.com/

昔の記事ですが、こういった情報は本当に助かります。
Skyrimのフォルダ(C:\Program Files (x86)\Steam\steamapps\common\Skyrim)の、

SkyrimEditor.ini を開き、見てみると次のようになっていました。

SResourceArchiveList2=Skyrim - Shaders.bsa, Update.bsa

これを次のように修正。

SResourceArchiveList2=Skyrim - Shaders.bsa, Update.bsa, Dawnguard.bsa, HearthFires.bsa, Dragonborn.bsa

無事開けるようになりました。
でもいつの間に変わったんだろう?

Skyrim Mod 製作日記 - 17 - すべてのマップマーカーを編集する

前回から続いているModの作成途中ですが、esp/esmファイルの読込をより完全に出来るようにしました。
といってもまだ全然完全ではないですが。

以前に、Discovered Map MarkerというModを作成しました。
これは、ゲームスタート時に各要塞へ行けるようにマップマーカーのフラグをファストトラベル可能にしただけのものでした。
この際、できれば各大立石へもファストトラベル可能としたかったのですが、CKでどのように探せば良いのかわからず、放置していました。

今回の目的は、MapMarker(0x00000010)を参照しているすべての項目を抜き出して出力し、FormIDを知ることです。
それには、Skyrim.esmを読込み、Worldspaceのデータをすべて適切に解析しなければなりません。
他の項目は基本的に単純なのですが、CellとWorldspaceは癖があります。

※今回で大体必要な解析ができたので、Skyrimのesm/espファイルのバイナリ解析については別途記事にする予定です。

ともかくも、Worldspaceの読込が適切に読込めるようになったので、MapMarkerを参照しているものをすべて抜き出しました。
本来なら、そのデータをコピーして出力すればよいのですが、一部仕組みが分からない点があったため、FormIDを出力して、後はSSEEditで対象をコピーするようにしました。(SSEEditは本当すごいですね)

結果、次のようになりました。

全てのマップマーカーのリスト

FormIDが判明したので、そのまま player.moveto {FromID} としてテレポートもできます。

リストが長いので次の場所へ移しました。
rrryutaro.hatenadiary.com

ファストトラベル可能にする

さて、ファストトラベル可能にするには、"FNAM"(Map Flags)の値を Visible:(1) + Can Travel To (2) = 3 にする必要があります。
SSEEditでコピーして作ったespファイルを読込み、FNAMの値を3に変更して保存します。

実行してみたところ次のように、マップマーカーが表示されました。
実際に全て表示されているか確認してみた所、表示されていないマーカーがありました。

表示されていないマーカーについて、player.moveto {FormID} でテレポートしてみた所、発見される場合と、そうでないマーカーがあります。
基本的にはクエストと紐づいているであろうマーカーは表示されないようです。
特に内戦系は、帝国軍かストームクロークかの野営地のいずれかになる場所は表示されません。
また、ドワーフの遺跡のエレベータの入り口や一部の難破船などもマーカー表示にならないようです。
その他、セーブデータ上で、未発見でマーカーが表示されているものは、ファストトラベル可能な状態に書き換わらないようです。

調べた限りでは、全436箇所中、369箇所が有効でした。

PC版について

コンソールコマンドで tmm 1 とすれば、すべてのマーカーが有効になります。

※当初なぜか表示されるだけで、ファストトラベルできないものと勘違いしていました・・・
※折角なのでPC版も公開しようとNexusに登録したところ、コメントをもらい、確かめたところ表示されました
※う~ん。前に試したときに、ファストトラベルできなかったと気がするのですが、勘違いなのでしょう。

Skyrim SE:http://skyrimspecialedition.2game.info/detail.php?id=9186
Skyrim LE:http://skyrim.2game.info/detail.php?id=83030


以上

Skyrim Mod 製作日記 - 16 - ダンジョンを自動生成する (2)

ウィザードリィ#1のマップを利用したダンジョン生成ですが、とりあえずB4まで作成した段階のModを公開しました。

こんな感じです。
gaming.youtube.com

PS4 Version
https://bethesda.net/en/mods/skyrim/mod-detail/3622828
XB1 Version
https://bethesda.net/en/mods/skyrim/mod-detail/3624110
PC Version
https://bethesda.net/en/mods/skyrim/mod-detail/3624087

Skyrim Mod 製作日記 - 15 - ダンジョンを自動生成する (1)

以前にNIFを閲覧するためのModを作成しました。
rrryutaro.hatenablog.com

この画面の樽や床は全て自動生成したもので、元となるデータはフォルダとファイルの情報のみです。
f:id:rrryutaro:20170325224731p:plain

そこで、これを利用してちょっとしたダンジョンなら自動生成できるだろうと思い立ち、ダンジョン生成のプログラムを製作することにしました。
といっても、前に少し作りこんでいましたので、これも製作を再開といった所です。

ダンジョンの構成を1から考えて作るのは大変なので、ウィザードリィ #1のマップを利用することにします。
次のような仕様としました。

ダンジョン生成プログラムの基本仕様

  • 固定サイズの床などを組み合わせる形とする
  • マップは床や壁の配置を指定したcsv形式のファイルを読込むものとする
    • 行がY方向、列がZ方向
    • 壁を配置する方位をbit位置で 北:1 東:2 南:4 西:8 とし、合算した値の数字を設定する
    • 床壁情報、扉情報、敵情報、その他の情報とそれぞれにcsvを分ける
    • 敵情報とその他の情報は配置時の向きとする
  • その他の情報は別途xml形式で何を配置するかの詳細情報を記載する

マップの作成

表計算ソフトを利用して、セルを方眼にして罫線で壁を書き、床壁情報を書き込みます。
f:id:rrryutaro:20170325230436p:plain

扉情報はこんな感じ。
f:id:rrryutaro:20170325230534p:plain

敵情報はこんな感じ。
f:id:rrryutaro:20170325230629p:plain

あらかじめCKでCell情報や、最初の入り口などは作成しておき、これをプログラムから読込み追加情報を書き出して、CKで読込むとこんな感じになります。
f:id:rrryutaro:20170325230936p:plain

一部見えていない壁などは、床や壁に利用している素材が表面部分しか描画が無いためこのように見えます。
このため、ゲーム上で裏側からだと進入できて表面は壁として機能します。
逆にこれを利用して、一見通路のように見えるのに、通過すると壁になるという、ウィザードリィらしいトラップが実現できます。

ゲーム上から見るとこのような感じです。
f:id:rrryutaro:20170325231304p:plain

なお、ナビメッシュはCKで生成するようにしています。

現在実現できていること、出来るだろうけどまだやっていないこと、たぶん実現できないこと

PS4版基準でModを製作していますので、ウィザードリィらしくしたくてもできないこともあります。

現在できていること

  • 床・壁の配置
  • 扉の配置
  • 上下フロアへの移動手段
  • モンスターの配置(普通に配置しているのでダンジョンに入った瞬間全員動き出す)
  • 宝箱の配置
  • 一方通行の壁(床の裏面を通路にして、通り過ぎると表面が壁となるので)
  • ワープ(単純にMoveToできるスクリプト"DA16DreamwarpTriggerScript"を利用)
  • ダークゾーン(ライトを全て真っ暗にしたCellを作成してそこへワープさせる)

出来るだろうけどまだやっていないこと

  • エレベーターの配置
  • シュー
  • メッセージ表示
  • ダメージ系のトラップ類
  • 各種クエスト

タブンできないこと

  • 隠し扉
  • 一方通行の扉
  • 完全なダークゾーン
  • 呪文などを使えなくするゾーン
  • ランダムワープ
  • 回転床(近いことは出来るが、仕組み的にランダムワープと同じなので)

現在B3まで作っています。さすがにB10まで作るのは結構大変なので、調整してそこそこ動きそうなら公開してみようかと思います。

これまでの失敗について

次のようなことがありました。

  • NavMeshを配置してもPS4で動作しない
  • 壁などがCKで見るとちゃんと表示されるのにゲーム上で表示されない
  • ワープされる位置が指定と異なる

原因は、espファイルを出力しては読込んでを繰り返していたためか、セーブデータにゴミデータが出来上がっていたようです。
Mod導入前のセーブデータから実行したらちゃんと表示されました。


以上