LoginSignup
0
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-04-08

はじめに

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

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

  • グループ
    • レコード
      • フィールド

レコードクラスとヘッダークラス

前回、グループ、レコード、フィールドの最初にはヘッダー情報があることを記載しました。
ヘッダーには何の情報を扱うかの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での表示名称を使用

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

    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に格納するようにします。

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

    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の情報があるかわかっていますので、ファイルリーダーで、グループやレコードを一つの単位で扱えるようにします。

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;
        }

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

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 - グループを扱う 4 - フィールドを扱う
0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2