mirror of
https://github.com/AssetRipper/AssetRipper.git
synced 2025-12-11 20:15:29 +01:00
233 lines
6.9 KiB
C#
233 lines
6.9 KiB
C#
using AssetRipper.AssemblyDumper.Utils;
|
|
using AssetRipper.IO.Files.SerializedFiles;
|
|
using AssetRipper.Tpk.Shared;
|
|
using AssetRipper.Tpk.TypeTrees;
|
|
using System.Diagnostics;
|
|
|
|
namespace AssetRipper.AssemblyDumper;
|
|
|
|
internal sealed class UniversalNode : IEquatable<UniversalNode>, IDeepCloneable<UniversalNode>
|
|
{
|
|
/// <summary>
|
|
/// The current type name
|
|
/// </summary>
|
|
public string TypeName { get => typeName; set => typeName = value ?? ""; }
|
|
/// <summary>
|
|
/// The original type name as obtained from the tpk file
|
|
/// </summary>
|
|
public string OriginalTypeName
|
|
{
|
|
get => string.IsNullOrEmpty(originalTypeName) ? TypeName : originalTypeName;
|
|
set => originalTypeName = value ?? "";
|
|
}
|
|
public string Name { get => name; set => name = value ?? ""; }
|
|
/// <summary>
|
|
/// The original name as obtained from the tpk file
|
|
/// </summary>
|
|
public string OriginalName
|
|
{
|
|
get => string.IsNullOrEmpty(originalName) ? Name : originalName;
|
|
set => originalName = value ?? "";
|
|
}
|
|
public short Version { get; set; }
|
|
public TransferMetaFlags MetaFlag { get; set; }
|
|
public List<UniversalNode> SubNodes { get => subNodes; set => subNodes = value ?? new(); }
|
|
|
|
private string originalTypeName = "";
|
|
private string originalName = "";
|
|
private string typeName = "";
|
|
private string name = "";
|
|
private List<UniversalNode> subNodes = new();
|
|
|
|
public bool IgnoreInMetaFiles => MetaFlag.IsIgnoreInMetaFiles();
|
|
public bool AlignBytes => MetaFlag.IsAlignBytes();
|
|
public bool TreatIntegerAsBoolean => MetaFlag.IsTreatIntegerValueAsBoolean();
|
|
private bool TreatIntegerAsChar => MetaFlag.IsCharPropertyMask();
|
|
|
|
public NodeType NodeType
|
|
{
|
|
get
|
|
{
|
|
return subNodes.Count == 0
|
|
? TypeName switch
|
|
{
|
|
"bool" => NodeType.Boolean,
|
|
//"char" => NodeType.Character,
|
|
"char" => NodeType.UInt8,
|
|
"SInt8" => NodeType.Int8,
|
|
"UInt8" => NodeType.UInt8,
|
|
"short" or "SInt16" => NodeType.Int16,
|
|
"ushort" or "UInt16" or "unsigned short" => TreatIntegerAsChar ? NodeType.Character : NodeType.UInt16,
|
|
"int" or "SInt32" or "Type*" or "EntityId" => NodeType.Int32,
|
|
"uint" or "UInt32" or "unsigned int" => NodeType.UInt32,
|
|
"SInt64" or "long long" => NodeType.Int64,
|
|
"UInt64" or "FileSize" or "unsigned long long" => NodeType.UInt64,//FileSize is used in StreamedResource.m_Offset 2020.1+
|
|
"float" => NodeType.Single,
|
|
"double" => NodeType.Double,
|
|
_ => NodeType.Type,
|
|
}
|
|
: TypeName switch
|
|
{
|
|
"Array" => NodeType.Array,
|
|
"vector" or "staticvector" or "set" => NodeType.Vector,
|
|
"map" => NodeType.Map,
|
|
"pair" => NodeType.Pair,
|
|
"TypelessData" => NodeType.TypelessData,
|
|
"string" or Passes.Pass002_RenameSubnodes.Utf8StringName => NodeType.String,
|
|
_ => NodeType.Type,
|
|
};
|
|
}
|
|
}
|
|
|
|
public UniversalNode()
|
|
{
|
|
}
|
|
|
|
public bool TryGetSubNodeByName(string nodeName, [NotNullWhen(true)] out UniversalNode? subnode)
|
|
{
|
|
subnode = SubNodes.SingleOrDefault(n => n.Name == nodeName);
|
|
return subnode is not null;
|
|
}
|
|
|
|
public UniversalNode? TryGetSubNodeByName(string nodeName)
|
|
{
|
|
return SubNodes.SingleOrDefault(n => n.Name == nodeName);
|
|
}
|
|
|
|
public bool TryGetSubNodeByTypeAndName(string nodeTypeName, string nodeName, [NotNullWhen(true)] out UniversalNode? subnode)
|
|
{
|
|
subnode = SubNodes.SingleOrDefault(n => n.Name == nodeName && n.TypeName == nodeTypeName);
|
|
return subnode is not null;
|
|
}
|
|
|
|
public UniversalNode GetSubNodeByName(string nodeName)
|
|
{
|
|
return SubNodes.Single(n => n.Name == nodeName);
|
|
}
|
|
|
|
public static UniversalNode FromTpkUnityNode(TpkUnityNode tpkNode, TpkStringBuffer stringBuffer, TpkUnityNodeBuffer nodeBuffer)
|
|
{
|
|
UniversalNode result = new UniversalNode();
|
|
result.TypeName = GetFixedTypeName(stringBuffer[tpkNode.TypeName]);
|
|
result.OriginalTypeName = result.TypeName;
|
|
result.Name = stringBuffer[tpkNode.Name];
|
|
result.OriginalName = result.Name;
|
|
result.Version = tpkNode.Version;
|
|
result.MetaFlag = (TransferMetaFlags)tpkNode.MetaFlag;
|
|
result.SubNodes = tpkNode.SubNodes
|
|
.Select(nodeIndex => FromTpkUnityNode(nodeBuffer[nodeIndex], stringBuffer, nodeBuffer))
|
|
.ToList();
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only store one name for each primitive integer size.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Although this deduplicates, it also prevents these loaded type trees from being used in making new serialized files.
|
|
/// </remarks>
|
|
/// <param name="originalName"></param>
|
|
/// <returns></returns>
|
|
private static string GetFixedTypeName(string originalName)
|
|
{
|
|
return originalName switch
|
|
{
|
|
"short" => "SInt16",
|
|
"int" => "SInt32",
|
|
"long long" => "SInt64",
|
|
"unsigned short" => "UInt16",
|
|
"unsigned int" => "UInt32",
|
|
"unsigned long long" => "UInt64",
|
|
_ => originalName,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deep clones a node and all its subnodes<br/>
|
|
/// Warning: Deep cloning a node with a circular hierarchy will cause an endless loop
|
|
/// </summary>
|
|
/// <returns>The new node</returns>
|
|
public UniversalNode DeepClone()
|
|
{
|
|
UniversalNode clone = new UniversalNode();
|
|
clone.TypeName = TypeName;
|
|
clone.originalTypeName = originalTypeName;
|
|
clone.Name = Name;
|
|
clone.originalName = originalName;
|
|
clone.Version = Version;
|
|
clone.MetaFlag = MetaFlag;
|
|
clone.SubNodes = SubNodes.ConvertAll(x => x.DeepClone());
|
|
return clone;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shallow clones a node but not its subnodes
|
|
/// </summary>
|
|
/// <returns>The new node</returns>
|
|
public UniversalNode ShallowClone()
|
|
{
|
|
UniversalNode clone = new UniversalNode();
|
|
clone.TypeName = TypeName;
|
|
clone.originalTypeName = originalTypeName;
|
|
clone.Name = Name;
|
|
clone.originalName = originalName;
|
|
clone.Version = Version;
|
|
clone.MetaFlag = MetaFlag;
|
|
clone.SubNodes = SubNodes.ToList();
|
|
return clone;
|
|
}
|
|
|
|
public UniversalNode DeepCloneAsRootNode()
|
|
{
|
|
UniversalNode clone = DeepClone();
|
|
clone.Name = "Base";
|
|
clone.OriginalName = clone.Name;
|
|
return clone;
|
|
}
|
|
|
|
public UniversalNode ShallowCloneAsRootNode()
|
|
{
|
|
UniversalNode clone = ShallowClone();
|
|
clone.Name = "Base";
|
|
clone.OriginalName = clone.Name;
|
|
return clone;
|
|
}
|
|
|
|
public override bool Equals(object? obj)
|
|
{
|
|
return Equals(obj as UniversalNode);
|
|
}
|
|
|
|
public bool Equals(UniversalNode? other)
|
|
{
|
|
return other is not null &&
|
|
TypeName == other.TypeName &&
|
|
OriginalTypeName == other.OriginalTypeName &&
|
|
Name == other.Name &&
|
|
OriginalName == other.OriginalName &&
|
|
Version == other.Version &&
|
|
MetaFlag == other.MetaFlag &&
|
|
EqualityComparer<List<UniversalNode>>.Default.Equals(SubNodes, other.SubNodes);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return HashCode.Combine(TypeName, OriginalTypeName, Name, OriginalName, Version, MetaFlag, SubNodes);
|
|
}
|
|
|
|
public static bool operator ==(UniversalNode? left, UniversalNode? right)
|
|
{
|
|
return EqualityComparer<UniversalNode>.Default.Equals(left, right);
|
|
}
|
|
|
|
public static bool operator !=(UniversalNode? left, UniversalNode? right)
|
|
{
|
|
return !(left == right);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"{TypeName} {Name}";
|
|
}
|
|
}
|