はじめに
この記事は、SkyrimというゲームソフトのModファイルのバリナリ解析とそれを利用してModを生成するC#のコードについて記載していくシリーズ物です。
ファイルフォーマットについての詳しい情報は以下のサイトがあります。(以降UESP)
Tes5Mod:Mod File Format - The Unofficial Elder Scrolls Pages (UESP)
前回はレコードについて扱いましたので、今回はフィールドについてです。
- グループ
- レコード
- フィールド
- レコード
フィールドクラス
フィールドのヘッダー構成は次の通りです。
項目名 | 型 | byte数 | byte数合計 | 説明 |
---|---|---|---|---|
Signature | string | 4 | 4 | どのような情報を扱うかを識別する記号。 |
DataSize | ushort | 2 | 6 | ヘッダーサイズを含まないフィールドの全体サイズ。 |
フィールド内でもデータの内容が分かれていたりしますが、後は、構造体として何byte目が何の情報で・・・のように扱うことになります。
public class TesField : TesBase
{
public TesString Signature { get; }
public TesUInt16 DataSize { get; }
public TesList<ITesBase> Values { get; } = new TesList<ITesBase>();
public TesField(TesFileReader fr)
{
Signature = new TesString(fr.GetString(4));
DataSize = new TesUInt16(fr);
Values.Add(fr.GetBytes(DataSize.Value));
OutputItems.Add(Signature);
OutputItems.Add(DataSize);
OutputItems.Add(Values);
}
}
まずは、そのままDataSize
分のバイト数を取り込むようにしていますが、後々個別のフィールドを扱うように、一応リストにしておきます。
では、フィールドを扱うようにレコードクラスなどに修正を入れます。
public class TesRecord : TesBase
{
public TesHeader Header { get; }
public Dictionary<string, TesList<TesField>> Fields { get; } = new Dictionary<string, TesList<TesField>>();
public TesRecord(TesFileReader fr)
{
Header = new TesHeader(fr);
OutputItems.Add(Header);
while (!fr.EOF)
{
TesField field = ReadField(fr) ?? new TesField(fr.GetField());
AddField(field);
}
}
public void AddField(TesField field)
{
OutputItems.Add(field);
if (!Fields.ContainsKey(field.Signature))
Fields.Add(field.Signature, new TesList<TesField>());
Fields[field.Signature].Add(field);
}
public virtual TesField ReadField(TesFileReader fr)
{
TesField result = null;
string id = fr.GetTypeID();
switch (id)
{
}
return result;
}
}
public TesFileReader GetField(bool next = true)
{
long count = GetUInt16(4, false) + 6;
TesFileReader result = new TesFileReader(br, pos, pos + count);
if (next)
pos += count;
return result;
}
public string GetTypeID(long offset = 0)
{
string result = GetString(4, offset, false);
return result;
}
レコードクラスはそこそこ手入れをしました。
まず、フィールドについては、出力用のOutputItems
に追加すると共に、SignatureでアクセスできるようにDictionary
に登録することにします。
また、同じSignatureで複数フィールドある場合があるため、リストに追加します。
フィールドの読み取りは、後々レコードクラスを継承して個別のレコードに対応した際に、必要なフィールドを処理できるようにしておきます。
特に、いくつかのフィールドは同じSignatureでも、中身が異なるケースがあるため、注意が必要です。
以上、今回はここまでです。
前回 | 次回 |
---|---|
3 - レコードを扱う | 5 - Modファイルを扱う |