Editor/HumanoidRetargeter/Embedded/ValveResourceFormat/Resource/Blocks/ResourceExtRefList.cs
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using HumanoidRetargeterVrf.Utils;
using System.IO;
using System.Linq;
using System.Text;
#nullable disable
namespace HumanoidRetargeterVrf.Blocks
{
/// <summary>
/// "RERL" block. ResourceExtRefList_t.
/// </summary>
public class ResourceExtRefList : Block
{
/// <inheritdoc/>
public override BlockType Type => BlockType.RERL;
/// <summary>
/// Represents an external resource reference.
/// </summary>
public class ResourceReferenceInfo
{
/// <summary>
/// Gets or sets the resource id.
/// </summary>
public ulong Id { get; set; }
/// <summary>
/// Gets or sets the resource name.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Writes the resource reference info as text.
/// </summary>
public void WriteText(IndentedTextWriter writer)
{
writer.WriteLine("ResourceReferenceInfo_t");
writer.WriteLine("{");
writer.Indent++;
writer.WriteLine("uint64 m_nId = 0x{0:X16}", Id);
writer.WriteLine("CResourceString m_pResourceName = \"{0}\"", Name);
writer.Indent--;
writer.WriteLine("}");
}
}
/// <summary>
/// Gets the list of external resource references.
/// </summary>
public List<ResourceReferenceInfo> ResourceRefInfoList { get; private set; }
/// <summary>
/// Gets the resource name mapped to the specified identifier, or <c>null</c> if missing.
/// </summary>
public string this[ulong id]
{
get
{
var value = ResourceRefInfoList.FirstOrDefault(c => c.Id == id);
return value?.Name;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ResourceExtRefList"/> class.
/// </summary>
public ResourceExtRefList()
{
ResourceRefInfoList = [];
}
/// <inheritdoc/>
public override void Read(BinaryReader reader)
{
reader.BaseStream.Position = Offset;
var offset = reader.ReadUInt32();
var size = reader.ReadUInt32();
if (size == 0)
{
return;
}
reader.BaseStream.Position += offset - 8; // 8 is 2 uint32s we just read
ResourceRefInfoList.EnsureCapacity((int)size);
for (var i = 0; i < size; i++)
{
var resInfo = new ResourceReferenceInfo { Id = reader.ReadUInt64() };
var previousPosition = reader.BaseStream.Position;
// jump to string
// offset is counted from current position,
// so we will need to add 8 to position later
reader.BaseStream.Position += reader.ReadInt64();
resInfo.Name = reader.ReadNullTermString(Encoding.UTF8);
ResourceRefInfoList.Add(resInfo);
reader.BaseStream.Position = previousPosition + 8; // 8 is to account for string offset
}
}
/// <inheritdoc/>
public override void Serialize(Stream stream)
{
using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true);
if (ResourceRefInfoList.Count == 0)
{
writer.Write(0u);
writer.Write(0u);
return;
}
writer.Write(8u); // size of the 2 ints we are writing right now
writer.Write(ResourceRefInfoList.Count);
const uint EntrySize = sizeof(ulong) + sizeof(long);
var stringsStartOffset = ResourceRefInfoList.Count * EntrySize;
var currentStringOffset = 0;
for (var i = 0; i < ResourceRefInfoList.Count; i++)
{
var refInfo = ResourceRefInfoList[i];
writer.Write(refInfo.Id);
var currentPosAfterID = sizeof(ulong) + i * EntrySize;
var stringAbsolutePos = stringsStartOffset + currentStringOffset;
var relativeOffset = stringAbsolutePos - currentPosAfterID;
writer.Write(relativeOffset);
currentStringOffset += Encoding.UTF8.GetByteCount(refInfo.Name) + 1;
}
foreach (var refInfo in ResourceRefInfoList)
{
var nameBytes = Encoding.UTF8.GetBytes(refInfo.Name);
writer.Write(nameBytes);
writer.Write((byte)0);
}
}
/// <inheritdoc/>
/// <remarks>
/// Outputs the external reference list in a structured format showing resource IDs and names.
/// </remarks>
public override void WriteText(IndentedTextWriter writer)
{
writer.WriteLine("ResourceExtRefList_t");
writer.WriteLine("{");
writer.Indent++;
writer.WriteLine("Struct m_resourceRefInfoList[{0}] =", ResourceRefInfoList.Count);
writer.WriteLine("[");
writer.Indent++;
foreach (var refInfo in ResourceRefInfoList)
{
refInfo.WriteText(writer);
}
writer.Indent--;
writer.WriteLine("]");
writer.Indent--;
writer.WriteLine("}");
}
}
}