SkyrimのModをプログラムで生成する - 4 - フィールドを扱う

  • 0
    いいね
  • 0
    コメント

    はじめに

    この記事は、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分のバイト数を取り込むようにしていますが、後々個別のフィールドを扱うように、一応リストにしておきます。

    では、フィールドを扱うようにレコードクラスなどに修正を入れます。

    TesRecord
        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;
            }
        }
    
    TesFileReader
            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ファイルを扱う