SkyrimのModをプログラムで生成する - 3 - レコードを扱う

#はじめに
この記事は、SkyrimというゲームソフトのModファイルのバリナリ解析とそれを利用してModを生成するC#のコードについて記載していくシリーズ物です。
ファイルフォーマットについての詳しい情報は以下のサイトがあります。(以降UESP)
[Tes5Mod:Mod File Format - The Unofficial Elder Scrolls Pages (UESP)](http://en.uesp.net/wiki/Tes5Mod:Mod_File_Format)

前回はグループについて扱いましたので、今回はレコードについてです。

- グループ
    - <font color=red>レコード</font>
        - フィールド

#レコードクラスとヘッダークラス
前回、グループ、レコード、フィールドの最初にはヘッダー情報があることを記載しました。
ヘッダーには何の情報を扱うかの4文字の記号、全体のデータサイズなどが記載されています。
ヘッダーは24byteあり次のようになっています。

|項目名|型|byte数|byte数合計|説明|
|:--|:--|--:|--:|:--|
|Signature|string|4|4|どのような情報を扱うかを識別する記号。|
|DataSize|uint|4|8|ヘッダーサイズを含まないレコードの全体サイズ。|
|RecordFlags|uint|4|12|あまり気にしない。詳細はUESPのRecordsのflags参照。|
|FormID|uint|4|16|mod内でデータを一意に識別するためのID。|
|VersionControlInfo1|uint|4|20|あまり気にしない。詳細はUESPのRecordsのrevision参照。|
|FormVersion|ushot|2|22|あまり気にしない。詳細はUESPのRecordsのversion参照。|
|VersionControlInfo2|ushot|2|24|あまり気にしない。詳細はUESPのRecordsのdata参照。|
※項目名はSSEEditでの表示名称を使用

では、ヘッダー情報を扱うクラスを作成します。

```C#
    public class TesHeader : TesBase
    {
        public TesString Signature { get; }
        public TesUInt32 DataSize { get; set; }
        public TesUInt32 RecordFlags { get; }
        public TesUInt32 FormID { get; set; }
        public TesUInt32 VersionControlInfo1 { get; }
        public TesUInt16 FormVersion { get; }
        public TesUInt16 VersionControlInfo2 { get; }

        public TesHeader(TesFileReader fr)
        {
            Signature = new TesString(fr);
            DataSize = new TesUInt32(fr);
            RecordFlags = new TesUInt32(fr);
            FormID = new TesUInt32(fr);
            VersionControlInfo1 = new TesUInt32(fr);
            FormVersion = new TesUInt16(fr);
            VersionControlInfo2 = new TesUInt16(fr);

            OutputItems.Add(Signature);
            OutputItems.Add(DataSize);
            OutputItems.Add(RecordFlags);
            OutputItems.Add(FormID);
            OutputItems.Add(VersionControlInfo1);
            OutputItems.Add(FormVersion);
            OutputItems.Add(VersionControlInfo2);
        }
    }
}
```

コンストラクターでファイルリーダーを受け取り、順次読み込んで行きます。
各項目は必ず順番通りにファイル出力用の`OutputItems`に格納するようにします。

続いて、レコードクラスを作成します。

```C#
    public class TesRecord : TesBase
    {
        public TesHeader Header { get; }
        public TesBytes FieldData { get; }

        public TesRecord(TesFileReader fr)
        {
            Header = new TesHeader(fr);
            FieldData = fr.GetBytes(Header.DataSize);
        }
    }
```

とりあえずまだ、フィールドデータは丸ごと読込む形とします。

#グループクラスでレコードクラスを扱えるようにする
前回、グループクラスは丸ごとデータを読込んでいましたが、レコードを扱うように修正を加えます。
基本的な処理方法は、ファイルを`DataSize`に合わせて順次読込んでいく形です。（このため、どこかで読込み間違えると大変なことになります）
これまでの内容で、どこに`DataSize`の情報があるかわかっていますので、ファイルリーダーで、グループやレコードを一つの単位で扱えるようにします。

```C#:TesFileReader
        public TesFileReader GetGroup(bool next = true)
        {
            long count = GetUInt32(4, false);
            TesFileReader result = new TesFileReader(br, pos, pos + count);
            if (next)
                pos += count;

            return result;
        }
        public TesFileReader GetRecord(bool next = true)
        {
            long count = GetUInt32(4, false) + 24;
            TesFileReader result = new TesFileReader(br, pos, pos + count);
            if (next)
                pos += count;

            return result;
        }
```

続いて、グループクラスです。

```C#:TesGroup
    public class TesGroup : TesBase
    {
        public TesString GRUP { get; }
        public TesUInt32 DataSize { get; }
        public public TesBytes Other { get; }
        public TesList<TesRecord> Records = new TesList<TesRecord>();

        public TesGroup(TesFileReader fr)
        {
            GRUP = new TesString(fr);
            DataSize = new TesUInt32(fr);
            Other = new TesBytes(fr.GetBytes(16));
            while (!fr.EOF)
            {
                Records.Add(new TesRecord(fr.GetRecord()));
            }

            OutputItems.Add(GRUP);
            OutputItems.Add(DataSize);
            OutputItems.Add(Other);
            OutputItems.Add(Records);
        }
    }
```
※前回グループクラスの内容には`OutputItems`への追加を記載し忘れていました

ファイルリーダーの`GetGroup`で1グループ分の読み取り範囲のファイルリーダーを`TesGroup`のコンストラクターに渡し、ヘッダー情報を読込み後、読み取り範囲が終了するまで、繰り返しレコードクラスを作成します。


以上、今回はここまでです。

|前回|次回|
|:--|--:|
|[2 - グループを扱う](http://qiita.com/rrryutaro/items/220515d8971c26f786df)|[4 - フィールドを扱う](http://qiita.com/rrryutaro/items/d49b7b610128fb69261f)|