Editor/HumanoidRetargeter/Embedded/ValveResourceFormat/Resource/Blocks/ResourceEditInfo.cs
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using HumanoidRetargeterVrf.Utils;
using System.IO;
using System.Text;
using HumanoidRetargeterVrf.Blocks.ResourceEditInfoStructs;
using HumanoidRetargeterVrf.Serialization.KeyValues;
namespace HumanoidRetargeterVrf.Blocks
{
/// <summary>
/// "REDI" block. ResourceEditInfoBlock_t.
/// </summary>
public class ResourceEditInfo : RawBinary
{
// Serialize legacy REDI info by copying raw data from the original resource because we have no plans to support NTRO serialization
/// <inheritdoc/>
public override BlockType Type => BlockType.REDI;
/// <summary>
/// Gets the list of input dependencies.
/// </summary>
public List<InputDependency> InputDependencies { get; } = [];
/// <summary>
/// Gets the list of additional input dependencies.
/// </summary>
public List<InputDependency> AdditionalInputDependencies { get; } = [];
/// <summary>
/// Gets the list of argument dependencies.
/// </summary>
public List<ArgumentDependency> ArgumentDependencies { get; } = [];
/// <summary>
/// Gets the list of special dependencies.
/// </summary>
public List<SpecialDependency> SpecialDependencies { get; } = [];
/// <summary>
/// Gets the list of additional related files.
/// </summary>
public List<AdditionalRelatedFile> AdditionalRelatedFiles { get; } = [];
/// <summary>
/// Gets the list of child resources.
/// </summary>
public List<string> ChildResourceList { get; } = [];
/// <summary>
/// Gets the searchable user data.
/// </summary>
public KVObject SearchableUserData { get; } = new("m_SearchableUserData"); // Maybe these should be split..
/// <inheritdoc/>
public override void Read(BinaryReader reader)
{
var subBlock = 0;
int AdvanceGetCount()
{
reader.BaseStream.Position = Offset + (subBlock * 8);
var offset = reader.ReadUInt32();
var count = reader.ReadUInt32();
reader.BaseStream.Position = Offset + (subBlock * 8) + offset;
subBlock++;
return (int)count;
}
void ReadItems<T>(List<T> list, Func<BinaryReader, T> constructor)
{
var count = AdvanceGetCount();
list.EnsureCapacity(count);
for (var i = 0; i < count; i++)
{
var item = constructor.Invoke(reader);
list.Add(item);
}
}
void ReadKeyValues<T>(KVObject kvObject, Func<BinaryReader, T> valueReader)
{
var count = AdvanceGetCount();
kvObject.Properties.EnsureCapacity(kvObject.Properties.Count + count);
for (var i = 0; i < count; i++)
{
var key = reader.ReadOffsetString(Encoding.UTF8);
var value = valueReader.Invoke(reader);
// Note: we may override existing keys
kvObject.Properties[key] = new KVValue(value);
}
}
ReadItems(InputDependencies, static (reader) => new InputDependency(reader));
ReadItems(AdditionalInputDependencies, static (reader) => new InputDependency(reader));
ReadItems(ArgumentDependencies, static (reader) => new ArgumentDependency(reader));
ReadItems(SpecialDependencies, static (reader) => new SpecialDependency(reader));
var customDependencies = AdvanceGetCount();
if (customDependencies > 0)
{
throw new NotImplementedException("CustomDependencies in REDI are not handled.\n" +
"Please report this on https://github.com/HumanoidRetargeterVrf/HumanoidRetargeterVrf and provide the file that caused this exception.");
}
ReadItems(AdditionalRelatedFiles, static (reader) => new AdditionalRelatedFile(reader));
ReadItems(ChildResourceList, static (reader) =>
{
var id = reader.ReadUInt64();
var name = reader.ReadOffsetString(Encoding.UTF8);
var unknown = reader.ReadInt32();
return name; // Ignoring 'id' to match RED2
});
ReadKeyValues(SearchableUserData, static (reader) => (long)reader.ReadInt32());
ReadKeyValues(SearchableUserData, static (reader) => (double)reader.ReadSingle());
ReadKeyValues(SearchableUserData, static (reader) => reader.ReadOffsetString(Encoding.UTF8));
}
/// <inheritdoc/>
public override void WriteText(IndentedTextWriter writer)
{
throw new NotSupportedException("Diagnostic REDI serialization is not included in model recovery.");
}
}
}