This commit is contained in:
Razmoth
2023-09-22 20:31:55 +04:00
parent d594f90fec
commit 6ebeaeee3f
48 changed files with 5886 additions and 81 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -93,15 +93,15 @@ namespace AssetStudio
public class SkeletonPose public class SkeletonPose
{ {
public xform[] m_X; public XForm[] m_X;
public SkeletonPose(ObjectReader reader) public SkeletonPose(ObjectReader reader)
{ {
int numXforms = reader.ReadInt32(); int numXforms = reader.ReadInt32();
m_X = new xform[numXforms]; m_X = new XForm[numXforms];
for (int i = 0; i < numXforms; i++) for (int i = 0; i < numXforms; i++)
{ {
m_X[i] = new xform(reader); m_X[i] = reader.ReadXForm(reader.version);
} }
} }
} }
@@ -118,13 +118,13 @@ namespace AssetStudio
public class Handle public class Handle
{ {
public xform m_X; public XForm m_X;
public uint m_ParentHumanIndex; public uint m_ParentHumanIndex;
public uint m_ID; public uint m_ID;
public Handle(ObjectReader reader) public Handle(ObjectReader reader)
{ {
m_X = new xform(reader); m_X = reader.ReadXForm(reader.version);
m_ParentHumanIndex = reader.ReadUInt32(); m_ParentHumanIndex = reader.ReadUInt32();
m_ID = reader.ReadUInt32(); m_ID = reader.ReadUInt32();
} }
@@ -132,7 +132,7 @@ namespace AssetStudio
public class Collider public class Collider
{ {
public xform m_X; public XForm m_X;
public uint m_Type; public uint m_Type;
public uint m_XMotionType; public uint m_XMotionType;
public uint m_YMotionType; public uint m_YMotionType;
@@ -144,7 +144,7 @@ namespace AssetStudio
public Collider(ObjectReader reader) public Collider(ObjectReader reader)
{ {
m_X = new xform(reader); m_X = reader.ReadXForm(reader.version);
m_Type = reader.ReadUInt32(); m_Type = reader.ReadUInt32();
m_XMotionType = reader.ReadUInt32(); m_XMotionType = reader.ReadUInt32();
m_YMotionType = reader.ReadUInt32(); m_YMotionType = reader.ReadUInt32();
@@ -158,7 +158,7 @@ namespace AssetStudio
public class Human public class Human
{ {
public xform m_RootX; public XForm m_RootX;
public Skeleton m_Skeleton; public Skeleton m_Skeleton;
public SkeletonPose m_SkeletonPose; public SkeletonPose m_SkeletonPose;
public Hand m_LeftHand; public Hand m_LeftHand;
@@ -183,7 +183,7 @@ namespace AssetStudio
public Human(ObjectReader reader) public Human(ObjectReader reader)
{ {
var version = reader.version; var version = reader.version;
m_RootX = new xform(reader); m_RootX = reader.ReadXForm(reader.version);
m_Skeleton = new Skeleton(reader); m_Skeleton = new Skeleton(reader);
m_SkeletonPose = new SkeletonPose(reader); m_SkeletonPose = new SkeletonPose(reader);
m_LeftHand = new Hand(reader); m_LeftHand = new Hand(reader);
@@ -243,7 +243,7 @@ namespace AssetStudio
public int[] m_HumanSkeletonIndexArray; public int[] m_HumanSkeletonIndexArray;
public int[] m_HumanSkeletonReverseIndexArray; public int[] m_HumanSkeletonReverseIndexArray;
public int m_RootMotionBoneIndex; public int m_RootMotionBoneIndex;
public xform m_RootMotionBoneX; public XForm m_RootMotionBoneX;
public Skeleton m_RootMotionSkeleton; public Skeleton m_RootMotionSkeleton;
public SkeletonPose m_RootMotionSkeletonPose; public SkeletonPose m_RootMotionSkeletonPose;
public int[] m_RootMotionSkeletonIndexArray; public int[] m_RootMotionSkeletonIndexArray;
@@ -271,7 +271,7 @@ namespace AssetStudio
} }
m_RootMotionBoneIndex = reader.ReadInt32(); m_RootMotionBoneIndex = reader.ReadInt32();
m_RootMotionBoneX = new xform(reader); m_RootMotionBoneX = reader.ReadXForm(reader.version);
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
{ {

View File

@@ -297,6 +297,7 @@ namespace AssetStudio
public class MeshBlendShape public class MeshBlendShape
{ {
public string name;
public uint firstVertex; public uint firstVertex;
public uint vertexCount; public uint vertexCount;
public bool hasNormals; public bool hasNormals;
@@ -308,7 +309,7 @@ namespace AssetStudio
if (version[0] == 4 && version[1] < 3) //4.3 down if (version[0] == 4 && version[1] < 3) //4.3 down
{ {
var name = reader.ReadAlignedString(); name = reader.ReadAlignedString();
} }
firstVertex = reader.ReadUInt32(); firstVertex = reader.ReadUInt32();
vertexCount = reader.ReadUInt32(); vertexCount = reader.ReadUInt32();

View File

@@ -1,11 +1,10 @@
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using static AssetStudio.AssetsHelper;
namespace AssetStudio namespace AssetStudio
{ {
public sealed class PPtr<T> where T : Object public sealed class PPtr<T> : IYAMLExportable where T : Object
{ {
public int m_FileID; public int m_FileID;
public long m_PathID; public long m_PathID;
@@ -13,6 +12,13 @@ namespace AssetStudio
private SerializedFile assetsFile; private SerializedFile assetsFile;
private int index = -2; //-2 - Prepare, -1 - Missing private int index = -2; //-2 - Prepare, -1 - Missing
public PPtr(int m_FileID, long m_PathID, SerializedFile assetsFile)
{
this.m_FileID = m_FileID;
this.m_PathID = m_PathID;
this.assetsFile = assetsFile;
}
public PPtr(ObjectReader reader) public PPtr(ObjectReader reader)
{ {
m_FileID = reader.ReadInt32(); m_FileID = reader.ReadInt32();
@@ -20,6 +26,14 @@ namespace AssetStudio
assetsFile = reader.assetsFile; assetsFile = reader.assetsFile;
} }
public YAMLNode ExportYAML(int[] version)
{
var node = new YAMLMappingNode();
node.Style = MappingStyle.Flow;
node.Add("fileID", m_FileID);
return node;
}
private bool TryGetAssetsFile(out SerializedFile result) private bool TryGetAssetsFile(out SerializedFile result)
{ {
result = null; result = null;
@@ -129,6 +143,11 @@ namespace AssetStudio
m_PathID = m_Object.m_PathID; m_PathID = m_Object.m_PathID;
} }
public PPtr<T2> Cast<T2>() where T2 : Object
{
return new PPtr<T2>(m_FileID, m_PathID, assetsFile);
}
public bool IsNull => m_PathID == 0 || m_FileID < 0; public bool IsNull => m_PathID == 0 || m_FileID < 0;
} }
} }

View File

@@ -184,6 +184,20 @@ namespace AssetStudio
return new Matrix4x4(ReadSingleArray(16)); return new Matrix4x4(ReadSingleArray(16));
} }
public XForm ReadXForm(int[] version, bool isVector4 = false)
{
var t = (version[0] > 5 || (version[0] == 5 && version[1] >= 4)) && !isVector4 ? ReadVector3() : (Vector3)ReadVector4();//5.4 and up
var q = ReadQuaternion();
var s = (version[0] > 5 || (version[0] == 5 && version[1] >= 4)) && !isVector4 ? ReadVector3() : (Vector3)ReadVector4();//5.4 and up
return new XForm(t, q, s);
}
public Float ReadFloat()
{
return new Float(ReadSingle());
}
public int ReadMhy0Int() public int ReadMhy0Int()
{ {
var buffer = ReadBytes(6); var buffer = ReadBytes(6);
@@ -224,6 +238,16 @@ namespace AssetStudio
return ReadBytes(ReadInt32()); return ReadBytes(ReadInt32());
} }
public short[] ReadInt16Array()
{
return ReadArray(ReadInt16, ReadInt32());
}
public short[] ReadInt16Array(int length)
{
return ReadArray(ReadInt16, length);
}
public ushort[] ReadUInt16Array() public ushort[] ReadUInt16Array()
{ {
return ReadArray(ReadUInt16, ReadInt32()); return ReadArray(ReadUInt16, ReadInt32());

View File

@@ -20,5 +20,29 @@ namespace AssetStudio
} }
} }
} }
public static void AlignStream(this Stream stream)
{
stream.AlignStream(4);
}
public static void AlignStream(this Stream stream, int alignment)
{
var pos = stream.Position;
var mod = pos % alignment;
if (mod != 0)
{
var rem = alignment - mod;
for (int _ = 0; _ < rem; _++)
{
if (!stream.CanWrite)
{
throw new IOException("End of stream");
}
stream.WriteByte(0);
}
}
}
} }
} }

27
AssetStudio/Math/Float.cs Normal file
View File

@@ -0,0 +1,27 @@
namespace AssetStudio
{
public struct Float : IYAMLExportable
{
public float Value;
public Float(float value)
{
Value = value;
}
public static implicit operator Float(float value)
{
return new Float(value);
}
public static implicit operator float(Float value)
{
return value.Value;
}
public YAMLNode ExportYAML(int[] version)
{
return new YAMLScalarNode(Value);
}
}
}

View File

@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
namespace AssetStudio namespace AssetStudio
{ {
[StructLayout(LayoutKind.Sequential, Pack = 4)] [StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct Quaternion : IEquatable<Quaternion> public struct Quaternion : IEquatable<Quaternion>, IYAMLExportable
{ {
public float X; public float X;
public float Y; public float Y;
@@ -83,6 +83,16 @@ namespace AssetStudio
{ {
return !(lhs == rhs); return !(lhs == rhs);
} }
public YAMLNode ExportYAML(int[] version)
{
var node = new YAMLMappingNode();
node.Style = MappingStyle.Flow;
node.Add("x", X);
node.Add("y", Y);
node.Add("z", Z);
node.Add("w", W);
return node;
}
private const float kEpsilon = 0.000001F; private const float kEpsilon = 0.000001F;
} }

View File

@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
namespace AssetStudio namespace AssetStudio
{ {
[StructLayout(LayoutKind.Sequential, Pack = 4)] [StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct Vector3 : IEquatable<Vector3> public struct Vector3 : IEquatable<Vector3>, IYAMLExportable
{ {
public float X; public float X;
public float Y; public float Y;
@@ -87,6 +87,16 @@ namespace AssetStudio
return X * X + Y * Y + Z * Z; return X * X + Y * Y + Z * Z;
} }
public YAMLNode ExportYAML(int[] version)
{
var node = new YAMLMappingNode();
node.Style = MappingStyle.Flow;
node.Add("x", X);
node.Add("y", Y);
node.Add("z", Z);
return node;
}
public static Vector3 Zero => new Vector3(); public static Vector3 Zero => new Vector3();
public static Vector3 One => new Vector3(1.0f, 1.0f, 1.0f); public static Vector3 One => new Vector3(1.0f, 1.0f, 1.0f);

71
AssetStudio/Math/XForm.cs Normal file
View File

@@ -0,0 +1,71 @@
using System;
using System.Runtime.InteropServices;
namespace AssetStudio
{
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct XForm : IEquatable<XForm>
{
public Vector3 t;
public Quaternion q;
public Vector3 s;
public XForm(Vector3 t, Quaternion q, Vector3 s)
{
this.t = t;
this.q = q;
this.s = s;
}
public float this[int index]
{
get
{
switch (index)
{
case 0: return t.X;
case 1: return t.Y;
case 2: return t.Z;
case 3: return q.X;
case 4: return q.Y;
case 5: return q.Z;
case 6: return q.W;
case 7: return s.X;
case 8: return s.Y;
case 9: return s.Z;
default: throw new ArgumentOutOfRangeException(nameof(index), "Invalid xform index!");
}
}
set
{
switch (index)
{
case 0: t.X = value; break;
case 1: t.Y = value; break;
case 2: t.Z = value; break;
case 3: q.X = value; break;
case 4: q.Y = value; break;
case 5: q.Z = value; break;
case 6: q.W = value; break;
case 7: s.X = value; break;
case 8: s.Y = value; break;
case 9: s.Z = value; break;
default:
throw new ArgumentOutOfRangeException(nameof(index), "Invalid xform index!");
}
}
}
public override int GetHashCode()
{
return t.GetHashCode() ^ (q.GetHashCode() << 2) ^ (s.GetHashCode() >> 2);
}
bool IEquatable<XForm>.Equals(XForm other)
{
return t.Equals(other.t) && q.Equals(other.q) && s.Equals(other.s);
}
public static XForm Zero => new XForm(Vector3.Zero, Quaternion.Zero, Vector3.One);
}
}

View File

@@ -0,0 +1,231 @@
using System;
using System.IO;
using System.Text;
namespace AssetStudio
{
internal class Emitter
{
public Emitter(TextWriter writer, bool formatKeys)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
m_stream = writer;
IsFormatKeys = formatKeys;
if (formatKeys)
{
m_sb = new StringBuilder();
}
}
public Emitter IncreaseIndent()
{
m_indent++;
return this;
}
public Emitter DecreaseIndent()
{
if (m_indent == 0)
{
throw new Exception($"Increase/decrease indent mismatch");
}
m_indent--;
return this;
}
public Emitter Write(char value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter WriteRaw(char value)
{
m_stream.Write(value);
return this;
}
public Emitter Write(byte value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(ushort value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(short value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(uint value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(int value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(ulong value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(long value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(float value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(double value)
{
WriteDelayed();
m_stream.Write(value);
return this;
}
public Emitter Write(string value)
{
if (value.Length > 0)
{
WriteDelayed();
m_stream.Write(value);
}
return this;
}
public Emitter WriteFormat(string value)
{
if (value.Length > 0)
{
WriteDelayed();
if (value.Length > 2 && value.StartsWith("m_", StringComparison.Ordinal))
{
m_sb.Append(value, 2, value.Length - 2);
if (char.IsUpper(m_sb[0]))
{
m_sb[0] = char.ToLower(m_sb[0]);
}
value = m_sb.ToString();
m_sb.Clear();
}
m_stream.Write(value);
}
return this;
}
public Emitter WriteRaw(string value)
{
m_stream.Write(value);
return this;
}
public Emitter WriteClose(char @char)
{
m_isNeedSeparator = false;
m_isNeedWhitespace = false;
m_isNeedLineBreak = false;
return Write(@char);
}
public Emitter WriteClose(string @string)
{
m_isNeedSeparator = false;
m_isNeedWhitespace = false;
return Write(@string);
}
public Emitter WriteWhitespace()
{
m_isNeedWhitespace = true;
return this;
}
public Emitter WriteSeparator()
{
m_isNeedSeparator = true;
return this;
}
public Emitter WriteLine()
{
m_isNeedLineBreak = true;
return this;
}
public void WriteMeta(MetaType type, string value)
{
Write('%').Write(type.ToString()).WriteWhitespace();
Write(value).WriteLine();
}
public void WriteDelayed()
{
if (m_isNeedLineBreak)
{
m_stream.Write('\n');
m_isNeedSeparator = false;
m_isNeedWhitespace = false;
m_isNeedLineBreak = false;
WriteIndent();
}
if (m_isNeedSeparator)
{
m_stream.Write(',');
m_isNeedSeparator = false;
}
if (m_isNeedWhitespace)
{
m_stream.Write(' ');
m_isNeedWhitespace = false;
}
}
private void WriteIndent()
{
for (int i = 0; i < m_indent * 2; i++)
{
m_stream.Write(' ');
}
}
public bool IsFormatKeys { get; }
public bool IsKey { get; set; }
private readonly TextWriter m_stream;
private readonly StringBuilder m_sb;
private int m_indent = 0;
private bool m_isNeedWhitespace = false;
private bool m_isNeedSeparator = false;
private bool m_isNeedLineBreak = false;
}
}

View File

@@ -0,0 +1,7 @@
namespace AssetStudio
{
public interface IYAMLExportable
{
YAMLNode ExportYAML(int[] version);
}
}

View File

@@ -0,0 +1,18 @@
namespace AssetStudio
{
/// <summary>
/// Specifies the style of a mapping.
/// </summary>
public enum MappingStyle
{
/// <summary>
/// The block mapping style.
/// </summary>
Block,
/// <summary>
/// The flow mapping style.
/// </summary>
Flow
}
}

View File

@@ -0,0 +1,8 @@
namespace AssetStudio
{
internal enum MetaType
{
YAML,
TAG,
}
}

View File

@@ -0,0 +1,28 @@
namespace AssetStudio
{
/// <summary>
/// Specifies the style of a YAML scalar.
/// </summary>
public enum ScalarStyle
{
/// <summary>
/// The plain scalar style.
/// </summary>
Plain,
/// <summary>
///
/// </summary>
Hex,
/// <summary>
/// The single-quoted scalar style.
/// </summary>
SingleQuoted,
/// <summary>
/// The double-quoted scalar style.
/// </summary>
DoubleQuoted,
}
}

View File

@@ -0,0 +1,17 @@
namespace AssetStudio
{
internal enum ScalarType
{
Boolean,
Byte,
UInt16,
Int16,
UInt32,
Int32,
UInt64,
Int64,
Single,
Double,
String,
}
}

View File

@@ -0,0 +1,51 @@
namespace AssetStudio
{
/// <summary>
/// Specifies the style of a sequence.
/// </summary>
public enum SequenceStyle
{
/// <summary>
/// The block sequence style
/// </summary>
Block,
/// <summary>
/// The block sequence style but with curly braces
/// </summary>
BlockCurve,
/// <summary>
/// The flow sequence style
/// </summary>
Flow,
/// <summary>
/// Single line with hex data
/// </summary>
Raw,
}
public static class SequenceStyleExtensions
{
public static bool IsRaw(this SequenceStyle _this)
{
return _this == SequenceStyle.Raw;
}
public static bool IsAnyBlock(this SequenceStyle _this)
{
return _this == SequenceStyle.Block || _this == SequenceStyle.BlockCurve;
}
/// <summary>
/// Get scalar style corresponding to current sequence style
/// </summary>
/// <param name="_this">Sequence style</param>
/// <returns>Corresponding scalar style</returns>
public static ScalarStyle ToScalarStyle(this SequenceStyle _this)
{
return _this == SequenceStyle.Raw ? ScalarStyle.Hex : ScalarStyle.Plain;
}
}
}

View File

@@ -0,0 +1,42 @@
namespace AssetStudio
{
public sealed class YAMLDocument
{
public YAMLDocument()
{
}
public YAMLScalarNode CreateScalarRoot()
{
YAMLScalarNode root = new YAMLScalarNode();
Root = root;
return root;
}
public YAMLSequenceNode CreateSequenceRoot()
{
YAMLSequenceNode root = new YAMLSequenceNode();
Root = root;
return root;
}
public YAMLMappingNode CreateMappingRoot()
{
YAMLMappingNode root = new YAMLMappingNode();
Root = root;
return root;
}
internal void Emit(Emitter emitter, bool isSeparator)
{
if(isSeparator)
{
emitter.Write("---").WriteWhitespace();
}
Root.Emit(emitter);
}
public YAMLNode Root { get; private set; }
}
}

View File

@@ -0,0 +1,330 @@
using System;
using System.Collections.Generic;
namespace AssetStudio
{
public sealed class YAMLMappingNode : YAMLNode
{
public YAMLMappingNode()
{
}
public YAMLMappingNode(MappingStyle style)
{
Style = style;
}
public void Add(int key, long value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(int key, string value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(int key, YAMLNode value)
{
YAMLScalarNode keyNode = new YAMLScalarNode(key);
InsertEnd(keyNode, value);
}
public void Add(uint key, string value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(uint key, YAMLNode value)
{
YAMLScalarNode keyNode = new YAMLScalarNode(key);
InsertEnd(keyNode, value);
}
public void Add(long key, string value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(long key, YAMLNode value)
{
YAMLScalarNode keyNode = new YAMLScalarNode(key);
InsertEnd(keyNode, value);
}
public void Add(string key, bool value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, byte value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, short value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, ushort value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, int value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, uint value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, long value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, ulong value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, float value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, string value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(string key, YAMLNode value)
{
YAMLScalarNode keyNode = new YAMLScalarNode(key, true);
InsertEnd(keyNode, value);
}
public void Add(YAMLNode key, bool value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, byte value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, short value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, ushort value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, int value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, uint value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, long value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, ulong value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, float value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, string value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
Add(key, valueNode);
}
public void Add(YAMLNode key, YAMLNode value)
{
if (key.NodeType != YAMLNodeType.Scalar)
{
throw new Exception($"Only {YAMLNodeType.Scalar} node as a key supported");
}
InsertEnd(key, value);
}
public void Append(YAMLMappingNode map)
{
foreach (KeyValuePair<YAMLNode, YAMLNode> child in map.m_children)
{
Add(child.Key, child.Value);
}
}
public void InsertBegin(string key, int value)
{
YAMLScalarNode valueNode = new YAMLScalarNode(value);
InsertBegin(key, valueNode);
}
public void InsertBegin(string key, YAMLNode value)
{
YAMLScalarNode keyNode = new YAMLScalarNode(key, true);
InsertBegin(keyNode, value);
}
public void InsertBegin(YAMLNode key, YAMLNode value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
KeyValuePair<YAMLNode, YAMLNode> pair = new KeyValuePair<YAMLNode, YAMLNode>(key, value);
m_children.Insert(0, pair);
}
internal override void Emit(Emitter emitter)
{
base.Emit(emitter);
StartChildren(emitter);
foreach (var kvp in m_children)
{
YAMLNode key = kvp.Key;
YAMLNode value = kvp.Value;
bool iskey = emitter.IsKey;
emitter.IsKey = true;
key.Emit(emitter);
emitter.IsKey = false;
StartTransition(emitter, value);
value.Emit(emitter);
EndTransition(emitter, value);
emitter.IsKey = iskey;
}
EndChildren(emitter);
}
private void StartChildren(Emitter emitter)
{
if (Style == MappingStyle.Block)
{
if (m_children.Count == 0)
{
emitter.Write('{');
}
}
else if (Style == MappingStyle.Flow)
{
emitter.Write('{');
}
}
private void EndChildren(Emitter emitter)
{
if (Style == MappingStyle.Block)
{
if (m_children.Count == 0)
{
emitter.Write('}');
}
emitter.WriteLine();
}
else if (Style == MappingStyle.Flow)
{
emitter.WriteClose('}');
}
}
private void StartTransition(Emitter emitter, YAMLNode next)
{
emitter.Write(':').WriteWhitespace();
if (Style == MappingStyle.Block)
{
if (next.IsMultiline)
{
emitter.WriteLine();
}
}
if (next.IsIndent)
{
emitter.IncreaseIndent();
}
}
private void EndTransition(Emitter emitter, YAMLNode next)
{
if (Style == MappingStyle.Block)
{
emitter.WriteLine();
}
else if (Style == MappingStyle.Flow)
{
emitter.WriteSeparator().WriteWhitespace();
}
if (next.IsIndent)
{
emitter.DecreaseIndent();
}
}
private void InsertEnd(YAMLNode key, YAMLNode value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
KeyValuePair<YAMLNode, YAMLNode> pair = new KeyValuePair<YAMLNode, YAMLNode>(key, value);
m_children.Add(pair);
}
public static YAMLMappingNode Empty { get; } = new YAMLMappingNode(MappingStyle.Flow);
public override YAMLNodeType NodeType => YAMLNodeType.Mapping;
public override bool IsMultiline => Style == MappingStyle.Block && m_children.Count > 0;
public override bool IsIndent => Style == MappingStyle.Block;
public MappingStyle Style { get; set; }
private readonly List<KeyValuePair<YAMLNode, YAMLNode>> m_children = new List<KeyValuePair<YAMLNode, YAMLNode>>();
}
}

View File

@@ -0,0 +1,40 @@
namespace AssetStudio
{
public abstract class YAMLNode
{
internal virtual void Emit(Emitter emitter)
{
bool isWrote = false;
if (!CustomTag.IsEmpty)
{
emitter.Write(CustomTag.ToString()).WriteWhitespace();
isWrote = true;
}
if (Anchor.Length > 0)
{
emitter.Write("&").Write(Anchor).WriteWhitespace();
isWrote = true;
}
if (isWrote)
{
if (IsMultiline)
{
emitter.WriteLine();
}
}
}
public abstract YAMLNodeType NodeType { get; }
public abstract bool IsMultiline { get; }
public abstract bool IsIndent { get; }
public string Tag
{
get => CustomTag.Content;
set => CustomTag = new YAMLTag(YAMLWriter.DefaultTagHandle, value);
}
public YAMLTag CustomTag { get; set; }
public string Anchor { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,20 @@
namespace AssetStudio
{
public enum YAMLNodeType
{
/// <summary>
/// The node is a <see cref="YamlMappingNode"/>.
/// </summary>
Mapping,
/// <summary>
/// The node is a <see cref="YamlScalarNode"/>.
/// </summary>
Scalar,
/// <summary>
/// The node is a <see cref="YamlSequenceNode"/>.
/// </summary>
Sequence
}
}

View File

@@ -0,0 +1,456 @@
//#define USE_HEX_FLOAT
using System;
using System.Globalization;
using System.Text.RegularExpressions;
namespace AssetStudio
{
public sealed class YAMLScalarNode : YAMLNode
{
public YAMLScalarNode()
{
}
public YAMLScalarNode(bool value) :
this(value, false)
{
}
public YAMLScalarNode(bool value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(byte value) :
this(value, false)
{
}
public YAMLScalarNode(byte value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(short value) :
this(value, false)
{
}
public YAMLScalarNode(short value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(ushort value) :
this(value, false)
{
}
public YAMLScalarNode(ushort value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(int value) :
this(value, false)
{
}
public YAMLScalarNode(int value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(uint value) :
this(value, false)
{
}
public YAMLScalarNode(uint value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(long value) :
this(value, false)
{
}
public YAMLScalarNode(long value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(ulong value) :
this(value, false)
{
}
public YAMLScalarNode(ulong value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(float value) :
this(value, false)
{
}
public YAMLScalarNode(float value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(double value) :
this(value, false)
{
}
public YAMLScalarNode(double value, bool isHex)
{
SetValue(value);
Style = isHex ? ScalarStyle.Hex : ScalarStyle.Plain;
}
public YAMLScalarNode(string value)
{
SetValue(value);
Style = GetStringStyle(value);
}
internal YAMLScalarNode(string value, bool _)
{
SetValue(value);
Style = ScalarStyle.Plain;
}
public void SetValue(bool value)
{
m_value = value ? 1u : 0u;
m_objectType = ScalarType.Boolean;
}
public void SetValue(byte value)
{
m_value = value;
m_objectType = ScalarType.Byte;
}
public void SetValue(short value)
{
m_value = unchecked((ushort)value);
m_objectType = ScalarType.Int16;
}
public void SetValue(ushort value)
{
m_value = value;
m_objectType = ScalarType.UInt16;
}
public void SetValue(int value)
{
m_value = unchecked((uint)value);
m_objectType = ScalarType.Int32;
}
public void SetValue(uint value)
{
m_value = value;
m_objectType = ScalarType.UInt32;
}
public void SetValue(long value)
{
m_value = unchecked((ulong)value);
m_objectType = ScalarType.Int64;
}
public void SetValue(ulong value)
{
m_value = value;
m_objectType = ScalarType.UInt64;
}
public void SetValue(float value)
{
#if USE_HEX_FLOAT
// It is more precise technic but output looks vague and less readable
uint hex = BitConverterExtensions.ToUInt32(value);
m_string = $"0x{hex.ToHexString()}({value.ToString(CultureInfo.InvariantCulture)})";
m_objectType = ScalarType.String;
#else
m_value = BitConverterExtensions.ToUInt32(value);
m_objectType = ScalarType.Single;
#endif
}
public void SetValue(double value)
{
#if USE_HEX_FLOAT
// It is more precise technic but output looks vague and less readable
ulong hex = BitConverterExtensions.ToUInt64(value);
m_string = $"0x{hex.ToHexString()}({value.ToString(CultureInfo.InvariantCulture)})";
m_objectType = ScalarType.String;
#else
m_value = BitConverterExtensions.ToUInt64(value);
m_objectType = ScalarType.Double;
#endif
}
public void SetValue(string value)
{
m_string = value;
m_objectType = ScalarType.String;
}
internal Emitter ToString(Emitter emitter)
{
if (Style == ScalarStyle.Hex)
{
switch (m_objectType)
{
case ScalarType.Byte:
return emitter.WriteHex((byte)m_value);
case ScalarType.Int16:
return emitter.WriteHex(unchecked((short)m_value));
case ScalarType.UInt16:
return emitter.WriteHex((ushort)m_value);
case ScalarType.Int32:
return emitter.WriteHex(unchecked((int)m_value));
case ScalarType.UInt32:
return emitter.WriteHex((uint)m_value);
case ScalarType.Int64:
return emitter.WriteHex(unchecked((long)m_value));
case ScalarType.UInt64:
return emitter.WriteHex(m_value);
case ScalarType.Single:
return emitter.WriteHex((uint)m_value);
case ScalarType.Double:
return emitter.WriteHex(m_value);
default:
throw new NotImplementedException(m_objectType.ToString());
}
}
switch (m_objectType)
{
case ScalarType.Boolean:
return emitter.Write(m_value);
case ScalarType.Byte:
return emitter.Write(m_value);
case ScalarType.Int16:
return emitter.Write(unchecked((short)m_value));
case ScalarType.UInt16:
return emitter.Write(m_value);
case ScalarType.Int32:
return emitter.Write(unchecked((int)m_value));
case ScalarType.UInt32:
return emitter.Write(m_value);
case ScalarType.Int64:
return emitter.Write(unchecked((long)m_value));
case ScalarType.UInt64:
return emitter.Write(m_value);
case ScalarType.Single:
return emitter.Write(BitConverterExtensions.ToSingle((uint)m_value));
case ScalarType.Double:
return emitter.Write(BitConverterExtensions.ToDouble(m_value));
case ScalarType.String:
return WriteString(emitter);
default:
throw new NotImplementedException(m_objectType.ToString());
}
}
internal override void Emit(Emitter emitter)
{
base.Emit(emitter);
switch (Style)
{
case ScalarStyle.Hex:
case ScalarStyle.Plain:
ToString(emitter);
break;
case ScalarStyle.SingleQuoted:
emitter.Write('\'');
ToString(emitter);
emitter.Write('\'');
break;
case ScalarStyle.DoubleQuoted:
emitter.Write('"');
ToString(emitter);
emitter.Write('"');
break;
default:
throw new Exception($"Unsupported scalar style {Style}");
}
}
private Emitter WriteString(Emitter emitter)
{
if (Style == ScalarStyle.Plain)
{
if (emitter.IsFormatKeys && emitter.IsKey)
{
emitter.WriteFormat(m_string);
}
else
{
emitter.Write(m_string);
}
}
else if (Style == ScalarStyle.SingleQuoted)
{
emitter.WriteDelayed();
for (int i = 0; i < m_string.Length; i++)
{
char c = m_string[i];
emitter.WriteRaw(c);
if (c == '\'')
{
emitter.WriteRaw(c);
}
else if (c == '\n')
{
emitter.WriteRaw("\n ");
}
}
}
else if (Style == ScalarStyle.DoubleQuoted)
{
emitter.WriteDelayed();
for (int i = 0; i < m_string.Length; i++)
{
char c = m_string[i];
switch (c)
{
case '\\':
emitter.WriteRaw('\\').WriteRaw('\\');
break;
case '\n':
emitter.WriteRaw('\\').WriteRaw('n');
break;
case '\r':
emitter.WriteRaw('\\').WriteRaw('r');
break;
case '\t':
emitter.WriteRaw('\\').WriteRaw('t');
break;
case '"':
emitter.WriteRaw('\\').WriteRaw('"');
break;
default:
emitter.WriteRaw(c);
break;
}
}
}
else
{
throw new NotSupportedException(Style.ToString());
}
return emitter;
}
private static ScalarStyle GetStringStyle(string value)
{
if (s_illegal.IsMatch(value))
{
return value.Contains("\n ") ? ScalarStyle.DoubleQuoted : ScalarStyle.SingleQuoted;
}
return ScalarStyle.Plain;
}
public static YAMLScalarNode Empty { get; } = new YAMLScalarNode();
public override YAMLNodeType NodeType => YAMLNodeType.Scalar;
public override bool IsMultiline => false;
public override bool IsIndent => false;
public string Value
{
get
{
if (Style == ScalarStyle.Hex)
{
switch (m_objectType)
{
case ScalarType.Byte:
return unchecked((byte)m_value).ToHexString();
case ScalarType.Int16:
return unchecked((short)m_value).ToHexString();
case ScalarType.UInt16:
return unchecked((ushort)m_value).ToHexString();
case ScalarType.Int32:
return unchecked((int)m_value).ToHexString();
case ScalarType.UInt32:
return unchecked((uint)m_value).ToHexString();
case ScalarType.Int64:
return unchecked((long)m_value).ToHexString();
case ScalarType.UInt64:
return m_value.ToHexString();
case ScalarType.Single:
return BitConverterExtensions.ToSingle((uint)m_value).ToHexString();
case ScalarType.Double:
return BitConverterExtensions.ToDouble(m_value).ToHexString();
default:
throw new NotImplementedException(m_objectType.ToString());
}
}
switch (m_objectType)
{
case ScalarType.Boolean:
return m_value == 1 ? "true" : "false";
case ScalarType.Byte:
return m_value.ToString();
case ScalarType.Int16:
return unchecked((short)m_value).ToString();
case ScalarType.UInt16:
return m_value.ToString();
case ScalarType.Int32:
return unchecked((int)m_value).ToString();
case ScalarType.UInt32:
return m_value.ToString();
case ScalarType.Int64:
return unchecked((long)m_value).ToString();
case ScalarType.UInt64:
return m_value.ToString();
case ScalarType.Single:
return BitConverterExtensions.ToSingle((uint)m_value).ToString(CultureInfo.InvariantCulture);
case ScalarType.Double:
return BitConverterExtensions.ToDouble(m_value).ToString(CultureInfo.InvariantCulture);
case ScalarType.String:
return m_string;
default:
throw new NotImplementedException(m_objectType.ToString());
}
}
set => m_string = value;
}
public ScalarStyle Style { get; }
private static readonly Regex s_illegal = new Regex("(^\\s)|(^-\\s)|(^-$)|(^[\\:\\[\\]'\"*&!@#%{}?<>,\\`])|([:@]\\s)|([\\n\\r])|([:\\s]$)", RegexOptions.Compiled);
private ScalarType m_objectType = ScalarType.String;
private string m_string = string.Empty;
private ulong m_value = 0;
}
}

View File

@@ -0,0 +1,213 @@
using System.Collections.Generic;
namespace AssetStudio
{
public sealed class YAMLSequenceNode : YAMLNode
{
public YAMLSequenceNode()
{
}
public YAMLSequenceNode(SequenceStyle style)
{
Style = style;
}
public void Add(bool value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(byte value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(short value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(ushort value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(int value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(uint value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(long value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(ulong value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(float value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(double value)
{
YAMLScalarNode node = new YAMLScalarNode(value, Style.IsRaw());
Add(node);
}
public void Add(string value)
{
YAMLScalarNode node = new YAMLScalarNode(value);
Add(node);
}
public void Add(YAMLNode child)
{
m_children.Add(child);
}
internal override void Emit(Emitter emitter)
{
base.Emit(emitter);
StartChildren(emitter);
foreach (YAMLNode child in m_children)
{
StartChild(emitter, child);
child.Emit(emitter);
EndChild(emitter, child);
}
EndChildren(emitter);
}
private void StartChildren(Emitter emitter)
{
switch (Style)
{
case SequenceStyle.Block:
if (m_children.Count == 0)
{
emitter.Write('[');
}
break;
case SequenceStyle.BlockCurve:
if (m_children.Count == 0)
{
emitter.Write('{');
}
break;
case SequenceStyle.Flow:
emitter.Write('[');
break;
case SequenceStyle.Raw:
if (m_children.Count == 0)
{
emitter.Write('[');
}
break;
}
}
private void EndChildren(Emitter emitter)
{
switch (Style)
{
case SequenceStyle.Block:
if (m_children.Count == 0)
{
emitter.Write(']');
}
emitter.WriteLine();
break;
case SequenceStyle.BlockCurve:
if (m_children.Count == 0)
{
emitter.WriteClose('}');
}
emitter.WriteLine();
break;
case SequenceStyle.Flow:
emitter.WriteClose(']');
break;
case SequenceStyle.Raw:
if (m_children.Count == 0)
{
emitter.Write(']');
}
emitter.WriteLine();
break;
}
}
private void StartChild(Emitter emitter, YAMLNode next)
{
if (Style.IsAnyBlock())
{
emitter.Write('-').Write(' ');
if (next.NodeType == NodeType)
{
emitter.IncreaseIndent();
}
}
if (next.IsIndent)
{
emitter.IncreaseIndent();
}
}
private void EndChild(Emitter emitter, YAMLNode next)
{
if (Style.IsAnyBlock())
{
emitter.WriteLine();
if (next.NodeType == NodeType)
{
emitter.DecreaseIndent();
}
}
else if (Style == SequenceStyle.Flow)
{
emitter.WriteSeparator().WriteWhitespace();
}
if (next.IsIndent)
{
emitter.DecreaseIndent();
}
}
public static YAMLSequenceNode Empty { get; } = new YAMLSequenceNode();
public override YAMLNodeType NodeType => YAMLNodeType.Sequence;
public override bool IsMultiline => Style.IsAnyBlock() && m_children.Count > 0;
public override bool IsIndent => false;
public SequenceStyle Style { get; }
private readonly List<YAMLNode> m_children = new List<YAMLNode>();
}
}

View File

@@ -0,0 +1,26 @@
namespace AssetStudio
{
public readonly struct YAMLTag
{
public YAMLTag(string handle, string content)
{
Handle = handle;
Content = content;
}
public override string ToString()
{
return IsEmpty ? string.Empty : $"{Handle}{Content}";
}
public string ToHeaderString()
{
return IsEmpty ? string.Empty : $"{Handle} {Content}";
}
public bool IsEmpty => string.IsNullOrEmpty(Handle);
public string Handle { get; }
public string Content { get; }
}
}

View File

@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace AssetStudio
{
using Version = System.Version;
public class YAMLWriter
{
public void AddDocument(YAMLDocument document)
{
#if DEBUG
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
if (m_documents.Contains(document))
{
throw new ArgumentException($"Document {document} is added already", nameof(document));
}
#endif
m_documents.Add(document);
}
public void AddTag(string handle, string content)
{
if(m_tags.Any(t => t.Handle == handle))
{
throw new Exception($"Writer already contains tag {handle}");
}
YAMLTag tag = new YAMLTag(handle, content);
m_tags.Add(tag);
}
public void Write(TextWriter output)
{
WriteHead(output);
foreach (YAMLDocument doc in m_documents)
{
WriteDocument(doc);
}
WriteTail(output);
}
public void WriteHead(TextWriter output)
{
m_emitter = new Emitter(output, IsFormatKeys);
m_isWriteSeparator = false;
if (IsWriteVersion)
{
m_emitter.WriteMeta(MetaType.YAML, Version.ToString());
m_isWriteSeparator = true;
}
if (IsWriteDefaultTag)
{
m_emitter.WriteMeta(MetaType.TAG, DefaultTag.ToHeaderString());
m_isWriteSeparator = true;
}
foreach (YAMLTag tag in m_tags)
{
m_emitter.WriteMeta(MetaType.TAG, tag.ToHeaderString());
m_isWriteSeparator = true;
}
}
public void WriteDocument(YAMLDocument doc)
{
doc.Emit(m_emitter, m_isWriteSeparator);
m_isWriteSeparator = true;
}
public void WriteTail(TextWriter output)
{
output.Write('\n');
}
public static Version Version { get; } = new Version(1, 1);
public const string DefaultTagHandle = "!u!";
public const string DefaultTagContent = "tag:unity3d.com,2011:";
public readonly YAMLTag DefaultTag = new YAMLTag(DefaultTagHandle, DefaultTagContent);
public bool IsWriteVersion { get; set; } = true;
public bool IsWriteDefaultTag { get; set; } = true;
public bool IsFormatKeys { get; set; }
private readonly HashSet<YAMLDocument> m_documents = new HashSet<YAMLDocument>();
private readonly List<YAMLTag> m_tags = new List<YAMLTag>();
private Emitter m_emitter;
private bool m_isWriteSeparator;
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace AssetStudio
{
public static class ArrayYAMLExtensions
{
public static YAMLNode ExportYAML(this byte[] _this)
{
StringBuilder sb = new StringBuilder(_this.Length * 2);
for (int i = 0; i < _this.Length; i++)
{
sb.AppendHex(_this[i]);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML<T>(this T[][] _this, int[] version)
where T : IYAMLExportable
{
return ((IEnumerable<IEnumerable<T>>)_this).ExportYAML(version);
}
}
}

View File

@@ -0,0 +1,107 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace AssetStudio
{
public static class BitConverterExtensions
{
[StructLayout(LayoutKind.Explicit)]
private struct FloatUIntUnion
{
[FieldOffset(0)]
public uint Int;
[FieldOffset(0)]
public float Float;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ToUInt32(float value)
{
return new FloatUIntUnion { Float = value }.Int;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong ToUInt64(double value)
{
return unchecked((ulong)BitConverter.DoubleToInt64Bits(value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ToSingle(uint value)
{
return new FloatUIntUnion { Int = value }.Float;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double ToDouble(ulong value)
{
return BitConverter.Int64BitsToDouble(unchecked((long)value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetBytes(ushort value, byte[] buffer, int offset)
{
buffer[offset + 0] = unchecked((byte)(value >> 0));
buffer[offset + 1] = unchecked((byte)(value >> 8));
}
public static void GetBytes(uint value, byte[] buffer, int offset)
{
buffer[offset + 0] = unchecked((byte)(value >> 0));
buffer[offset + 1] = unchecked((byte)(value >> 8));
buffer[offset + 2] = unchecked((byte)(value >> 16));
buffer[offset + 3] = unchecked((byte)(value >> 24));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short Swap(short value)
{
return unchecked((short)(Swap(unchecked((ushort)value))));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort Swap(ushort value)
{
return unchecked((ushort)(value >> 8 | value << 8));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Swap(int value)
{
return unchecked((int)(Swap(unchecked((uint)value))));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Swap(uint value)
{
value = value >> 16 | value << 16;
return ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long Swap(long value)
{
return unchecked((long)(Swap(unchecked((ulong)value))));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong Swap(ulong value)
{
value = value >> 32 | value << 32;
value = ((value & 0xFFFF0000FFFF0000) >> 16) | ((value & 0x0000FFFF0000FFFF) << 16);
return ((value & 0xFF00FF00FF00FF00) >> 8) | ((value & 0x00FF00FF00FF00FF) << 8);
}
public static int GetDigitsCount(uint value)
{
int count = 0;
while (value != 0)
{
value /= 10;
count++;
}
return count;
}
}
}

View File

@@ -0,0 +1,82 @@
namespace AssetStudio
{
internal static class EmitterExtensions
{
public static Emitter WriteHex(this Emitter _this, byte value)
{
_this.Write(HexAlphabet[value >> 4]);
_this.Write(HexAlphabet[value & 0xF]);
return _this;
}
public static Emitter WriteHex(this Emitter _this, ushort value)
{
_this.Write(HexAlphabet[(value >> 4) & 0xF]);
_this.Write(HexAlphabet[(value >> 0) & 0xF]);
_this.Write(HexAlphabet[(value >> 12) & 0xF]);
_this.Write(HexAlphabet[(value >> 8) & 0xF]);
return _this;
}
public static Emitter WriteHex(this Emitter _this, short value)
{
return WriteHex(_this, unchecked((ushort)value));
}
public static Emitter WriteHex(this Emitter _this, uint value)
{
_this.Write(HexAlphabet[unchecked((int)(value >> 4) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 0) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 12) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 8) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 20) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 16) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 28) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 24) & 0xF)]);
return _this;
}
public static Emitter WriteHex(this Emitter _this, int value)
{
return WriteHex(_this, unchecked((uint)value));
}
public static Emitter WriteHex(this Emitter _this, ulong value)
{
_this.Write(HexAlphabet[unchecked((int)(value >> 4) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 0) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 12) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 8) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 20) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 16) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 28) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 24) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 36) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 32) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 44) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 40) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 52) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 48) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 60) & 0xF)]);
_this.Write(HexAlphabet[unchecked((int)(value >> 56) & 0xF)]);
return _this;
}
public static Emitter WriteHex(this Emitter _this, long value)
{
return WriteHex(_this, unchecked((ulong)value));
}
public static Emitter WriteHex(this Emitter _this, float value)
{
return WriteHex(_this, BitConverterExtensions.ToUInt32(value));
}
public static Emitter WriteHex(this Emitter _this, double value)
{
return WriteHex(_this, BitConverterExtensions.ToUInt64(value));
}
private static readonly string HexAlphabet = "0123456789ABCDEF";
}
}

View File

@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
namespace AssetStudio
{
public static class IDictionaryExportYAMLExtensions
{
public static YAMLNode ExportYAML<T>(this IReadOnlyDictionary<int, T> _this, int[] version)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(kvp.Key, kvp.Value.ExportYAML(version));
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T>(this IReadOnlyDictionary<string, T> _this, int[] version)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(kvp.Key, kvp.Value.ExportYAML(version));
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T1, T2>(this IReadOnlyDictionary<Tuple<T1, long>, T2> _this, int[] version)
where T1 : IYAMLExportable
where T2 : IYAMLExportable
{
// TODO: test
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode kvpMap = new YAMLMappingNode();
YAMLMappingNode keyMap = new YAMLMappingNode();
keyMap.Add("first", kvp.Key.Item1.ExportYAML(version));
keyMap.Add("second", kvp.Key.Item2);
kvpMap.Add("first", keyMap);
kvpMap.Add("second", kvp.Value.ExportYAML(version));
node.Add(kvpMap);
}
return node;
}
public static YAMLNode ExportYAML<T>(this IReadOnlyDictionary<T, int> _this, int[] version)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
YAMLNode key = kvp.Key.ExportYAML(version);
if (key.NodeType == YAMLNodeType.Scalar)
{
map.Add(key, kvp.Value);
}
else
{
map.Add("first", key);
map.Add("second", kvp.Value);
}
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T>(this IReadOnlyDictionary<T, float> _this, int[] version)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
YAMLNode key = kvp.Key.ExportYAML(version);
if (key.NodeType == YAMLNodeType.Scalar)
{
map.Add(key, kvp.Value);
}
else
{
map.Add("first", key);
map.Add("second", kvp.Value);
}
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T1, T2>(this IReadOnlyDictionary<T1, T2> _this, int[] version)
where T1 : IYAMLExportable
where T2 : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
YAMLNode key = kvp.Key.ExportYAML(version);
if (key.NodeType == YAMLNodeType.Scalar)
{
map.Add(key, kvp.Value.ExportYAML(version));
}
else
{
map.Add("first", key);
map.Add("second", kvp.Value.ExportYAML(version));
}
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T1, T2>(this IReadOnlyDictionary<T1, T2[]> _this, int[] version)
where T1 : IYAMLExportable
where T2 : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
YAMLNode key = kvp.Key.ExportYAML(version);
if (key.NodeType == YAMLNodeType.Scalar)
{
map.Add(key, kvp.Value.ExportYAML(version));
}
else
{
map.Add("first", key);
map.Add("second", kvp.Value.ExportYAML(version));
}
node.Add(map);
}
return node;
}
}
}

View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
namespace AssetStudio
{
public static class IDictionaryYAMLExtensions
{
public static YAMLNode ExportYAML(this IReadOnlyDictionary<uint, string> _this)
{
YAMLMappingNode node = new YAMLMappingNode();
foreach (var kvp in _this)
{
node.Add(kvp.Key, kvp.Value);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<long, string> _this)
{
YAMLMappingNode node = new YAMLMappingNode();
foreach (var kvp in _this)
{
node.Add(kvp.Key, kvp.Value);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<string, string> _this)
{
YAMLMappingNode node = new YAMLMappingNode();
foreach (var kvp in _this)
{
node.Add(kvp.Key, kvp.Value);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<string, int> _this)
{
YAMLMappingNode node = new YAMLMappingNode();
foreach (var kvp in _this)
{
node.Add(kvp.Key, kvp.Value);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<string, float> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(kvp.Key, kvp.Value);
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<Tuple<ushort, ushort>, float> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode keyNode = new YAMLMappingNode();
keyNode.Add(kvp.Key.Item1, kvp.Key.Item2);
YAMLMappingNode kvpMap = new YAMLMappingNode();
kvpMap.Add("first", keyNode);
kvpMap.Add("second", kvp.Value);
node.Add(kvpMap);
}
return node;
}
public static YAMLNode ExportYAML(this IReadOnlyDictionary<Tuple<int, long>, string> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode keyNode = new YAMLMappingNode();
keyNode.Add(kvp.Key.Item1, kvp.Key.Item2);
YAMLMappingNode kvpMap = new YAMLMappingNode();
kvpMap.Add("first", keyNode);
kvpMap.Add("second", kvp.Value);
node.Add(kvpMap);
}
return node;
}
public static YAMLNode ExportYAML<T>(this IReadOnlyDictionary<Tuple<T, long>, string> _this, Func<T, int> converter)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.BlockCurve);
foreach (var kvp in _this)
{
YAMLMappingNode keyNode = new YAMLMappingNode();
keyNode.Add(converter(kvp.Key.Item1), kvp.Key.Item2);
YAMLMappingNode kvpMap = new YAMLMappingNode();
kvpMap.Add("first", keyNode);
kvpMap.Add("second", kvp.Value);
node.Add(kvpMap);
}
return node;
}
}
}

View File

@@ -0,0 +1,273 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AssetStudio
{
public static class IEnumerableYAMLExtensions
{
public static YAMLNode ExportYAML(this IEnumerable<bool> _this)
{
StringBuilder sb = new StringBuilder();
foreach (bool value in _this)
{
byte bvalue = unchecked((byte)(value ? 1 : 0));
sb.AppendHex(bvalue);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IEnumerable<char> _this)
{
StringBuilder sb = new StringBuilder();
foreach (char value in _this)
{
sb.AppendHex((ushort)value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IEnumerable<byte> _this)
{
StringBuilder sb = new StringBuilder();
foreach (byte value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IEnumerable<ushort> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (ushort value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (ushort value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<short> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (short value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (short value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<uint> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (uint value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (uint value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<int> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (int value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (int value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<ulong> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (ulong value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (ulong value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<long> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder();
foreach (long value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (long value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IEnumerable<float> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (float value in _this)
{
node.Add(value);
}
return node;
}
public static YAMLNode ExportYAML(this IEnumerable<double> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (double value in _this)
{
node.Add(value);
}
return node;
}
public static YAMLNode ExportYAML(this IEnumerable<string> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (string value in _this)
{
node.Add(value);
}
return node;
}
public static YAMLNode ExportYAML(this IEnumerable<IEnumerable<string>> _this)
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (IEnumerable<string> export in _this)
{
node.Add(export.ExportYAML());
}
return node;
}
public static YAMLNode ExportYAML<T>(this IEnumerable<T> _this, int[] version)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (T export in _this)
{
node.Add(export.ExportYAML(version));
}
return node;
}
public static YAMLNode ExportYAML<T>(this IEnumerable<IEnumerable<T>> _this, int[] version)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (IEnumerable<T> export in _this)
{
node.Add(export.ExportYAML(version));
}
return node;
}
public static YAMLNode ExportYAML<T>(this IEnumerable<Tuple<string, T>> _this, int[] version)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode();
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(kvp.Item1, kvp.Item2.ExportYAML(version));
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T1, T2>(this IEnumerable<Tuple<T1, T2>> _this, Func<T1, int> converter, int[] version)
where T2 : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode();
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(converter(kvp.Item1), kvp.Item2.ExportYAML(version));
node.Add(map);
}
return node;
}
public static YAMLNode ExportYAML<T>(this IEnumerable<KeyValuePair<string, T>> _this, int[] version)
where T : IYAMLExportable
{
YAMLSequenceNode node = new YAMLSequenceNode();
foreach (var kvp in _this)
{
YAMLMappingNode map = new YAMLMappingNode();
map.Add(kvp.Key, kvp.Value.ExportYAML(version));
node.Add(map);
}
return node;
}
}
}

View File

@@ -0,0 +1,171 @@
using System.Collections.Generic;
using System.Text;
namespace AssetStudio
{
public static class IListYAMLExtensions
{
public static YAMLNode ExportYAML(this IReadOnlyList<bool> _this)
{
StringBuilder sb = new StringBuilder(_this.Count * 2);
foreach (bool value in _this)
{
byte bvalue = unchecked((byte)(value ? 1 : 0));
sb.AppendHex(bvalue);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IReadOnlyList<char> _this)
{
StringBuilder sb = new StringBuilder(_this.Count * 4);
foreach (char value in _this)
{
sb.AppendHex((ushort)value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IReadOnlyList<byte> _this)
{
StringBuilder sb = new StringBuilder(_this.Count * 2);
foreach (byte value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
public static YAMLNode ExportYAML(this IReadOnlyList<ushort> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 4);
foreach (ushort value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (ushort value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IReadOnlyList<short> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 4);
foreach (short value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (short value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IReadOnlyList<uint> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 8);
foreach (uint value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (uint value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IReadOnlyList<int> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 8);
foreach (int value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (int value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IReadOnlyList<ulong> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 16);
foreach (ulong value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (ulong value in _this)
{
node.Add(value);
}
return node;
}
}
public static YAMLNode ExportYAML(this IReadOnlyList<long> _this, bool isRaw)
{
if (isRaw)
{
StringBuilder sb = new StringBuilder(_this.Count * 16);
foreach (long value in _this)
{
sb.AppendHex(value);
}
return new YAMLScalarNode(sb.ToString(), true);
}
else
{
YAMLSequenceNode node = new YAMLSequenceNode(SequenceStyle.Block);
foreach (long value in _this)
{
node.Add(value);
}
return node;
}
}
}
}

View File

@@ -0,0 +1,79 @@
namespace AssetStudio
{
public static class PrimitiveExtensions
{
public static int ParseDigit(this char _this)
{
return _this - '0';
}
public static string ToHexString(this byte _this)
{
return _this.ToString("x2");
}
public static string ToHexString(this short _this)
{
ushort value = unchecked((ushort)_this);
return ToHexString(value);
}
public static string ToHexString(this ushort _this)
{
ushort reverse = unchecked((ushort)(((0xFF00 & _this) >> 8) | ((0x00FF & _this) << 8)));
return reverse.ToString("x4");
}
public static string ToHexString(this int _this)
{
uint value = unchecked((uint)_this);
return ToHexString(value);
}
public static string ToHexString(this uint _this)
{
uint reverse = ((0xFF000000 & _this) >> 24) | ((0x00FF0000 & _this) >> 8) | ((0x0000FF00 & _this) << 8) | ((0x000000FF & _this) << 24);
return reverse.ToString("x8");
}
public static string ToHexString(this long _this)
{
ulong value = unchecked((ulong)_this);
return ToHexString(value);
}
public static string ToHexString(this ulong _this)
{
ulong reverse = (_this & 0x00000000000000FFUL) << 56 | (_this & 0x000000000000FF00UL) << 40 |
(_this & 0x0000000000FF0000UL) << 24 | (_this & 0x00000000FF000000UL) << 8 |
(_this & 0x000000FF00000000UL) >> 8 | (_this & 0x0000FF0000000000UL) >> 24 |
(_this & 0x00FF000000000000UL) >> 40 | (_this & 0xFF00000000000000UL) >> 56;
return reverse.ToString("x16");
}
public static string ToHexString(this float _this)
{
uint value = BitConverterExtensions.ToUInt32(_this);
return ToHexString(value);
}
public static string ToHexString(this double _this)
{
ulong value = BitConverterExtensions.ToUInt64(_this);
return ToHexString(value);
}
public static int ToClosestInt(this long _this)
{
if (_this > int.MaxValue)
{
return int.MaxValue;
}
if (_this < int.MinValue)
{
return int.MinValue;
}
return unchecked((int)_this);
}
}
}

View File

@@ -0,0 +1,87 @@
using System.Text;
namespace AssetStudio
{
public static class StringBuilderExtensions
{
static StringBuilderExtensions()
{
for (int i = 0; i <= byte.MaxValue; i++)
{
ByteHexRepresentations[i] = i.ToString("x2");
}
}
public static StringBuilder AppendHex(this StringBuilder _this, byte value)
{
_this.Append(ByteHexRepresentations[value]);
return _this;
}
public static StringBuilder AppendHex(this StringBuilder _this, ushort value)
{
_this.Append(ByteHexRepresentations[(value >> 0) & 0xFF]);
_this.Append(ByteHexRepresentations[(value >> 8) & 0xFF]);
return _this;
}
public static StringBuilder AppendHex(this StringBuilder _this, short value)
{
return AppendHex(_this, unchecked((ushort)value));
}
public static StringBuilder AppendHex(this StringBuilder _this, uint value)
{
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 0) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 8) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 16) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 24) & 0xFF)]);
return _this;
}
public static StringBuilder AppendHex(this StringBuilder _this, int value)
{
return AppendHex(_this, unchecked((uint)value));
}
public static StringBuilder AppendHex(this StringBuilder _this, ulong value)
{
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 0) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 8) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 16) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 24) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 32) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 40) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 48) & 0xFF)]);
_this.Append(ByteHexRepresentations[unchecked((int)(value >> 56) & 0xFF)]);
return _this;
}
public static StringBuilder AppendHex(this StringBuilder _this, long value)
{
return AppendHex(_this, unchecked((ulong)value));
}
public static StringBuilder AppendHex(this StringBuilder _this, float value)
{
return AppendHex(_this, BitConverterExtensions.ToUInt32(value));
}
public static StringBuilder AppendHex(this StringBuilder _this, double value)
{
return AppendHex(_this, BitConverterExtensions.ToUInt64(value));
}
public static StringBuilder AppendIndent(this StringBuilder _this, int count)
{
for (int i = 0; i < count; i++)
{
_this.Append('\t');
}
return _this;
}
public static readonly string HexAlphabet = "0123456789abcdef";
public static readonly string[] ByteHexRepresentations = new string[256];
}
}

View File

@@ -0,0 +1,31 @@
namespace AssetStudio
{
public static class YAMLMappingNodeExtensions
{
public static void AddSerializedVersion(this YAMLMappingNode _this, int version)
{
if(version > 1)
{
_this.Add(SerializedVersionName, version);
}
}
public static void ForceAddSerializedVersion(this YAMLMappingNode _this, int version)
{
if (version > 0)
{
_this.Add(SerializedVersionName, version);
}
}
public static void InsertSerializedVersion(this YAMLMappingNode _this, int version)
{
if(version > 1)
{
_this.InsertBegin(SerializedVersionName, version);
}
}
public const string SerializedVersionName = "serializedVersion";
}
}

View File

@@ -316,6 +316,16 @@ namespace AssetStudioCLI
return false; return false;
} }
public static bool ExportAnimationClip(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".anim", out var exportFullPath))
return false;
var m_AnimationClip = (AnimationClip)item.Asset;
var str = m_AnimationClip.Convert();
File.WriteAllText(exportFullPath, str);
return true;
}
public static bool ExportAnimator(AssetItem item, string exportPath, List<AssetItem> animationList = null) public static bool ExportAnimator(AssetItem item, string exportPath, List<AssetItem> animationList = null)
{ {
var exportFullPath = Path.Combine(exportPath, item.Text, item.Text + ".fbx"); var exportFullPath = Path.Combine(exportPath, item.Text, item.Text + ".fbx");
@@ -415,7 +425,7 @@ namespace AssetStudioCLI
case ClassIDType.Animator: case ClassIDType.Animator:
return ExportAnimator(item, exportPath); return ExportAnimator(item, exportPath);
case ClassIDType.AnimationClip: case ClassIDType.AnimationClip:
return false; return ExportAnimationClip(item, exportPath);
case ClassIDType.MiHoYoBinData: case ClassIDType.MiHoYoBinData:
return ExportMiHoYoBinData(item, exportPath); return ExportMiHoYoBinData(item, exportPath);
default: default:

View File

@@ -78,6 +78,17 @@
</ContentWithTargetPath> </ContentWithTargetPath>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Libraries\x86\acldb.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>x86\acldb.dll</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Libraries\x64\acldb.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>x64\acldb.dll</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OpenTK" Version="4.8.0" /> <PackageReference Include="OpenTK" Version="4.8.0" />

View File

@@ -835,8 +835,8 @@ namespace AssetStudioGUI
//StatusStripUpdate("Can be exported to FBX file."); //StatusStripUpdate("Can be exported to FBX file.");
PreviewAnimator(m_Animator); PreviewAnimator(m_Animator);
break; break;
case AnimationClip _: case AnimationClip m_AnimationClip:
StatusStripUpdate("Can be exported with Animator or Objects"); PreviewAnimationClip(m_AnimationClip);
break; break;
case MiHoYoBinData m_MiHoYoBinData: case MiHoYoBinData m_MiHoYoBinData:
PreviewText(m_MiHoYoBinData.AsString); PreviewText(m_MiHoYoBinData.AsString);
@@ -1288,6 +1288,12 @@ namespace AssetStudioGUI
var model = new ModelConverter(m_Animator, Properties.Settings.Default.convertType, Studio.Game, false, Array.Empty<AnimationClip>()); var model = new ModelConverter(m_Animator, Properties.Settings.Default.convertType, Studio.Game, false, Array.Empty<AnimationClip>());
PreviewModel(model); PreviewModel(model);
} }
private void PreviewAnimationClip(AnimationClip clip)
{
var str = clip.Convert();
PreviewText(str.Replace("\n", "\r\n"));
}
private void PreviewModel(ModelConverter model) private void PreviewModel(ModelConverter model)
{ {

View File

@@ -315,6 +315,15 @@ namespace AssetStudioGUI
} }
return false; return false;
} }
public static bool ExportAnimationClip(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".anim", out var exportFullPath))
return false;
var m_AnimationClip = (AnimationClip)item.Asset;
var str = m_AnimationClip.Convert();
File.WriteAllText(exportFullPath, str);
return true;
}
public static bool ExportAnimator(AssetItem item, string exportPath, List<AssetItem> animationList = null) public static bool ExportAnimator(AssetItem item, string exportPath, List<AssetItem> animationList = null)
{ {
@@ -427,7 +436,7 @@ namespace AssetStudioGUI
case ClassIDType.Animator: case ClassIDType.Animator:
return ExportAnimator(item, exportPath); return ExportAnimator(item, exportPath);
case ClassIDType.AnimationClip: case ClassIDType.AnimationClip:
return false; return ExportAnimationClip(item, exportPath);
case ClassIDType.MiHoYoBinData: case ClassIDType.MiHoYoBinData:
return ExportMiHoYoBinData(item, exportPath); return ExportMiHoYoBinData(item, exportPath);
default: default:

Binary file not shown.

Binary file not shown.

View File

@@ -74,4 +74,48 @@ namespace ACLLibs
#endregion #endregion
} }
public static class DBACL
{
private const string DLL_NAME = "acldb";
static DBACL()
{
DllLoader.PreloadDll(DLL_NAME);
}
public static void DecompressTracks(byte[] data, byte[] db, out float[] values, out float[] times)
{
var decompressedClip = new DecompressedClip();
var dataPtr = Marshal.AllocHGlobal(data.Length + 8);
var dataAligned = new IntPtr(16 * (((long)dataPtr + 15) / 16));
Marshal.Copy(data, 0, dataPtr, data.Length);
var dbPtr = Marshal.AllocHGlobal(db.Length + 8);
var dbAligned = new IntPtr(16 * (((long)dbPtr + 15) / 16));
Marshal.Copy(db, 0, dbAligned, db.Length);
DecompressTracks(dataAligned, dbAligned, ref decompressedClip);
Marshal.FreeHGlobal(dataPtr);
Marshal.FreeHGlobal(dbPtr);
values = new float[decompressedClip.ValuesCount];
Marshal.Copy(decompressedClip.Values, values, 0, decompressedClip.ValuesCount);
times = new float[decompressedClip.TimesCount];
Marshal.Copy(decompressedClip.Times, times, 0, decompressedClip.TimesCount);
Dispose(ref decompressedClip);
}
#region importfunctions
[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
private static extern void DecompressTracks(nint data, nint db, ref DecompressedClip decompressedClip);
[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
private static extern void Dispose(ref DecompressedClip decompressedClip);
#endregion
}
} }

View File

@@ -4,7 +4,23 @@ namespace AssetStudio
{ {
public static class ACLExtensions public static class ACLExtensions
{ {
public static void Process(this ACLClip m_ACLClip, out float[] values, out float[] times) => ACL.DecompressAll(m_ACLClip.m_ClipData, out values, out times); public static void Process(this ACLClip m_ACLClip, Game game, out float[] values, out float[] times)
public static void ProcessSR(this ACLClip m_ACLClip, out float[] values, out float[] times) => SRACL.DecompressAll(m_ACLClip.m_ClipData, out values, out times); {
if (game.Type.IsSRGroup())
{
SRACL.DecompressAll(m_ACLClip.m_ClipData, out values, out times);
}
else
{
if (!m_ACLClip.m_DatabaseData.IsNullOrEmpty())
{
DBACL.DecompressTracks(m_ACLClip.m_ClipData, m_ACLClip.m_DatabaseData, out values, out times);
}
else
{
ACL.DecompressAll(m_ACLClip.m_ClipData, out values, out times);
}
}
}
} }
} }

View File

@@ -899,7 +899,7 @@ namespace AssetStudio
var aclCount = m_ACLClip.m_CurveCount; var aclCount = m_ACLClip.m_CurveCount;
if (!m_ACLClip.m_ClipData.IsNullOrEmpty() && !Game.Type.IsSRGroup()) if (!m_ACLClip.m_ClipData.IsNullOrEmpty() && !Game.Type.IsSRGroup())
{ {
m_ACLClip.Process(out var values, out var times); m_ACLClip.Process(Game, out var values, out var times);
for (int frameIndex = 0; frameIndex < times.Length; frameIndex++) for (int frameIndex = 0; frameIndex < times.Length; frameIndex++)
{ {
var time = times[frameIndex]; var time = times[frameIndex];
@@ -940,7 +940,7 @@ namespace AssetStudio
} }
if (!m_ACLClip.m_ClipData.IsNullOrEmpty() && Game.Type.IsSRGroup()) if (!m_ACLClip.m_ClipData.IsNullOrEmpty() && Game.Type.IsSRGroup())
{ {
m_ACLClip.ProcessSR(out var values, out var times); m_ACLClip.Process(Game, out var values, out var times);
for (int frameIndex = 0; frameIndex < times.Length; frameIndex++) for (int frameIndex = 0; frameIndex < times.Length; frameIndex++)
{ {
var time = times[frameIndex]; var time = times[frameIndex];

View File

@@ -0,0 +1,538 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using SevenZip;
namespace AssetStudio
{
public class AnimationClipConverter
{
public static readonly Regex UnknownPathRegex = new Regex($@"^{UnknownPathPrefix}[0-9]{{1,10}}$", RegexOptions.Compiled);
private const string UnknownPathPrefix = "path_";
private const string MissedPropertyPrefix = "missed_";
private const string ScriptPropertyPrefix = "script_";
private const string TypeTreePropertyPrefix = "typetree_";
private readonly Game game;
private readonly AnimationClip animationClip;
private readonly CustomCurveResolver m_customCurveResolver;
private readonly Dictionary<Vector3Curve, List<Keyframe<Vector3>>> m_translations = new Dictionary<Vector3Curve, List<Keyframe<Vector3>>>();
private readonly Dictionary<QuaternionCurve, List<Keyframe<Quaternion>>> m_rotations = new Dictionary<QuaternionCurve, List<Keyframe<Quaternion>>>();
private readonly Dictionary<Vector3Curve, List<Keyframe<Vector3>>> m_scales = new Dictionary<Vector3Curve, List<Keyframe<Vector3>>>();
private readonly Dictionary<Vector3Curve, List<Keyframe<Vector3>>> m_eulers = new Dictionary<Vector3Curve, List<Keyframe<Vector3>>>();
private readonly Dictionary<FloatCurve, List<Keyframe<Float>>> m_floats = new Dictionary<FloatCurve, List<Keyframe<Float>>>();
private readonly Dictionary<PPtrCurve, List<PPtrKeyframe>> m_pptrs = new Dictionary<PPtrCurve, List<PPtrKeyframe>>();
public Vector3Curve[] Translations { get; private set; }
public QuaternionCurve[] Rotations { get; private set; }
public Vector3Curve[] Scales { get; private set; }
public Vector3Curve[] Eulers { get; private set; }
public FloatCurve[] Floats { get; private set; }
public PPtrCurve[] PPtrs { get; private set; }
public AnimationClipConverter(AnimationClip clip)
{
game = clip.assetsFile.game;
animationClip = clip;
m_customCurveResolver = new CustomCurveResolver(animationClip);
}
public static AnimationClipConverter Process(AnimationClip clip)
{
var converter = new AnimationClipConverter(clip);
converter.ProcessInner();
return converter;
}
private void ProcessInner()
{
var m_Clip = animationClip.m_MuscleClip.m_Clip;
var bindings = animationClip.m_ClipBindingConstant;
var tos = animationClip.FindTOS();
var streamedFrames = m_Clip.m_StreamedClip.ReadData();
var lastDenseFrame = m_Clip.m_DenseClip.m_FrameCount / m_Clip.m_DenseClip.m_SampleRate;
var lastSampleFrame = streamedFrames.Count > 1 ? streamedFrames[streamedFrames.Count - 2].time : 0.0f;
var lastFrame = Math.Max(lastDenseFrame, lastSampleFrame);
if (!m_Clip.m_ACLClip.m_ClipData.IsNullOrEmpty() && !game.Type.IsSRGroup())
{
var lastACLFrame = ProcessACLClip(m_Clip, bindings, tos);
lastFrame = Math.Max(lastFrame, lastACLFrame);
animationClip.m_Compressed = false;
}
ProcessStreams(streamedFrames, bindings, tos, m_Clip.m_DenseClip.m_SampleRate);
ProcessDenses(m_Clip, bindings, tos);
if (!m_Clip.m_ACLClip.m_ClipData.IsNullOrEmpty() && game.Type.IsSRGroup())
{
var lastACLFrame = ProcessACLClip(m_Clip, bindings, tos);
lastFrame = Math.Max(lastFrame, lastACLFrame);
animationClip.m_Compressed = false;
}
if (m_Clip.m_ConstantClip != null)
{
ProcessConstant(m_Clip, bindings, tos, lastFrame);
}
CreateCurves();
}
private void CreateCurves()
{
m_translations.AsEnumerable().ToList().ForEach(x => x.Key.curve.m_Curve.AddRange(x.Value));
Translations = m_translations.Keys.ToArray();
m_rotations.AsEnumerable().ToList().ForEach(x => x.Key.curve.m_Curve.AddRange(x.Value));
Rotations = m_rotations.Keys.ToArray();
m_scales.AsEnumerable().ToList().ForEach(x => x.Key.curve.m_Curve.AddRange(x.Value));
Scales = m_scales.Keys.ToArray();
m_eulers.AsEnumerable().ToList().ForEach(x => x.Key.curve.m_Curve.AddRange(x.Value));
Eulers = m_eulers.Keys.ToArray();
m_floats.AsEnumerable().ToList().ForEach(x => x.Key.curve.m_Curve.AddRange(x.Value));
Floats = m_floats.Keys.ToArray();
m_pptrs.AsEnumerable().ToList().ForEach(x => x.Key.curve.AddRange(x.Value));
PPtrs = m_pptrs.Keys.ToArray();
}
private void ProcessStreams(List<StreamedClip.StreamedFrame> streamFrames, AnimationClipBindingConstant bindings, Dictionary<uint, string> tos, float sampleRate)
{
var curveValues = new float[4];
var inSlopeValues = new float[4];
var outSlopeValues = new float[4];
var interval = 1.0f / sampleRate;
// first (index [0]) stream frame is for slope calculation for the first real frame (index [1])
// last one (index [count - 1]) is +Infinity
// it is made for slope processing, but we don't need them
for (var frameIndex = 1; frameIndex < streamFrames.Count - 1; frameIndex++)
{
var frame = streamFrames[frameIndex];
for (var curveIndex = 0; curveIndex < frame.keyList.Length;)
{
var curve = frame.keyList[curveIndex];
var index = curve.index;
if (!game.Type.IsSRGroup())
index += (int)animationClip.m_MuscleClip.m_Clip.m_ACLClip.m_CurveCount;
var binding = bindings.FindBinding(index);
var path = GetCurvePath(tos, binding.path);
if (binding.typeID == ClassIDType.Transform)
{
GetPreviousFrame(streamFrames, curve.index, frameIndex, out var prevFrameIndex, out var prevCurveIndex);
var dimension = binding.GetDimension();
for (int key = 0; key < dimension; key++)
{
var keyCurve = frame.keyList[curveIndex];
var prevFrame = streamFrames[prevFrameIndex];
var prevKeyCurve = prevFrame.keyList[prevCurveIndex + key];
var deltaTime = frame.time - prevFrame.time;
curveValues[key] = keyCurve.value;
inSlopeValues[key] = prevKeyCurve.CalculateNextInSlope(deltaTime, keyCurve);
outSlopeValues[key] = keyCurve.outSlope;
curveIndex = GetNextCurve(frame, curveIndex);
}
AddTransformCurve(frame.time, binding.attribute, curveValues, inSlopeValues, outSlopeValues, 0, path);
}
else if ((BindingCustomType)binding.customType == BindingCustomType.None)
{
AddDefaultCurve(binding, path, frame.time, frame.keyList[curveIndex].value);
curveIndex = GetNextCurve(frame, curveIndex);
}
else
{
AddCustomCurve(bindings, binding, path, frame.time, frame.keyList[curveIndex].value);
curveIndex = GetNextCurve(frame, curveIndex);
}
}
}
}
private void ProcessDenses(Clip clip, AnimationClipBindingConstant bindings, Dictionary<uint, string> tos)
{
var dense = clip.m_DenseClip;
var streamCount = clip.m_StreamedClip.curveCount;
var slopeValues = new float[4]; // no slopes - 0 values
for (var frameIndex = 0; frameIndex < dense.m_FrameCount; frameIndex++)
{
var time = frameIndex / dense.m_SampleRate;
var frameOffset = frameIndex * (int)dense.m_CurveCount;
for (var curveIndex = 0; curveIndex < dense.m_CurveCount;)
{
var index = (int)streamCount + curveIndex;
if (!game.Type.IsSRGroup())
index += (int)clip.m_ACLClip.m_CurveCount;
var binding = bindings.FindBinding(index);
var path = GetCurvePath(tos, binding.path);
var framePosition = frameOffset + curveIndex;
if (binding.typeID == ClassIDType.Transform)
{
AddTransformCurve(time, binding.attribute, dense.m_SampleArray, slopeValues, slopeValues, framePosition, path);
curveIndex += binding.GetDimension();
}
else if ((BindingCustomType)binding.customType == BindingCustomType.None)
{
AddDefaultCurve(binding, path, time, dense.m_SampleArray[framePosition]);
curveIndex++;
}
else
{
AddCustomCurve(bindings, binding, path, time, dense.m_SampleArray[framePosition]);
curveIndex++;
}
}
}
}
private float ProcessACLClip(Clip clip, AnimationClipBindingConstant bindings, Dictionary<uint, string> tos)
{
var acl = clip.m_ACLClip;
acl.Process(game, out var values, out var times);
float[] slopeValues = new float[4]; // no slopes - 0 values
int frameCount = times.Length;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
{
float time = times[frameIndex];
int frameOffset = frameIndex * (int)acl.m_CurveCount;
for (int curveIndex = 0; curveIndex < acl.m_CurveCount;)
{
var index = curveIndex;
if (game.Type.IsSRGroup())
index += (int)(clip.m_DenseClip.m_CurveCount + clip.m_StreamedClip.curveCount);
GenericBinding binding = bindings.FindBinding(index);
string path = GetCurvePath(tos, binding.path);
int framePosition = frameOffset + curveIndex;
if (binding.typeID == ClassIDType.Transform)
{
AddTransformCurve(time, binding.attribute, values, slopeValues, slopeValues, framePosition, path);
curveIndex += binding.GetDimension();
}
else if ((BindingCustomType)binding.customType == BindingCustomType.None)
{
AddDefaultCurve(binding, path, time, values[framePosition]);
curveIndex++;
}
else
{
AddCustomCurve(bindings, binding, path, time, values[framePosition]);
curveIndex++;
}
}
}
return times[frameCount - 1];
}
private void ProcessConstant(Clip clip, AnimationClipBindingConstant bindings, Dictionary<uint, string> tos, float lastFrame)
{
var constant = clip.m_ConstantClip;
var streamCount = clip.m_StreamedClip.curveCount;
var denseCount = clip.m_DenseClip.m_CurveCount;
var slopeValues = new float[4]; // no slopes - 0 values
// only first and last frames
var time = 0.0f;
for (var i = 0; i < 2; i++, time += lastFrame)
{
for (var curveIndex = 0; curveIndex < constant.data.Length;)
{
var index = (int)(streamCount + denseCount + curveIndex);
if (!clip.m_ACLClip.m_ClipData.IsNullOrEmpty())
index += (int)clip.m_ACLClip.m_CurveCount;
GenericBinding binding = bindings.FindBinding(index);
string path = GetCurvePath(tos, binding.path);
if (binding.typeID == ClassIDType.Transform)
{
AddTransformCurve(time, binding.attribute, constant.data, slopeValues, slopeValues, curveIndex, path);
curveIndex += binding.GetDimension();
}
else if ((BindingCustomType)binding.customType == BindingCustomType.None)
{
AddDefaultCurve(binding, path, time, constant.data[curveIndex]);
curveIndex++;
}
else
{
AddCustomCurve(bindings, binding, path, time, constant.data[curveIndex]);
curveIndex++;
}
}
}
}
private void AddCustomCurve(AnimationClipBindingConstant bindings, GenericBinding binding, string path, float time, float value)
{
switch ((BindingCustomType)binding.customType)
{
case BindingCustomType.AnimatorMuscle:
AddAnimatorMuscleCurve(binding, time, value);
break;
default:
string attribute = m_customCurveResolver.ToAttributeName((BindingCustomType)binding.customType, binding.attribute, path);
if (binding.isPPtrCurve == 0x01)
{
PPtrCurve curve = new PPtrCurve(path, attribute, binding.typeID, binding.script.Cast<MonoScript>());
AddPPtrKeyframe(curve, bindings, time, (int)value);
}
else
{
FloatCurve curve = new FloatCurve(path, attribute, binding.typeID, binding.script.Cast<MonoScript>());
AddFloatKeyframe(curve, time, value);
}
break;
}
}
private void AddTransformCurve(float time, uint transType, float[] curveValues,
float[] inSlopeValues, float[] outSlopeValues, int offset, string path)
{
switch (transType)
{
case 1:
{
var curve = new Vector3Curve(path);
if (!m_translations.TryGetValue(curve, out List<Keyframe<Vector3>> transCurve))
{
transCurve = new List<Keyframe<Vector3>>();
m_translations.Add(curve, transCurve);
}
float x = curveValues[offset + 0];
float y = curveValues[offset + 1];
float z = curveValues[offset + 2];
float inX = inSlopeValues[0];
float inY = inSlopeValues[1];
float inZ = inSlopeValues[2];
float outX = outSlopeValues[0];
float outY = outSlopeValues[1];
float outZ = outSlopeValues[2];
Vector3 value = new Vector3(x, y, z);
Vector3 inSlope = new Vector3(inX, inY, inZ);
Vector3 outSlope = new Vector3(outX, outY, outZ);
Keyframe<Vector3> transKey = new Keyframe<Vector3>(time, value, inSlope, outSlope, AnimationClipExtensions.DefaultVector3Weight);
transCurve.Add(transKey);
}
break;
case 2:
{
var curve = new QuaternionCurve(path);
if (!m_rotations.TryGetValue(curve, out List<Keyframe<Quaternion>> rotCurve))
{
rotCurve = new List<Keyframe<Quaternion>>();
m_rotations.Add(curve, rotCurve);
}
float x = curveValues[offset + 0];
float y = curveValues[offset + 1];
float z = curveValues[offset + 2];
float w = curveValues[offset + 3];
float inX = inSlopeValues[0];
float inY = inSlopeValues[1];
float inZ = inSlopeValues[2];
float inW = inSlopeValues[3];
float outX = outSlopeValues[0];
float outY = outSlopeValues[1];
float outZ = outSlopeValues[2];
float outW = outSlopeValues[3];
Quaternion value = new Quaternion(x, y, z, w);
Quaternion inSlope = new Quaternion(inX, inY, inZ, inW);
Quaternion outSlope = new Quaternion(outX, outY, outZ, outW);
Keyframe<Quaternion> rotKey = new Keyframe<Quaternion>(time, value, inSlope, outSlope, AnimationClipExtensions.DefaultQuaternionWeight);
rotCurve.Add(rotKey);
}
break;
case 3:
{
var curve = new Vector3Curve(path);
if (!m_scales.TryGetValue(curve, out List<Keyframe<Vector3>> scaleCurve))
{
scaleCurve = new List<Keyframe<Vector3>>();
m_scales.Add(curve, scaleCurve);
}
float x = curveValues[offset + 0];
float y = curveValues[offset + 1];
float z = curveValues[offset + 2];
float inX = inSlopeValues[0];
float inY = inSlopeValues[1];
float inZ = inSlopeValues[2];
float outX = outSlopeValues[0];
float outY = outSlopeValues[1];
float outZ = outSlopeValues[2];
Vector3 value = new Vector3(x, y, z);
Vector3 inSlope = new Vector3(inX, inY, inZ);
Vector3 outSlope = new Vector3(outX, outY, outZ);
Keyframe<Vector3> scaleKey = new Keyframe<Vector3>(time, value, inSlope, outSlope, AnimationClipExtensions.DefaultVector3Weight);
scaleCurve.Add(scaleKey);
}
break;
case 4:
{
var curve = new Vector3Curve(path);
if (!m_eulers.TryGetValue(curve, out List<Keyframe<Vector3>> eulerCurve))
{
eulerCurve = new List<Keyframe<Vector3>>();
m_eulers.Add(curve, eulerCurve);
}
float x = curveValues[offset + 0];
float y = curveValues[offset + 1];
float z = curveValues[offset + 2];
float inX = inSlopeValues[0];
float inY = inSlopeValues[1];
float inZ = inSlopeValues[2];
float outX = outSlopeValues[0];
float outY = outSlopeValues[1];
float outZ = outSlopeValues[2];
Vector3 value = new Vector3(x, y, z);
Vector3 inSlope = new Vector3(inX, inY, inZ);
Vector3 outSlope = new Vector3(outX, outY, outZ);
Keyframe<Vector3> eulerKey = new Keyframe<Vector3>(time, value, inSlope, outSlope, AnimationClipExtensions.DefaultVector3Weight);
eulerCurve.Add(eulerKey);
}
break;
default:
throw new NotImplementedException(transType.ToString());
}
}
private void AddDefaultCurve(GenericBinding binding, string path, float time, float value)
{
switch (binding.typeID)
{
case ClassIDType.GameObject:
{
AddGameObjectCurve(binding, path, time, value);
}
break;
case ClassIDType.MonoBehaviour:
{
AddScriptCurve(binding, path, time, value);
}
break;
default:
AddEngineCurve(binding, path, time, value);
break;
}
}
private void AddGameObjectCurve(GenericBinding binding, string path, float time, float value)
{
if (binding.attribute == CRC.CalculateDigestAscii("m_IsActive"))
{
FloatCurve curve = new FloatCurve(path, "m_IsActive", ClassIDType.GameObject, new PPtr<MonoScript>(0, 0, null));
AddFloatKeyframe(curve, time, value);
return;
}
else
{
// that means that dev exported animation clip with missing component
FloatCurve curve = new FloatCurve(path, MissedPropertyPrefix + binding.attribute, ClassIDType.GameObject, new PPtr<MonoScript>(0, 0, null));
AddFloatKeyframe(curve, time, value);
}
}
private void AddScriptCurve(GenericBinding binding, string path, float time, float value)
{
#warning TODO:
FloatCurve curve = new FloatCurve(path, ScriptPropertyPrefix + binding.attribute, ClassIDType.MonoBehaviour, binding.script.Cast<MonoScript>());
AddFloatKeyframe(curve, time, value);
}
private void AddEngineCurve(GenericBinding binding, string path, float time, float value)
{
#warning TODO:
FloatCurve curve = new FloatCurve(path, TypeTreePropertyPrefix + binding.attribute, binding.typeID, new PPtr<MonoScript>(0, 0, null));
AddFloatKeyframe(curve, time, value);
}
private void AddAnimatorMuscleCurve(GenericBinding binding, float time, float value)
{
FloatCurve curve = new FloatCurve(string.Empty, binding.GetHumanoidMuscle().ToAttributeString(), ClassIDType.Animator, new PPtr<MonoScript>(0, 0, null));
AddFloatKeyframe(curve, time, value);
}
private void AddFloatKeyframe(FloatCurve curve, float time, float value)
{
if (!m_floats.TryGetValue(curve, out List<Keyframe<Float>> floatCurve))
{
floatCurve = new List<Keyframe<Float>>();
m_floats.Add(curve, floatCurve);
}
Keyframe<Float> floatKey = new Keyframe<Float>(time, value, default, default, AnimationClipExtensions.DefaultFloatWeight);
floatCurve.Add(floatKey);
}
private void AddPPtrKeyframe(PPtrCurve curve, AnimationClipBindingConstant bindings, float time, int index)
{
if (!m_pptrs.TryGetValue(curve, out List<PPtrKeyframe> pptrCurve))
{
pptrCurve = new List<PPtrKeyframe>();
m_pptrs.Add(curve, pptrCurve);
AddPPtrKeyframe(curve, bindings, 0.0f, index - 1);
}
PPtr<Object> value = bindings.pptrCurveMapping[index];
PPtrKeyframe pptrKey = new PPtrKeyframe(time, value);
pptrCurve.Add(pptrKey);
}
private void GetPreviousFrame(List<StreamedClip.StreamedFrame> streamFrames, int curveID, int currentFrame, out int frameIndex, out int curveIndex)
{
for (frameIndex = currentFrame - 1; frameIndex >= 0; frameIndex--)
{
var frame = streamFrames[frameIndex];
for (curveIndex = 0; curveIndex < frame.keyList.Length; curveIndex++)
{
var curve = frame.keyList[curveIndex];
if (curve.index == curveID)
{
return;
}
}
}
throw new Exception($"There is no curve with index {curveID} in any of previous frames");
}
private int GetNextCurve(StreamedClip.StreamedFrame frame, int currentCurve)
{
var curve = frame.keyList[currentCurve];
int i = currentCurve + 1;
for (; i < frame.keyList.Length; i++)
{
if (frame.keyList[i].index != curve.index)
{
return i;
}
}
return i;
}
private static string GetCurvePath(Dictionary<uint, string> tos, uint hash)
{
if (tos.TryGetValue(hash, out string path))
{
return path;
}
else
{
return UnknownPathPrefix + hash;
}
}
}
}

View File

@@ -0,0 +1,335 @@
using System.IO;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using SevenZip;
using System;
namespace AssetStudio
{
public static class AnimationClipExtensions
{
public static float DefaultFloatWeight => 1.0f / 3.0f;
public static Vector3 DefaultVector3Weight => new Vector3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f);
public static Quaternion DefaultQuaternionWeight => new Quaternion(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f);
#region AnimationClip
public static IEnumerable<GameObject> FindRoots(this AnimationClip clip)
{
foreach (var asset in clip.assetsFile.assetsManager.assetsFileList.SelectMany(x => x.Objects))
{
switch (asset.type)
{
case ClassIDType.Animator:
Animator animator = (Animator)asset;
if (clip.IsAnimatorContainsClip(animator))
{
if (animator.m_GameObject.TryGet(out var go))
{
yield return go;
}
}
break;
case ClassIDType.Animation:
Animation animation = (Animation)asset;
if (clip.IsAnimationContainsClip(animation))
{
if (animation.m_GameObject.TryGet(out var go))
{
yield return go;
}
}
break;
}
}
yield break;
}
public static Dictionary<uint, string> FindTOS(this AnimationClip clip)
{
var tos = new Dictionary<uint, string>() { { 0, string.Empty } };
foreach (var asset in clip.assetsFile.assetsManager.assetsFileList.SelectMany(x => x.Objects).OrderBy(x => x.type).ToArray())
{
switch (asset.type)
{
case ClassIDType.Avatar:
var avatar = asset as Avatar;
if (clip.AddAvatarTOS(avatar, tos))
{
return tos;
}
break;
case ClassIDType.Animator:
var animator = asset as Animator;
if (clip.IsAnimatorContainsClip(animator))
{
if (clip.AddAnimatorTOS(animator, tos))
{
return tos;
}
}
break;
case ClassIDType.Animation:
var animation = asset as Animation;
if (clip.IsAnimationContainsClip(animation))
{
if (clip.AddAnimationTOS(animation, tos))
{
return tos;
}
}
break;
}
}
return tos;
}
private static bool AddAvatarTOS(this AnimationClip clip, Avatar avatar, Dictionary<uint, string> tos)
{
return clip.AddTOS(avatar.m_TOS.ToDictionary(x => x.Key, x => x.Value), tos);
}
private static bool AddAnimatorTOS(this AnimationClip clip, Animator animator, Dictionary<uint, string> tos)
{
if (animator.m_Avatar.TryGet(out var avatar))
{
if (clip.AddAvatarTOS(avatar, tos))
{
return true;
}
}
Dictionary<uint, string> animatorTOS = animator.BuildTOS();
return clip.AddTOS(animatorTOS, tos);
}
private static bool AddAnimationTOS(this AnimationClip clip, Animation animation, Dictionary<uint, string> tos)
{
if (animation.m_GameObject.TryGet(out var go))
{
Dictionary<uint, string> animationTOS = go.BuildTOS();
return clip.AddTOS(animationTOS, tos);
}
return false;
}
private static bool AddTOS(this AnimationClip clip, Dictionary<uint, string> src, Dictionary<uint, string> dest)
{
int tosCount = clip.m_ClipBindingConstant.genericBindings.Length;
for (int i = 0; i < tosCount; i++)
{
ref GenericBinding binding = ref clip.m_ClipBindingConstant.genericBindings[i];
if (src.TryGetValue(binding.path, out string path))
{
dest[binding.path] = path;
if (dest.Count == tosCount)
{
return true;
}
}
}
return false;
}
private static bool IsAnimationContainsClip(this AnimationClip clip, Animation animation)
{
return animation.IsContainsAnimationClip(clip);
}
private static bool IsAnimatorContainsClip(this AnimationClip clip, Animator animator)
{
if (animator.m_Controller.TryGet(out var runtime))
{
return runtime.IsContainsAnimationClip(clip);
}
else
{
return false;
}
}
public static string Convert(this AnimationClip clip)
{
var converter = AnimationClipConverter.Process(clip);
clip.m_RotationCurves = converter.Rotations.Union(clip.m_RotationCurves).ToArray();
clip.m_EulerCurves = converter.Eulers.Union(clip.m_EulerCurves).ToArray();
clip.m_PositionCurves = converter.Translations.Union(clip.m_PositionCurves).ToArray();
clip.m_ScaleCurves = converter.Scales.Union(clip.m_ScaleCurves).ToArray();
clip.m_FloatCurves = converter.Floats.Union(clip.m_FloatCurves).ToArray();
clip.m_PPtrCurves = converter.PPtrs.Union(clip.m_PPtrCurves).ToArray();
return ConvertSerializedAnimationClip(clip);
}
public static string ConvertSerializedAnimationClip(AnimationClip animationClip)
{
var sb = new StringBuilder();
using (var stringWriter = new StringWriter(sb))
{
YAMLWriter writer = new YAMLWriter();
YAMLDocument doc = ExportYAMLDocument(animationClip);
writer.AddDocument(doc);
writer.Write(stringWriter);
return sb.ToString();
}
}
public static YAMLDocument ExportYAMLDocument(AnimationClip animationClip)
{
var document = new YAMLDocument();
var root = document.CreateMappingRoot();
root.Tag = ((int)ClassIDType.AnimationClip).ToString();
root.Anchor = ((int)ClassIDType.AnimationClip * 100000).ToString();
var node = animationClip.ExportYAML(animationClip.version);
root.Add(ClassIDType.AnimationClip.ToString(), node);
return document;
}
public static YAMLMappingNode ExportYAML(this AnimationClip clip, int[] version)
{
var node = new YAMLMappingNode();
node.Add(nameof(clip.m_Name), clip.m_Name);
node.AddSerializedVersion(ToSerializedVersion(version));
node.Add(nameof(clip.m_Legacy), clip.m_Legacy);
node.Add(nameof(clip.m_Compressed), clip.m_Compressed);
node.Add(nameof(clip.m_UseHighQualityCurve), clip.m_UseHighQualityCurve);
node.Add(nameof(clip.m_RotationCurves), clip.m_RotationCurves.ExportYAML(version));
node.Add(nameof(clip.m_CompressedRotationCurves), clip.m_CompressedRotationCurves.ExportYAML(version));
node.Add(nameof(clip.m_EulerCurves), clip.m_EulerCurves.ExportYAML(version));
node.Add(nameof(clip.m_PositionCurves), clip.m_PositionCurves.ExportYAML(version));
node.Add(nameof(clip.m_ScaleCurves), clip.m_ScaleCurves.ExportYAML(version));
node.Add(nameof(clip.m_FloatCurves), clip.m_FloatCurves.ExportYAML(version));
node.Add(nameof(clip.m_PPtrCurves), clip.m_PPtrCurves.ExportYAML(version));
node.Add(nameof(clip.m_SampleRate), clip.m_SampleRate);
node.Add(nameof(clip.m_WrapMode), clip.m_WrapMode);
node.Add(nameof(clip.m_Bounds), clip.m_Bounds.ExportYAML(version));
node.Add(nameof(clip.m_ClipBindingConstant), clip.m_ClipBindingConstant.ExportYAML(version));
node.Add("m_AnimationClipSettings", clip.m_MuscleClip.ExportYAML(version));
node.Add(nameof(clip.m_Events), clip.m_Events.ExportYAML(version));
return node;
}
public static int ToSerializedVersion(int[] version)
{
if (version[0] >= 5)
{
return 6;
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3))
{
return 4;
}
if (version[0] > 2 || (version[0] == 2 && version[1] >= 6))
{
return 3;
}
return 2;
}
#endregion
#region Others
private static bool IsContainsAnimationClip(this Animation animation, AnimationClip clip)
{
foreach (PPtr<AnimationClip> ptr in animation.m_Animations)
{
if (ptr.TryGet(out var animationClip) && animationClip.Equals(clip))
{
return true;
}
}
return false;
}
private static Dictionary<uint, string> BuildTOS(this Animator animator)
{
if (animator.version[0] > 4 || (animator.version[0] == 4 && animator.version[1] >= 3))
{
if (animator.m_HasTransformHierarchy)
{
if (animator.m_GameObject.TryGet(out var go))
{
return go.BuildTOS();
}
}
else
{
return new Dictionary<uint, string>() { { 0, string.Empty } };
}
}
else
{
if (animator.m_GameObject.TryGet(out var go))
{
return go.BuildTOS();
}
}
return null;
}
private static Dictionary<uint, string> BuildTOS(this GameObject gameObject)
{
Dictionary<uint, string> tos = new Dictionary<uint, string>() { { 0, string.Empty } };
gameObject.BuildTOS(string.Empty, tos);
return tos;
}
private static void BuildTOS(this GameObject parent, string parentPath, Dictionary<uint, string> tos)
{
Transform transform = parent.m_Transform;
foreach (PPtr<Transform> childPtr in transform.m_Children)
{
if (childPtr.TryGet(out var childTransform))
{
if (childTransform.m_GameObject.TryGet(out var child))
{
string path = parentPath != string.Empty ? parentPath + '/' + child.m_Name : child.m_Name;
var pathHash = CRC.CalculateDigestUTF8(path);
tos[pathHash] = path;
BuildTOS(child, path, tos);
}
}
}
}
private static bool IsContainsAnimationClip(this RuntimeAnimatorController runtimeAnimatorController, AnimationClip clip)
{
if (runtimeAnimatorController is AnimatorController animatorController)
{
foreach (PPtr<AnimationClip> ptr in animatorController.m_AnimationClips)
{
if (ptr.TryGet(out var animationClip) && animationClip.Equals(clip))
{
return true;
}
}
}
return false;
}
public static int GetDimension(this GenericBinding binding)
{
return binding.attribute == 2 ? 4 : 3;
}
public static HumanoidMuscleType GetHumanoidMuscle(this GenericBinding binding)
{
return ((HumanoidMuscleType)binding.attribute).Update(binding.version);
}
#endregion
}
public enum BindingCustomType : byte
{
None = 0,
Transform = 4,
AnimatorMuscle = 8,
BlendShape = 20,
Renderer = 21,
RendererMaterial = 22,
SpriteRenderer = 23,
MonoBehaviour = 24,
Light = 25,
RendererShadows = 26,
ParticleSystem = 27,
RectTransform = 28,
LineRenderer = 29,
TrailRenderer = 30,
PositionConstraint = 31,
RotationConstraint = 32,
ScaleConstraint = 33,
AimConstraint = 34,
ParentConstraint = 35,
LookAtConstraint = 36,
Camera = 37,
}
}

View File

@@ -0,0 +1,672 @@
using System;
using SevenZip;
using System.Linq;
using System.Xml.Linq;
namespace AssetStudio
{
public sealed class CustomCurveResolver
{
public CustomCurveResolver(AnimationClip clip)
{
if (clip == null)
{
throw new ArgumentNullException(nameof(clip));
}
m_clip = clip;
}
public string ToAttributeName(BindingCustomType type, uint attribute, string path)
{
switch (type)
{
case BindingCustomType.BlendShape:
{
const string Prefix = "blendShape.";
if (AnimationClipConverter.UnknownPathRegex.IsMatch(path))
{
return Prefix + attribute;
}
foreach (GameObject root in Roots)
{
var rootTransform = root.m_Transform;
var child = rootTransform.FindChild(path);
if (child == null)
{
continue;
}
SkinnedMeshRenderer skin = null;
if (child.m_GameObject.TryGet(out var go))
{
skin = go.m_SkinnedMeshRenderer;
}
if (skin == null)
{
continue;
}
if (!skin.m_Mesh.TryGet(out var mesh))
{
continue;
}
string shapeName = mesh.FindBlendShapeNameByCRC(attribute);
if (shapeName == null)
{
continue;
}
return Prefix + shapeName;
}
return Prefix + attribute;
}
case BindingCustomType.Renderer:
return "m_Materials." + CommonString.StringBuffer[0x31] + "." + CommonString.StringBuffer[0x6A] + $"[{attribute}]";
case BindingCustomType.RendererMaterial:
{
const string Prefix = "material.";
if (AnimationClipConverter.UnknownPathRegex.IsMatch(path))
{
return Prefix + attribute;
}
foreach (GameObject root in Roots)
{
Transform rootTransform = root.m_Transform;
Transform child = rootTransform.FindChild(path);
if (child == null)
{
continue;
}
uint crc28 = attribute & 0xFFFFFFF;
Renderer renderer = null;
if (child.m_GameObject.TryGet(out var go))
{
renderer = (Renderer)go.m_SkinnedMeshRenderer ?? go.m_MeshRenderer;
}
if (renderer == null)
{
continue;
}
string property = renderer.FindMaterialPropertyNameByCRC28(crc28);
if (property == null)
{
continue;
}
if ((attribute & 0x80000000) != 0)
{
return Prefix + property;
}
char subProperty;
uint subPropIndex = attribute >> 28 & 3;
bool isRgba = (attribute & 0x40000000) != 0;
switch (subPropIndex)
{
case 0:
subProperty = isRgba ? 'r' : 'x';
break;
case 1:
subProperty = isRgba ? 'g' : 'y';
break;
case 2:
subProperty = isRgba ? 'b' : 'z';
break;
default:
subProperty = isRgba ? 'a' : 'w';
break;
}
return Prefix + property + "." + subProperty;
}
return Prefix + attribute;
}
case BindingCustomType.SpriteRenderer:
{
if (attribute == 0)
{
return "m_Sprite";
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
case BindingCustomType.MonoBehaviour:
{
if (attribute == CRC.CalculateDigestAscii("m_Enabled"))
{
return "m_Enabled";
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
case BindingCustomType.Light:
{
string ColorR = "m_Color.r";
if (attribute == CRC.CalculateDigestAscii(ColorR))
{
return ColorR;
}
string ColorG = "m_Color.g";
if (attribute == CRC.CalculateDigestAscii(ColorG))
{
return ColorG;
}
string ColorB = "m_Color.b";
if (attribute == CRC.CalculateDigestAscii(ColorB))
{
return ColorB;
}
string ColorA = "m_Color.a";
if (attribute == CRC.CalculateDigestAscii(ColorA))
{
return ColorA;
}
if (attribute == CRC.CalculateDigestAscii("m_CookieSize"))
{
return "m_CookieSize";
}
if (attribute == CRC.CalculateDigestAscii("m_DrawHalo"))
{
return "m_DrawHalo";
}
if (attribute == CRC.CalculateDigestAscii("m_Intensity"))
{
return "m_Intensity";
}
if (attribute == CRC.CalculateDigestAscii("m_Range"))
{
return "m_Range";
}
const string ShadowsStrength = "m_Shadows.m_Strength";
if (attribute == CRC.CalculateDigestAscii(ShadowsStrength))
{
return ShadowsStrength;
}
const string ShadowsBias = "m_Shadows.m_Bias";
if (attribute == CRC.CalculateDigestAscii(ShadowsBias))
{
return ShadowsBias;
}
const string ShadowsNormalBias = "m_Shadows.m_NormalBias";
if (attribute == CRC.CalculateDigestAscii(ShadowsNormalBias))
{
return ShadowsNormalBias;
}
const string ShadowsNearPlane = "m_Shadows.m_NearPlane";
if (attribute == CRC.CalculateDigestAscii(ShadowsNearPlane))
{
return ShadowsNearPlane;
}
if (attribute == CRC.CalculateDigestAscii("m_SpotAngle"))
{
return "m_SpotAngle";
}
if (attribute == CRC.CalculateDigestAscii("m_ColorTemperature"))
{
return "m_ColorTemperature";
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
case BindingCustomType.RendererShadows:
{
if (attribute == CRC.CalculateDigestAscii("m_ReceiveShadows"))
{
return "m_ReceiveShadows";
}
if (attribute == CRC.CalculateDigestAscii("m_SortingOrder"))
{
return "m_SortingOrder";
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
#warning TODO:
case BindingCustomType.ParticleSystem:
return "ParticleSystem_" + attribute;
/*{
// TODO: ordinal propertyName
}
throw new ArgumentException($"Unknown attribute {attribute} for {_this}");*/
case BindingCustomType.RectTransform:
{
string LocalPositionZ = "m_LocalPosition.z";
if (attribute == CRC.CalculateDigestAscii(LocalPositionZ))
{
return LocalPositionZ;
}
string AnchoredPositionX = "m_AnchoredPosition.x";
if (attribute == CRC.CalculateDigestAscii(AnchoredPositionX))
{
return AnchoredPositionX;
}
string AnchoredPositionY = "m_AnchoredPosition.y";
if (attribute == CRC.CalculateDigestAscii(AnchoredPositionY))
{
return AnchoredPositionY;
}
string AnchorMinX = "m_AnchorMin.x";
if (attribute == CRC.CalculateDigestAscii(AnchorMinX))
{
return AnchorMinX;
}
string AnchorMinY = "m_AnchorMin.y";
if (attribute == CRC.CalculateDigestAscii(AnchorMinY))
{
return AnchorMinY;
}
string AnchorMaxX = "m_AnchorMax.x";
if (attribute == CRC.CalculateDigestAscii(AnchorMaxX))
{
return AnchorMaxX;
}
string AnchorMaxY = "m_AnchorMax.y";
if (attribute == CRC.CalculateDigestAscii(AnchorMaxY))
{
return AnchorMaxY;
}
string SizeDeltaX = "m_SizeDelta.x";
if (attribute == CRC.CalculateDigestAscii(SizeDeltaX))
{
return SizeDeltaX;
}
string SizeDeltaY = "m_SizeDelta.y";
if (attribute == CRC.CalculateDigestAscii(SizeDeltaY))
{
return SizeDeltaY;
}
string PivotX = "m_Pivot.x";
if (attribute == CRC.CalculateDigestAscii(PivotX))
{
return PivotX;
}
string PivotY = "m_Pivot.y";
if (attribute == CRC.CalculateDigestAscii(PivotY))
{
return PivotY;
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
#warning TODO:
case BindingCustomType.LineRenderer:
{
const string ParametersWidthMultiplier = "m_Parameters" + "." + "widthMultiplier";
if (attribute == CRC.CalculateDigestAscii(ParametersWidthMultiplier))
{
return ParametersWidthMultiplier;
}
}
// TODO: old versions animate all properties as custom curves
return "LineRenderer_" + attribute;
#warning TODO:
case BindingCustomType.TrailRenderer:
{
const string ParametersWidthMultiplier = "m_Parameters" + "." + "widthMultiplier";
if (attribute == CRC.CalculateDigestAscii(ParametersWidthMultiplier))
{
return ParametersWidthMultiplier;
}
}
// TODO: old versions animate all properties as custom curves
return "TrailRenderer_" + attribute;
#warning TODO:
case BindingCustomType.PositionConstraint:
{
uint property = attribute & 0xF;
switch (property)
{
case 0:
return "m_RestTranslation.x";
case 1:
return "m_RestTranslation.y";
case 2:
return "m_RestTranslation.z";
case 3:
return "m_Weight";
case 4:
return "m_TranslationOffset.x";
case 5:
return "m_TranslationOffset.y";
case 6:
return "m_TranslationOffset.z";
case 7:
return "m_AffectTranslationX";
case 8:
return "m_AffectTranslationY";
case 9:
return "m_AffectTranslationZ";
case 10:
return "m_Active";
case 11:
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
case 12:
return $"m_Sources.Array.data[{attribute >> 8}].weight";
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
#warning TODO:
case BindingCustomType.RotationConstraint:
{
uint property = attribute & 0xF;
switch (property)
{
case 0:
return "m_RestRotation.x";
case 1:
return "m_RestRotation.y";
case 2:
return "m_RestRotation.z";
case 3:
return "m_Weight";
case 4:
return "m_RotationOffset.x";
case 5:
return "m_RotationOffset.y";
case 6:
return "m_RotationOffset.z";
case 7:
return "m_AffectRotationX";
case 8:
return "m_AffectRotationY";
case 9:
return "m_AffectRotationZ";
case 10:
return "m_Active";
case 11:
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
case 12:
return $"m_Sources.Array.data[{attribute >> 8}].weight";
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
#warning TODO:
case BindingCustomType.ScaleConstraint:
{
uint property = attribute & 0xF;
switch (property)
{
case 0:
return "m_ScaleAtRest.x";
case 1:
return "m_ScaleAtRest.y";
case 2:
return "m_ScaleAtRest.z";
case 3:
return "m_Weight";
case 4:
return "m_ScalingOffset.x";
case 5:
return "m_ScalingOffset.y";
case 6:
return "m_ScalingOffset.z";
case 7:
return "m_AffectScalingX";
case 8:
return "m_AffectScalingY";
case 9:
return "m_AffectScalingZ";
case 10:
return "m_Active";
case 11:
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
case 12:
return $"m_Sources.Array.data[{attribute >> 8}].weight";
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
#warning TODO:
case BindingCustomType.AimConstraint:
{
uint property = attribute & 0xF;
switch (property)
{
case 0:
return "m_Weight";
case 1:
return "m_AffectRotationX";
case 2:
return "m_AffectRotationY";
case 3:
return "m_AffectRotationZ";
case 4:
return "m_Active";
case 5:
return "m_WorldUpObject";
case 6:
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
case 7:
return $"m_Sources.Array.data[{attribute >> 8}].weight";
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
#warning TODO:
case BindingCustomType.ParentConstraint:
{
uint property = attribute & 0xF;
switch (property)
{
case 0:
return "m_Weight";
case 1:
return "m_AffectTranslationX";
case 2:
return "m_AffectTranslationY";
case 3:
return "m_AffectTranslationZ";
case 4:
return "m_AffectRotationX";
case 5:
return "m_AffectRotationY";
case 6:
return "m_AffectRotationZ";
case 7:
return "m_Active";
case 8:
return $"m_TranslationOffsets.Array.data[{attribute >> 8}].x";
case 9:
return $"m_TranslationOffsets.Array.data[{attribute >> 8}].y";
case 10:
return $"m_TranslationOffsets.Array.data[{attribute >> 8}].z";
case 11:
return $"m_RotationOffsets.Array.data[{attribute >> 8}].x";
case 12:
return $"m_RotationOffsets.Array.data[{attribute >> 8}].y";
case 13:
return $"m_RotationOffsets.Array.data[{attribute >> 8}].z";
case 14:
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
case 15:
return $"m_Sources.Array.data[{attribute >> 8}].weight";
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
#warning TODO:
case BindingCustomType.LookAtConstraint:
{
uint property = attribute & 0xF;
switch (property)
{
case 0:
return "m_Weight";
case 1:
return "m_Active";
case 2:
return "m_WorldUpObject";
case 3:
return $"m_Sources.Array.data[{attribute >> 8}].sourceTransform";
case 4:
return $"m_Sources.Array.data[{attribute >> 8}].weight";
case 5:
return "m_Roll";
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
case BindingCustomType.Camera:
{
if (attribute == CRC.CalculateDigestAscii("field of view"))
{
return "field of view";
}
if (attribute == CRC.CalculateDigestAscii("m_FocalLength"))
{
return "m_FocalLength";
}
}
throw new ArgumentException($"Unknown attribute {attribute} for {type}");
default:
throw new ArgumentException(type.ToString());
}
}
private GameObject[] Roots
{
get
{
if (!m_rootInited)
{
m_roots = m_clip.FindRoots().ToArray();
m_rootInited = true;
}
return m_roots;
}
}
private readonly AnimationClip m_clip = null;
private GameObject[] m_roots = null;
private bool m_rootInited = false;
}
public static class CustomCurveResolverExtensions
{
public static Transform FindChild(this Transform transform, string path)
{
if (path.Length == 0)
{
return transform;
}
return transform.FindChild(path, 0);
}
private static Transform FindChild(this Transform transform, string path, int startIndex)
{
int separatorIndex = path.IndexOf('/', startIndex);
string childName = separatorIndex == -1 ?
path.Substring(startIndex, path.Length - startIndex) :
path.Substring(startIndex, separatorIndex - startIndex);
foreach (PPtr<Transform> childPtr in transform.m_Children)
{
if (childPtr.TryGet(out var child))
{
if (child.m_GameObject.TryGet(out var childGO) && childGO.m_Name == childName)
{
return separatorIndex == -1 ? child : child.FindChild(path, separatorIndex + 1);
}
}
}
return null;
}
public static string FindBlendShapeNameByCRC(this Mesh mesh, uint crc)
{
if (mesh.version[0] > 4 || (mesh.version[0] == 4 && mesh.version[1] >= 3))
{
return mesh.m_Shapes.FindShapeNameByCRC(crc);
}
else
{
foreach (var blendShape in mesh.m_Shapes.shapes)
{
if (CRC.VerifyDigestUTF8(blendShape.name, crc))
{
return blendShape.name;
}
}
return null;
}
}
public static string FindShapeNameByCRC(this BlendShapeData blendShapeData, uint crc)
{
foreach (var blendChannel in blendShapeData.channels)
{
if (blendChannel.nameHash == crc)
{
return blendChannel.name;
}
}
return null;
}
public static string FindMaterialPropertyNameByCRC28(this Renderer renderer, uint crc)
{
foreach (PPtr<Material> materialPtr in renderer.m_Materials)
{
if (!materialPtr.TryGet(out var material))
{
continue;
}
string property = material.FindPropertyNameByCRC28(crc);
if (property == null)
{
continue;
}
return property;
}
return null;
}
public static string FindPropertyNameByCRC28(this Material material, uint crc)
{
foreach (var property in material.m_SavedProperties.m_TexEnvs.Keys)
{
string hdrName = $"{property}_HDR";
if (CRC.Verify28DigestUTF8(hdrName, crc))
{
return hdrName;
}
string stName = $"{property}_ST";
if (CRC.Verify28DigestUTF8(stName, crc))
{
return stName;
}
string texelName = $"{property}_TexelSize";
if (CRC.Verify28DigestUTF8(texelName, crc))
{
return texelName;
}
}
foreach (var property in material.m_SavedProperties.m_Floats.Keys)
{
if (CRC.Verify28DigestUTF8(property, crc))
{
return property;
}
}
foreach (var property in material.m_SavedProperties.m_Colors.Keys)
{
if (CRC.Verify28DigestUTF8(property, crc))
{
return property;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,648 @@
using System;
namespace AssetStudio
{
public enum HumanoidMuscleType
{
Motion = 0,
Root = Motion + 7,
Limbs = Root + 7,
Muscles = Limbs + LimbType.Last * 7,
Fingers = Muscles + MuscleType.Last,
TDoFBones = Fingers + ArmType.Last * FingerType.Last * FingerDoFType.Last,
Last = TDoFBones + TDoFBoneType.Last * 3,
}
public static class AnimationMuscleTypeExtensions
{
public static HumanoidMuscleType Update(this HumanoidMuscleType _this, int[] version)
{
if (_this < HumanoidMuscleType.Muscles)
{
return _this;
}
MuscleType muscle = (MuscleType)(_this - HumanoidMuscleType.Muscles);
MuscleType fixedMuscle = muscle.Update(version);
_this = HumanoidMuscleType.Muscles + (int)fixedMuscle;
if (_this < HumanoidMuscleType.TDoFBones)
{
return _this;
}
TDoFBoneType tdof = (TDoFBoneType)(_this - HumanoidMuscleType.TDoFBones);
TDoFBoneType fixedTdof = tdof.Update(version);
_this = HumanoidMuscleType.TDoFBones + (int)fixedTdof;
return _this;
}
public static string ToAttributeString(this HumanoidMuscleType _this)
{
if (_this < HumanoidMuscleType.Root)
{
int delta = _this - HumanoidMuscleType.Motion;
return nameof(HumanoidMuscleType.Motion) + GetTransformPostfix(delta % 7);
}
if (_this < HumanoidMuscleType.Limbs)
{
int delta = _this - HumanoidMuscleType.Root;
return nameof(HumanoidMuscleType.Root) + GetTransformPostfix(delta % 7);
}
if (_this < HumanoidMuscleType.Muscles)
{
int delta = _this - HumanoidMuscleType.Limbs;
LimbType limb = (LimbType)(delta / 7);
return limb.ToBoneType().ToAttributeString() + GetTransformPostfix(delta % 7);
}
if (_this < HumanoidMuscleType.Fingers)
{
int delta = _this - HumanoidMuscleType.Muscles;
MuscleType muscle = (MuscleType)delta;
return muscle.ToAttributeString();
}
if (_this < HumanoidMuscleType.TDoFBones)
{
const int armSize = (int)FingerType.Last * (int)FingerDoFType.Last;
const int dofSize = (int)FingerDoFType.Last;
int delta = _this - HumanoidMuscleType.Fingers;
ArmType arm = (ArmType)(delta / armSize);
delta = delta % armSize;
FingerType finger = (FingerType)(delta / dofSize);
delta = delta % dofSize;
FingerDoFType dof = (FingerDoFType)delta;
return $"{arm.ToBoneType().ToAttributeString()}.{finger.ToAttributeString()}.{dof.ToAttributeString()}";
}
if (_this < HumanoidMuscleType.Last)
{
int delta = _this - HumanoidMuscleType.TDoFBones;
TDoFBoneType tdof = (TDoFBoneType)(delta / 3);
return $"{tdof.ToBoneType().ToAttributeString()}{GetTDoFTransformPostfix(delta % 3)}";
}
throw new ArgumentException(_this.ToString());
}
private static string GetTransformPostfix(int index)
{
switch (index)
{
case 0:
return "T.x";
case 1:
return "T.y";
case 2:
return "T.z";
case 3:
return "Q.x";
case 4:
return "Q.y";
case 5:
return "Q.z";
case 6:
return "Q.w";
default:
throw new ArgumentException(index.ToString());
}
}
private static string GetTDoFTransformPostfix(int index)
{
switch (index)
{
case 0:
return "TDOF.x";
case 1:
return "TDOF.y";
case 2:
return "TDOF.z";
default:
throw new ArgumentException(index.ToString());
}
}
}
public enum LimbType
{
LeftFoot = 0,
RightFoot = 1,
LeftHand = 2,
RightHand = 3,
Last,
}
public static class LimbTypeExtensions
{
public static BoneType ToBoneType(this LimbType _this)
{
switch (_this)
{
case LimbType.LeftFoot:
return BoneType.LeftFoot;
case LimbType.RightFoot:
return BoneType.RightFoot;
case LimbType.LeftHand:
return BoneType.LeftHand;
case LimbType.RightHand:
return BoneType.RightHand;
default:
throw new ArgumentException(_this.ToString());
}
}
}
public enum MuscleType
{
SpineFrontBack = 0,
SpineLeftRight = 1,
SpineTwistLeftRight = 2,
ChestFrontBack = 3,
ChestLeftRight = 4,
ChestTwistLeftRight = 5,
UpperchestFrontBack = 6,
UpperchestLeftRight = 7,
UpperchestTwisLeftRight = 8,
NeckNodDownUp = 9,
NeckTiltLeftRight = 10,
NeckTurnLeftRight = 11,
HeadNodDownUp = 12,
HeadTiltLeftRight = 13,
HeadTurnLeftRight = 14,
LeftEyeDownUp = 15,
LeftEyeInOut = 16,
RightEyeDownUp = 17,
RightEyeInOut = 18,
JawClose = 19,
JawLeftRight = 20,
LeftUpperLegFrontBack = 21,
LeftUpperLegInOut = 22,
LeftUpperLegTwistInOut = 23,
LeftLowerLegStretch = 24,
LeftLowerLegTwistInOut = 25,
LeftFootUpDown = 26,
LeftFootTwistInOut = 27,
LeftToesUpDown = 28,
RightUpperLegFrontBack = 29,
RightUpperLegInOut = 30,
RightUpperLegTwistInOut = 31,
RightLowerLegStretch = 32,
RightLowerLegTwistInOut = 33,
RightFootUpDown = 34,
RightFootTwistInOut = 35,
RightToesUpDown = 36,
LeftShoulderDownUp = 37,
LeftShoulderFrontBack = 38,
LeftArmDownUp = 39,
LeftArmFrontBack = 40,
LeftArmTwistInOut = 41,
LeftForearmStretch = 42,
LeftForearmTwistInOut = 43,
LeftHandDownUp = 44,
LeftHandInOut = 45,
RightShoulderDownUp = 46,
RightShoulderFrontBack = 47,
RightArmDownUp = 48,
RightArmFrontBack = 49,
RightArmTwistInOut = 50,
RightForearmStretch = 51,
RightForearmTwistInOut = 52,
RightHandDownUp = 53,
RightHandInOut = 54,
Last,
}
public static class MuscleTypeExtensions
{
public static MuscleType Update(this MuscleType _this, int[] version)
{
if (!(version[0] > 5 || (version[0] == 5 && version[1] >= 6)))
{
if (_this >= MuscleType.UpperchestFrontBack)
{
_this += 3;
}
}
return _this;
}
public static string ToAttributeString(this MuscleType _this)
{
switch (_this)
{
case MuscleType.SpineFrontBack:
return "Spine Front-Back";
case MuscleType.SpineLeftRight:
return "Spine Left-Right";
case MuscleType.SpineTwistLeftRight:
return "Spine Twist Left-Right";
case MuscleType.ChestFrontBack:
return "Chest Front-Back";
case MuscleType.ChestLeftRight:
return "Chest Left-Right";
case MuscleType.ChestTwistLeftRight:
return "Chest Twist Left-Right";
case MuscleType.UpperchestFrontBack:
return "UpperChest Front-Back";
case MuscleType.UpperchestLeftRight:
return "UpperChest Left-Right";
case MuscleType.UpperchestTwisLeftRight:
return "UpperChest Twist Left-Right";
case MuscleType.NeckNodDownUp:
return "Neck Nod Down-Up";
case MuscleType.NeckTiltLeftRight:
return "Neck Tilt Left-Right";
case MuscleType.NeckTurnLeftRight:
return "Neck Turn Left-Right";
case MuscleType.HeadNodDownUp:
return "Head Nod Down-Up";
case MuscleType.HeadTiltLeftRight:
return "Head Tilt Left-Right";
case MuscleType.HeadTurnLeftRight:
return "Head Turn Left-Right";
case MuscleType.LeftEyeDownUp:
return "Left Eye Down-Up";
case MuscleType.LeftEyeInOut:
return "Left Eye In-Out";
case MuscleType.RightEyeDownUp:
return "Right Eye Down-Up";
case MuscleType.RightEyeInOut:
return "Right Eye In-Out";
case MuscleType.JawClose:
return "Jaw Close";
case MuscleType.JawLeftRight:
return "Jaw Left-Right";
case MuscleType.LeftUpperLegFrontBack:
return "Left Upper Leg Front-Back";
case MuscleType.LeftUpperLegInOut:
return "Left Upper Leg In-Out";
case MuscleType.LeftUpperLegTwistInOut:
return "Left Upper Leg Twist In-Out";
case MuscleType.LeftLowerLegStretch:
return "Left Lower Leg Stretch";
case MuscleType.LeftLowerLegTwistInOut:
return "Left Lower Leg Twist In-Out";
case MuscleType.LeftFootUpDown:
return "Left Foot Up-Down";
case MuscleType.LeftFootTwistInOut:
return "Left Foot Twist In-Out";
case MuscleType.LeftToesUpDown:
return "Left Toes Up-Down";
case MuscleType.RightUpperLegFrontBack:
return "Right Upper Leg Front-Back";
case MuscleType.RightUpperLegInOut:
return "Right Upper Leg In-Out";
case MuscleType.RightUpperLegTwistInOut:
return "Right Upper Leg Twist In-Out";
case MuscleType.RightLowerLegStretch:
return "Right Lower Leg Stretch";
case MuscleType.RightLowerLegTwistInOut:
return "Right Lower Leg Twist In-Out";
case MuscleType.RightFootUpDown:
return "Right Foot Up-Down";
case MuscleType.RightFootTwistInOut:
return "Right Foot Twist In-Out";
case MuscleType.RightToesUpDown:
return "Right Toes Up-Down";
case MuscleType.LeftShoulderDownUp:
return "Left Shoulder Down-Up";
case MuscleType.LeftShoulderFrontBack:
return "Left Shoulder Front-Back";
case MuscleType.LeftArmDownUp:
return "Left Arm Down-Up";
case MuscleType.LeftArmFrontBack:
return "Left Arm Front-Back";
case MuscleType.LeftArmTwistInOut:
return "Left Arm Twist In-Out";
case MuscleType.LeftForearmStretch:
return "Left Forearm Stretch";
case MuscleType.LeftForearmTwistInOut:
return "Left Forearm Twist In-Out";
case MuscleType.LeftHandDownUp:
return "Left Hand Down-Up";
case MuscleType.LeftHandInOut:
return "Left Hand In-Out";
case MuscleType.RightShoulderDownUp:
return "Right Shoulder Down-Up";
case MuscleType.RightShoulderFrontBack:
return "Right Shoulder Front-Back";
case MuscleType.RightArmDownUp:
return "Right Arm Down-Up";
case MuscleType.RightArmFrontBack:
return "Right Arm Front-Back";
case MuscleType.RightArmTwistInOut:
return "Right Arm Twist In-Out";
case MuscleType.RightForearmStretch:
return "Right Forearm Stretch";
case MuscleType.RightForearmTwistInOut:
return "Right Forearm Twist In-Out";
case MuscleType.RightHandDownUp:
return "Right Hand Down-Up";
case MuscleType.RightHandInOut:
return "Right Hand In-Out";
default:
throw new ArgumentException(_this.ToString());
}
}
}
public enum BoneType
{
Hips = 0,
LeftUpperLeg = 1,
RightUpperLeg = 2,
LeftLowerLeg = 3,
RightLowerLeg = 4,
LeftFoot = 5,
RightFoot = 6,
Spine = 7,
Chest = 8,
UpperChest = 9,
Neck = 10,
Head = 11,
LeftShoulder = 12,
RightShoulder = 13,
LeftUpperArm = 14,
RightUpperArm = 15,
LeftLowerArm = 16,
RightLowerArm = 17,
LeftHand = 18,
RightHand = 19,
LeftToes = 20,
RightToes = 21,
LeftEye = 22,
RightEye = 23,
Jaw = 24,
Last,
}
public static class BoneTypeExtensions
{
public static BoneType Update(this BoneType _this, int[] version)
{
if (version[0] > 5 || (version[0] == 5 && version[1] >= 6))
{
if (_this >= BoneType.UpperChest)
{
_this++;
}
}
return _this;
}
public static string ToAttributeString(this BoneType _this)
{
if (_this < BoneType.Last)
{
return _this.ToString();
}
throw new ArgumentException(_this.ToString());
}
}
public enum TransformType
{
Translation = 1,
Rotation = 2,
Scaling = 3,
EulerRotation = 4,
}
public static class BindingTypeExtensions
{
public static bool IsValid(this TransformType _this)
{
return _this >= TransformType.Translation && _this <= TransformType.EulerRotation;
}
public static int GetDimension(this TransformType _this)
{
switch (_this)
{
case TransformType.Translation:
case TransformType.Scaling:
case TransformType.EulerRotation:
return 3;
case TransformType.Rotation:
return 4;
default:
throw new NotImplementedException($"Binding type {_this} is not implemented");
}
}
}
public enum ArmType
{
LeftHand = 0,
RightHand = 1,
Last,
}
public static class ArmTypeExtensions
{
public static BoneType ToBoneType(this ArmType _this)
{
switch (_this)
{
case ArmType.LeftHand:
return BoneType.LeftHand;
case ArmType.RightHand:
return BoneType.RightHand;
default:
throw new ArgumentException(_this.ToString());
}
}
}
public enum FingerType
{
Thumb = 0,
Index = 1,
Middle = 2,
Ring = 3,
Little = 4,
Last,
}
public static class FingerTypeExtensions
{
public static string ToAttributeString(this FingerType _this)
{
if (_this < FingerType.Last)
{
return _this.ToString();
}
throw new ArgumentException(_this.ToString());
}
}
public enum FingerDoFType
{
_1Stretched = 0,
Spread = 1,
_2Stretched = 2,
_3Stretched = 3,
Last,
}
public static class FingerDoFTypeExtensions
{
public static string ToAttributeString(this FingerDoFType _this)
{
switch (_this)
{
case FingerDoFType._1Stretched:
return "1 Stretched";
case FingerDoFType.Spread:
return "Spread";
case FingerDoFType._2Stretched:
return "2 Stretched";
case FingerDoFType._3Stretched:
return "3 Stretched";
default:
throw new ArgumentException(_this.ToString());
}
}
}
public enum TDoFBoneType
{
Spine = 0,
Chest = 1,
UpperChest = 2,
Neck = 3,
Head = 4,
LeftUpperLeg = 5,
LeftLowerLeg = 6,
LeftFoot = 7,
LeftToes = 8,
RightUpperLeg = 9,
RightLowerLeg = 10,
RightFoot = 11,
RightToes = 12,
LeftShoulder = 13,
LeftUpperArm = 14,
LeftLowerArm = 15,
LeftHand = 16,
RightShoulder = 17,
RightUpperArm = 18,
RightLowerArm = 19,
RightHand = 20,
Last,
}
public static class TDoFBoneTypeExtensions
{
public static TDoFBoneType Update(this TDoFBoneType _this, int[] version)
{
if (!(version[0] > 5 || (version[0] == 5 && version[1] >= 6)))
{
if (_this >= TDoFBoneType.UpperChest)
{
_this++;
}
}
if (!(version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)))
{
if (_this >= TDoFBoneType.Head)
{
_this++;
}
}
if (!(version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)))
{
if (_this >= TDoFBoneType.LeftLowerLeg)
{
_this += 3;
}
}
if (!(version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)))
{
if (_this >= TDoFBoneType.RightLowerLeg)
{
_this += 3;
}
}
if (!(version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)))
{
if (_this >= TDoFBoneType.LeftUpperArm)
{
_this += 3;
}
}
if (!(version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)))
{
if (_this >= TDoFBoneType.RightUpperArm)
{
_this += 3;
}
}
return _this;
}
public static BoneType ToBoneType(this TDoFBoneType _this)
{
switch (_this)
{
case TDoFBoneType.Spine:
return BoneType.Spine;
case TDoFBoneType.Chest:
return BoneType.Chest;
case TDoFBoneType.UpperChest:
return BoneType.UpperChest;
case TDoFBoneType.Neck:
return BoneType.Neck;
case TDoFBoneType.Head:
return BoneType.Head;
case TDoFBoneType.LeftUpperLeg:
return BoneType.LeftUpperLeg;
case TDoFBoneType.LeftLowerLeg:
return BoneType.LeftLowerLeg;
case TDoFBoneType.LeftFoot:
return BoneType.LeftFoot;
case TDoFBoneType.LeftToes:
return BoneType.LeftToes;
case TDoFBoneType.RightUpperLeg:
return BoneType.RightUpperLeg;
case TDoFBoneType.RightLowerLeg:
return BoneType.RightLowerLeg;
case TDoFBoneType.RightFoot:
return BoneType.RightFoot;
case TDoFBoneType.RightToes:
return BoneType.RightToes;
case TDoFBoneType.LeftShoulder:
return BoneType.LeftShoulder;
case TDoFBoneType.LeftUpperArm:
return BoneType.LeftUpperArm;
case TDoFBoneType.LeftLowerArm:
return BoneType.LeftLowerArm;
case TDoFBoneType.LeftHand:
return BoneType.LeftHand;
case TDoFBoneType.RightShoulder:
return BoneType.RightShoulder;
case TDoFBoneType.RightUpperArm:
return BoneType.RightUpperArm;
case TDoFBoneType.RightLowerArm:
return BoneType.RightLowerArm;
case TDoFBoneType.RightHand:
return BoneType.RightHand;
default:
throw new ArgumentException(_this.ToString());
}
}
}
}