Establish the executable-backed PhysicsDesc, sequence-gate, PhysicsScript, CreateBlocking, particle-anchor, projectile, and Hidden-state behavior before changing runtime code. Correct stale blocking/threshold claims and synchronize the project instructions with the current UI architecture and matching retail binary. Add copyright-safe packet and DAT-container fixtures plus a failing installed-DAT conformance audit for projectile shapes, typed tables, recall motion, default scripts, and raw CreateBlocking inventory. Co-Authored-By: Codex <noreply@openai.com>
135 lines
5.6 KiB
C#
135 lines
5.6 KiB
C#
using System.Buffers.Binary;
|
|
|
|
namespace AcDream.Content.Tests.Vfx;
|
|
|
|
/// <summary>
|
|
/// Synthetic raw DAT fixtures captured from the retail hook schema audit.
|
|
/// They contain no installed Turbine DAT bytes. Step 3 consumes the blocking
|
|
/// hook followed by an ordinary hook to prove cursor alignment.
|
|
/// </summary>
|
|
internal static class ProjectileVfxDatFixtures
|
|
{
|
|
internal static readonly byte[] PhysicsScriptCreateBlockingThenAnimationDone = BuildPhysicsScript();
|
|
internal static readonly byte[] AnimationCreateBlockingThenAnimationDone = BuildAnimation();
|
|
|
|
private static byte[] BuildPhysicsScript()
|
|
{
|
|
var bytes = new byte[80];
|
|
int pos = 0;
|
|
|
|
WriteU32(0x3300F001u); // synthetic PhysicsScript DID
|
|
WriteU32(2); // two PhysicsScriptData entries
|
|
|
|
WriteF64(0.0);
|
|
WriteU32(0x1Au); // CreateBlockingParticle
|
|
WriteU32(0); // AnimationHookDir.Both
|
|
WriteU32(0x3200F001u); // emitter DID
|
|
WriteU32(0xFFFFFFFFu); // root attachment, distinct from part zero
|
|
WriteF32(1.0f); WriteF32(2.0f); WriteF32(3.0f); // offset origin
|
|
WriteF32(0.5f); WriteF32(0.5f); WriteF32(-0.5f); WriteF32(-0.5f); // WXYZ quaternion
|
|
WriteU32(7); // nonzero logical emitter ID
|
|
|
|
WriteF64(1.25);
|
|
WriteU32(0x04u); // AnimationDone: proves the next cursor
|
|
WriteU32(1); // AnimationHookDir.Forward
|
|
|
|
if (pos != bytes.Length) throw new InvalidOperationException($"Fixture length mismatch: {pos}");
|
|
return bytes;
|
|
|
|
void WriteU32(uint value)
|
|
{
|
|
BinaryPrimitives.WriteUInt32LittleEndian(bytes.AsSpan(pos), value);
|
|
pos += 4;
|
|
}
|
|
|
|
void WriteF32(float value)
|
|
{
|
|
BinaryPrimitives.WriteSingleLittleEndian(bytes.AsSpan(pos), value);
|
|
pos += 4;
|
|
}
|
|
|
|
void WriteF64(double value)
|
|
{
|
|
BinaryPrimitives.WriteDoubleLittleEndian(bytes.AsSpan(pos), value);
|
|
pos += 8;
|
|
}
|
|
}
|
|
|
|
private static byte[] BuildAnimation()
|
|
{
|
|
// Animation header: DID, flags, NumParts=0, NumFrames=1. The sole
|
|
// AnimationFrame then has hookCount=2 and the same two hook records as
|
|
// the PhysicsScript fixture. No position or part frames are present.
|
|
var bytes = new byte[76];
|
|
int pos = 0;
|
|
|
|
WriteU32(0x0300F001u); // synthetic Animation DID
|
|
WriteU32(0); // no PosFrames
|
|
WriteU32(0); // NumParts
|
|
WriteU32(1); // NumFrames
|
|
WriteU32(2); // AnimationFrame hook count
|
|
|
|
WriteU32(0x1Au); // CreateBlockingParticle
|
|
WriteU32(0); // AnimationHookDir.Both
|
|
WriteU32(0x3200F002u); // emitter DID
|
|
WriteU32(0); // part zero, distinct from the PES root fixture
|
|
WriteF32(-1.0f); WriteF32(4.0f); WriteF32(0.25f); // offset origin
|
|
WriteF32(1.0f); WriteF32(0.0f); WriteF32(0.0f); WriteF32(0.0f); // WXYZ identity
|
|
WriteU32(9); // nonzero logical emitter ID
|
|
|
|
WriteU32(0x04u); // AnimationDone at the exact next cursor
|
|
WriteU32(0xFFFFFFFFu); // AnimationHookDir.Backward
|
|
|
|
if (pos != bytes.Length) throw new InvalidOperationException($"Fixture length mismatch: {pos}");
|
|
return bytes;
|
|
|
|
void WriteU32(uint value)
|
|
{
|
|
BinaryPrimitives.WriteUInt32LittleEndian(bytes.AsSpan(pos), value);
|
|
pos += 4;
|
|
}
|
|
|
|
void WriteF32(float value)
|
|
{
|
|
BinaryPrimitives.WriteSingleLittleEndian(bytes.AsSpan(pos), value);
|
|
pos += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
public sealed class ProjectileVfxDatFixtureTests
|
|
{
|
|
[Fact]
|
|
public void BlockingThenOrdinaryHook_PinsInheritedPayloadAndNextCursor()
|
|
{
|
|
ReadOnlySpan<byte> bytes = ProjectileVfxDatFixtures.PhysicsScriptCreateBlockingThenAnimationDone;
|
|
|
|
Assert.Equal(80, bytes.Length);
|
|
Assert.Equal(0x3300F001u, BinaryPrimitives.ReadUInt32LittleEndian(bytes));
|
|
Assert.Equal(2u, BinaryPrimitives.ReadUInt32LittleEndian(bytes[4..]));
|
|
Assert.Equal(0x1Au, BinaryPrimitives.ReadUInt32LittleEndian(bytes[16..]));
|
|
Assert.Equal(0x3200F001u, BinaryPrimitives.ReadUInt32LittleEndian(bytes[24..]));
|
|
Assert.Equal(0xFFFFFFFFu, BinaryPrimitives.ReadUInt32LittleEndian(bytes[28..]));
|
|
Assert.Equal(7u, BinaryPrimitives.ReadUInt32LittleEndian(bytes[60..]));
|
|
Assert.Equal(1.25, BinaryPrimitives.ReadDoubleLittleEndian(bytes[64..]));
|
|
Assert.Equal(0x04u, BinaryPrimitives.ReadUInt32LittleEndian(bytes[72..]));
|
|
Assert.Equal(1u, BinaryPrimitives.ReadUInt32LittleEndian(bytes[76..]));
|
|
}
|
|
|
|
[Fact]
|
|
public void AnimationContainer_BlockingThenOrdinaryHookPinsNextCursor()
|
|
{
|
|
ReadOnlySpan<byte> bytes = ProjectileVfxDatFixtures.AnimationCreateBlockingThenAnimationDone;
|
|
|
|
Assert.Equal(76, bytes.Length);
|
|
Assert.Equal(0x0300F001u, BinaryPrimitives.ReadUInt32LittleEndian(bytes));
|
|
Assert.Equal(0u, BinaryPrimitives.ReadUInt32LittleEndian(bytes[8..]));
|
|
Assert.Equal(1u, BinaryPrimitives.ReadUInt32LittleEndian(bytes[12..]));
|
|
Assert.Equal(2u, BinaryPrimitives.ReadUInt32LittleEndian(bytes[16..]));
|
|
Assert.Equal(0x1Au, BinaryPrimitives.ReadUInt32LittleEndian(bytes[20..]));
|
|
Assert.Equal(0x3200F002u, BinaryPrimitives.ReadUInt32LittleEndian(bytes[28..]));
|
|
Assert.Equal(9u, BinaryPrimitives.ReadUInt32LittleEndian(bytes[64..]));
|
|
Assert.Equal(0x04u, BinaryPrimitives.ReadUInt32LittleEndian(bytes[68..]));
|
|
Assert.Equal(0xFFFFFFFFu, BinaryPrimitives.ReadUInt32LittleEndian(bytes[72..]));
|
|
}
|
|
}
|