tests/SmartPort/Parser/RecoveryTests.cs
using HumanoidRetargeter.Editor;
using HumanoidRetargeterCompression;
using HumanoidRetargeterVrf;
using Xunit;
namespace SmartPort.Parser.Tests;
public class RecoveryTests
{
[Fact]
public void EmbeddedZstdDecodesFrameWithoutNativeLibrary()
{
using var decoder = new HumanoidRetargeterZstd.Decompressor();
byte[] frame = [0x28, 0xB5, 0x2F, 0xFD, 0x20, 4, 0x21, 0, 0, 116, 101, 115, 116];
var output = new byte[4];
Assert.True(decoder.TryUnwrap(frame, output, out var count));
Assert.Equal(4, count);
Assert.Equal("test", System.Text.Encoding.ASCII.GetString(output));
}
[Theory]
[InlineData(0x2FD82220u, .068791926f, .017197981f, .99748284f)]
[InlineData(0x30080230u, .104684785f, 0f, .99450547f)]
[InlineData(0x000FFFFFu, 0f, 0f, -1f)]
public void SboxPackedNormalsMatchNativeRecompiledFixture(uint packed, float x, float y, float z)
{
var buffer = new HumanoidRetargeterVrf.Blocks.VBIB.OnDiskBufferData
{
ElementCount = 1, ElementSizeInBytes = 4, Data = BitConverter.GetBytes(packed)
};
var attribute = new HumanoidRetargeterVrf.Blocks.VBIB.RenderInputLayoutField
{
SemanticName = "NORMAL", Format = HumanoidRetargeterVrf.DXGI_FORMAT.R10G10B10A2_UNORM
};
var (normals, tangents) = HumanoidRetargeterVrf.Blocks.VBIB.GetNormalTangentArray(buffer, attribute);
Assert.True(System.Numerics.Vector3.Distance(normals[0], new(x, y, z)) < .002f);
Assert.Empty(tangents); // ModelDoc regenerates tangents from UVs.
}
[Theory]
[InlineData(90, 123, 26)]
[InlineData(-90, -123, 26)]
[InlineData(89.99999, -50, 80)]
[InlineData(-30, 55, -110)]
public void RecoveredEulerAnglesPreserveRotationAtPoles(float pitch, float yaw, float roll)
{
static System.Numerics.Quaternion Rotation(float p, float y, float r) =>
System.Numerics.Quaternion.CreateFromAxisAngle(System.Numerics.Vector3.UnitZ, y * MathF.PI / 180)
* System.Numerics.Quaternion.CreateFromAxisAngle(System.Numerics.Vector3.UnitY, p * MathF.PI / 180)
* System.Numerics.Quaternion.CreateFromAxisAngle(System.Numerics.Vector3.UnitX, r * MathF.PI / 180);
var original = Rotation(pitch, yaw, roll);
var angles = HumanoidRetargeterVrf.IO.ModelExtract.ToEulerAngles(original);
var actual = Rotation(angles.X, angles.Y, angles.Z);
Assert.True(MathF.Abs(System.Numerics.Quaternion.Dot(original, actual)) > .999999f);
}
[Fact]
public void Lz4DecodesLiteralsOverlapsAndChainedHistory()
{
byte[] output = new byte[8];
Assert.Equal(8, Lz4.Decode(new byte[] { 0x40, 97, 98, 99, 100, 4, 0 }, output));
Assert.Equal("abcdabcd", System.Text.Encoding.ASCII.GetString(output));
Assert.Equal(8, Lz4.Decode(new byte[] { 0x13, 97, 1, 0 }, output));
Assert.Equal("aaaaaaaa", System.Text.Encoding.ASCII.GetString(output));
using var chain = new Lz4Chain(65536, 0);
Assert.True(chain.DecodeAndDrain(new byte[] { 0x40, 97, 98, 99, 100 }, output, out var count));
Assert.Equal(4, count);
Assert.True(chain.DecodeAndDrain(new byte[] { 0, 4, 0 }, output, out count));
Assert.Equal(4, count);
Assert.Equal("abcd", System.Text.Encoding.ASCII.GetString(output, 0, count));
}
[Theory]
[InlineData(new byte[] { 0xF0 })]
[InlineData(new byte[] { 0, 0, 0 })]
[InlineData(new byte[] { 0, 1, 0 })]
[InlineData(new byte[] { 0x40, 1 })]
public void Lz4RejectsTruncatedAndInvalidInput(byte[] input)
=> Assert.Throws<InvalidDataException>(() => Lz4.Decode(input, new byte[16]));
[Fact]
public void Lz4RejectsOutputOverflow()
=> Assert.Throws<InvalidDataException>(() => Lz4.Decode(new byte[] { 0x20, 1, 2 }, new byte[1]));
[Theory]
[InlineData("../escape.dmx")]
[InlineData("nested/../../escape.dmx")]
[InlineData("C:/escape.dmx")]
[InlineData("file.dmx:alternate")]
public void RecoveredPathsCannotEscapeOutput(string relative)
=> Assert.Throws<InvalidDataException>(() => CompiledAssetRecovery.ConfinedPath(Path.Combine(Path.GetTempPath(), "smart-port-test"), relative));
[Fact]
public void InvalidResourceHeaderIsRejected()
{
var path = Path.GetTempFileName();
try
{
File.WriteAllBytes(path, new byte[32]);
using var resource = new Resource();
Assert.Throws<InvalidDataException>(() => resource.Read(path));
}
finally { File.Delete(path); }
}
}