Editor/Embedded/ValveResourceFormat/ResourceTypes/KeyValuesOrNTRO.cs

Editor block type that can contain either KeyValues (KV3) or NTRO introspected data. It reads the appropriate backing block based on resource capabilities, exposes the parsed KVObject as Data, and can serialize or write text when backed by a BinaryKV3.

File Access
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using HumanoidRetargeter.EditorTools.Embedded.ValveResourceFormat.Utils;
using System.IO;
using HumanoidRetargeter.EditorTools.Embedded.ValveResourceFormat.Serialization.KeyValues;

#nullable disable

namespace HumanoidRetargeter.EditorTools.Embedded.ValveResourceFormat.ResourceTypes;

    /// <summary>
    /// Block that can contain either <see cref="KVObject"/> or <see cref="NTRO"/> data.
    /// </summary>
    public class KeyValuesOrNTRO : Block
    {
        private readonly string IntrospectionStructName;
        private readonly BlockType KVBlockType;
        /// <inheritdoc/>
        public override BlockType Type => KVBlockType;

        /// <summary>
        /// Gets the parsed data as a <see cref="KVObject"/>.
        /// </summary>
        public KVObject Data { get; private set; }

        private Block BackingData;

        /// <summary>
        /// Initializes a new instance of the <see cref="KeyValuesOrNTRO"/> class.
        /// </summary>
        public KeyValuesOrNTRO()
        {
            KVBlockType = BlockType.DATA;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="KeyValuesOrNTRO"/> class with a specific block type and introspection struct name.
        /// </summary>
        /// <param name="type">The block type.</param>
        /// <param name="introspectionStructName">The introspection struct name for <see cref="NTRO"/> parsing.</param>
        public KeyValuesOrNTRO(BlockType type, string introspectionStructName)
        {
            KVBlockType = type;
            IntrospectionStructName = introspectionStructName;
        }

        /// <inheritdoc/>
        public override void Read(BinaryReader reader)
        {
            // It is possible to have MDAT block with NTRO in a file, but it will be KV3 anyway.
            if (!Resource.ContainsBlockType(BlockType.NTRO) || KVBlockType == BlockType.MDAT)
            {
                var kv3 = new BinaryKV3(KVBlockType)
                {
                    Offset = Offset,
                    Size = Size,
                    Resource = Resource,
                };
                kv3.Read(reader);
                Data = kv3.Data;
                BackingData = kv3;
            }
            else
            {
                var ntro = new NTRO
                {
                    StructName = IntrospectionStructName,
                    Offset = Offset,
                    Size = Size,
                    Resource = Resource,
                };
                ntro.Read(reader);
                Data = ntro.Output;
                BackingData = ntro;
            }
        }

        /// <inheritdoc/>
        public override void Serialize(Stream stream)
        {
            if (BackingData is BinaryKV3 dataKv3)
            {
                dataKv3.Serialize(stream);
                return;
            }

            throw new NotImplementedException("Serializing this block is not yet supported. If you need this, send us a pull request!");
        }

        /// <inheritdoc/>
        /// <remarks>
        /// Writes the data as KV3 text format, regardless of the backing data source.
        /// </remarks>
        public override void WriteText(IndentedTextWriter writer)
        {
            if (BackingData is BinaryKV3 dataKv3)
            {
                dataKv3.GetKV3File().WriteText(writer);
                return;
            }

            BackingData.WriteText(writer);
        }
    }