feat(vfx): decode retail hooks and typed tables

Replace the incomplete package path with one DatCollection-backed compatibility seam for PhysicsScripts and Animations. Preserve CreateBlockingParticle's inherited payload and following cursor, route every production and audit consumer through the corrected loaders, and apply retail's post-UnPack StartTime ordering.

Add exact stored-order PhysicsScriptTable upper-threshold resolution, high-byte DID and embedded-ID validation, plus live effect profiles with Setup-to-PhysicsDesc precedence across top-level and attached entity lifetimes. Keep blocking execution deferred and narrow TS-11 accordingly.

Pin synthetic malformed/cursor/order fixtures, installed-DAT blocking and recall audits, high-index IDs, IEEE boundaries, profile teardown, and ordinary decoder parity; synchronize architecture, inventory, milestones, roadmap, research, and memory.

Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
Erik 2026-07-14 08:17:44 +02:00
parent 8dd996053d
commit 363e046112
31 changed files with 1102 additions and 73 deletions

View file

@ -0,0 +1,117 @@
using AcDream.Core.Vfx;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Enums;
using DatReaderWriter.Types;
namespace AcDream.Core.Tests.Vfx;
public sealed class PhysicsScriptTableResolverTests
{
private const uint TableDid = 0x34000005u;
private const uint RawType = 0x00000016u;
[Fact]
public void Resolve_UsesFirstStoredUpperThresholdWithoutSorting()
{
PhysicsScriptTable table = BuildTable(
(1.0f, 0x33000011u),
(0.0f, 0x33000022u));
var resolver = new PhysicsScriptTableResolver(
id => id == TableDid ? table : null);
Assert.Equal(0x33000011u, resolver.Resolve(TableDid, RawType, -0.25f));
Assert.Equal(0x33000011u, resolver.Resolve(TableDid, RawType, 0.0f));
}
[Fact]
public void Resolve_EqualityAndDuplicateThresholdChooseFirst()
{
PhysicsScriptTable table = BuildTable(
(0.5f, 0x33000031u),
(0.5f, 0x33000032u),
(1.0f, 0x33000033u));
var resolver = new PhysicsScriptTableResolver(_ => table);
Assert.Equal(0x33000031u, resolver.Resolve(TableDid, RawType, 0.5f));
Assert.Equal(0x33000033u, resolver.Resolve(TableDid, RawType, 0.75f));
}
[Fact]
public void Resolve_PreservesRetailIeeeComparisonBoundaries()
{
PhysicsScriptTable table = BuildTable(
(0.0f, 0x33000041u),
(1.0f, 0x33000042u));
var resolver = new PhysicsScriptTableResolver(_ => table);
Assert.Equal(0x33000041u,
resolver.Resolve(TableDid, RawType, float.NegativeInfinity));
Assert.Null(resolver.Resolve(TableDid, RawType, float.PositiveInfinity));
Assert.Null(resolver.Resolve(TableDid, RawType, float.NaN));
Assert.Null(resolver.Resolve(TableDid, RawType, 1.0001f));
}
[Fact]
public void Resolve_MissingOrInvalidInputsReturnNoScript()
{
PhysicsScriptTable invalidScript = BuildTable((1.0f, 0x32000001u));
var resolver = new PhysicsScriptTableResolver(
id => id == TableDid ? invalidScript : null);
Assert.Null(resolver.Resolve(0u, RawType, 0f));
Assert.Null(resolver.Resolve(0x35000001u, RawType, 0f));
Assert.Null(resolver.Resolve(TableDid, 0xA5A5A5A5u, 0f));
Assert.Null(resolver.Resolve(TableDid, RawType, 0f));
}
[Fact]
public void DidValidation_UsesRetailHighByteAndAcceptsLargeIndexes()
{
Assert.True(PhysicsScriptTableResolver.IsPhysicsScriptDid(0x33010000u));
Assert.True(PhysicsScriptTableResolver.IsPhysicsScriptDid(0x33FFFFFFu));
Assert.True(PhysicsScriptTableResolver.IsPhysicsScriptTableDid(0x34010000u));
Assert.True(PhysicsScriptTableResolver.IsPhysicsScriptTableDid(0x34FFFFFFu));
Assert.False(PhysicsScriptTableResolver.IsPhysicsScriptDid(0x34010000u));
Assert.False(PhysicsScriptTableResolver.IsPhysicsScriptTableDid(0x33010000u));
}
[Fact]
public void Resolve_AcceptsHighIndexTableAndScriptIds()
{
const uint highTableDid = 0x34010000u;
PhysicsScriptTable table = BuildTable((1.0f, 0x33010000u));
table.Id = highTableDid;
var resolver = new PhysicsScriptTableResolver(
id => id == highTableDid ? table : null);
Assert.Equal(0x33010000u, resolver.Resolve(highTableDid, RawType, 0f));
}
[Fact]
public void Resolve_RejectsMismatchedEmbeddedTableId()
{
PhysicsScriptTable mismatched = BuildTable((1.0f, 0x33000051u));
mismatched.Id = 0x34000006u;
var resolver = new PhysicsScriptTableResolver(_ => mismatched);
Assert.Null(resolver.Resolve(TableDid, RawType, 0f));
}
private static PhysicsScriptTable BuildTable(
params (float Mod, uint ScriptDid)[] entries)
{
var data = new PhysicsScriptTableData();
foreach ((float mod, uint scriptDid) in entries)
{
data.Scripts.Add(new ScriptAndModData
{
Mod = mod,
ScriptId = scriptDid,
});
}
var table = new PhysicsScriptTable { Id = TableDid };
table.ScriptTable.Add(unchecked((PlayScript)RawType), data);
return table;
}
}