using System.Runtime.InteropServices;
namespace XFEWarframe.Bridge;
internal static unsafe partial class ExperimentalAbilityRuntime
{
// Exercises the production callbacks across the real unmanaged calling boundary.
// The fixture models the audited layouts; this does not substitute for gameplay validation.
internal static uint SelfTest()
{
if (Available) return 10; // Only run in the standalone installer self-test process.
var memory = (nint)NativeMemory.AllocZeroed(0x9000);
try
{
var reader = memory; var table = memory + 0x100; var a = memory + 0x400;
var b = memory + 0x800; var c = memory + 0xC00; var suit = memory + 0x1000;
*(nint*)reader = table; *(byte*)(reader + 10) = 1;
*(nint*)(table + 0x138) = (nint)(delegate* unmanaged<nint, nint, void>)&TestName;
*(nint*)(table + 0x158) = (nint)(delegate* unmanaged<nint, byte, void>)&TestType;
*(nint*)(table + 0x48) = (nint)(delegate* unmanaged<nint, byte>)&TestExists;
*(nint*)(table + 0x58) = (nint)(delegate* unmanaged<nint, void>)&TestBegin;
*(nint*)(table + 0x60) = (nint)(delegate* unmanaged<nint, void>)&TestEnd;
*(nint*)(table + 0xD0) = (nint)(delegate* unmanaged<nint, byte*, void>)&TestKing;
serialize = (nint)(delegate* unmanaged<nint, nint, void>)&TestSerialize;
typeSerialize = (nint)(delegate* unmanaged<nint*, nint, nint, void>)&TestReadAbility;
validate = (nint)(delegate* unmanaged<nint*, nint>)&TestValidate;
copy = move = assign = moveAssign = (nint)(delegate* unmanaged<nint, nint, nint>)&TestCopy;
destroy = suitDestroy = (nint)(delegate* unmanaged<nint, void>)&TestDestroy;
buildEquippedItem = (nint)(delegate* unmanaged<nint, nint, nint, byte, byte, nint, nint, nint, byte, byte, void>)&TestBuildEquippedItem;
set = (nint)(delegate* unmanaged<nint, nint, void>)&TestSet;
apply = (nint)(delegate* unmanaged<nint, void>)&TestApply;
initialize = (nint)(delegate* unmanaged<nint, void>)&TestInitialize;
rank = (nint)(delegate* unmanaged<nint, int>)&TestRank;
*(nint*)(a + OverrideOffset) = 101; *(byte*)(a + OverrideOffset + 8) = 0;
for (var i = 0; i < 4; i++) *(nint*)(reader + 32 + i * 8) = 101 + i;
((delegate* unmanaged<nint, nint, void>)&Serialize)(a, reader);
if (!Configs.TryGetValue(a, out var plan) || plan.Abilities[3] != 104 || *(int*)(reader + 24) != 0) return 11;
*(byte*)(reader + 96) = 1;
((delegate* unmanaged<nint, nint, void>)&Serialize)(a, reader);
if (!KingConfigs.Contains(a)) return 31;
// Configs are also cloned through serializers, not only C++ copy constructors.
*(byte*)(reader + 10) = 0;
for (var i = 0; i < 4; i++) *(nint*)(reader + 32 + i * 8) = 0;
((delegate* unmanaged<nint, nint, void>)&Serialize)(a, reader);
if (*(nint*)(reader + 32) != 101 || *(nint*)(reader + 56) != 104 || *(int*)(reader + 24) != 0) return 20;
*(nint*)(b + OverrideOffset) = 101; *(byte*)(b + OverrideOffset + 8) = 0;
*(byte*)(reader + 10) = 1;
((delegate* unmanaged<nint, nint, void>)&Serialize)(b, reader);
if (!Configs.TryGetValue(b, out var roundTrip) || !roundTrip.Abilities.SequenceEqual(plan.Abilities)) return 21;
((delegate* unmanaged<nint, nint, nint>)&Copy)(b, a);
((delegate* unmanaged<nint, nint, nint>)&Move)(c, b);
if (!Configs.ContainsKey(a) || !Configs.ContainsKey(c) || Configs.ContainsKey(b)) return 12;
if (!KingConfigs.Contains(a) || !KingConfigs.Contains(c) || KingConfigs.Contains(b)) return 32;
((delegate* unmanaged<nint, nint, nint>)&Assign)(b, c);
((delegate* unmanaged<nint, nint, nint>)&MoveAssign)(c, c);
if (!Configs.ContainsKey(b) || !Configs.ContainsKey(c)) return 13;
var types = memory + 0x6000; var reference = memory + 0x6100; var instances = memory + 0x6200;
*(nint*)reference = 1;
for (var i = 0; i < 4; i++) { *(nint*)(types + i * 16) = reference; *(nint*)(types + i * 16 + 8) = 11 + i; }
*(nint*)(suit + 0x3418) = types; *(int*)(suit + 0x3420) = 64; *(nint*)(suit + 0x3448) = instances;
((delegate* unmanaged<nint, nint, void>)&Set)(suit, c + OverrideOffset);
((delegate* unmanaged<nint, void>)&Initialize)(suit);
// Rank processing must observe all four replacements, not the defaults.
if (*(int*)(suit + 0x4000) != 410 || *(nint*)(suit + SuitOverrideOffset) != 101) return 14;
for (var i = 0; i < 4; i++) if (*(nint*)(instances + i * 8) != 101 + i) return 15;
// Switching to another config with the SAME primary must clear the extra slots.
Configs.Remove(b);
((delegate* unmanaged<nint, nint, void>)&Set)(suit, b + OverrideOffset);
if (*(nint*)instances != 101 || *(nint*)(instances + 8) != 12 || *(nint*)(instances + 24) != 14 || Suits.ContainsKey(suit)) return 16;
((delegate* unmanaged<nint, nint, void>)&Set)(suit, c + OverrideOffset);
if (*(nint*)(instances + 24) != 104) return 17;
// Actual game loadouts merge MOD and appearance configs field by field.
// A and C deliberately share the same primary, and appearance stays on C.
var items = memory + 0x6500; var sourceItem = memory + 0x6600; var equipped = memory + 0x6800;
var sourceConfigs = memory + 0x7000; var merged = memory + 0x7C00;
*(nint*)items = sourceItem; *(uint*)(items + 8) = 0x130;
*(ulong*)(sourceItem + 0x20) = *(ulong*)(equipped + 0x20) = 1234;
*(uint*)(sourceItem + 0x28) = *(uint*)(equipped + 0x28) = 5678;
*(nint*)(sourceItem + 0x68) = sourceConfigs; *(uint*)(sourceItem + 0x70) = 3 * 0x330;
*(nint*)(equipped + 0x68) = merged; *(nuint*)(equipped + 0x138) = 2;
*(nint*)(sourceConfigs + OverrideOffset) = *(nint*)(sourceConfigs + 2 * 0x330 + OverrideOffset) = 101;
Configs[sourceConfigs] = plan with { Abilities = [101, 102, 0, 0] };
Configs[sourceConfigs + 2 * 0x330] = plan;
for (nuint i = 0; i < 3; i++)
{
*(nuint*)(equipped + 0x130) = i;
((delegate* unmanaged<nint, nint, nint, byte, byte, nint, nint, nint, byte, byte, void>)&BuildEquippedItem)
(17, 18, 19, 0, 0, items, 20, equipped, 1, 1);
var expected = i == 0 ? 2 : i == 1 ? 0 : 4;
if ((Configs.TryGetValue(merged, out var selected) ? selected.Abilities.Count(x => x != 0) : 0) != expected ||
*(int*)(equipped + 0x150) != 1) return 22;
}
// Wrong ID or Conclave must never recover a plan based on the shared primary.
*(uint*)(equipped + 0x28) = 999;
if (SelectedPlan(items, equipped, 0) is not null) return 23;
*(uint*)(equipped + 0x28) = 5678;
if (SelectedPlan(items, equipped, 3) is not null) return 24;
Configs.Remove(sourceConfigs); Configs.Remove(sourceConfigs + 2 * 0x330); Configs.Remove(merged);
// Four banks, three choices each, across the production JSON callbacks.
*(int*)(reader + 64) = 1;
*(nint*)(reader + 80) = memory + 0x8500;
for (var slot = 0; slot < 4; slot++)
for (var choice = 0; choice < 3; choice++)
*(nint*)(memory + 0x8500 + (slot * 3 + choice) * 8) = 101 + slot + choice * 10;
((delegate* unmanaged<nint, nint, void>)&Serialize)(c, reader);
if (!Configs.TryGetValue(c, out var bankPlan) || bankPlan.Banks?[3].Length != 3 || bankPlan.Banks[3][2] != 124) return 25;
new Span<byte>((void*)(memory + 0x8500), 96).Clear();
*(byte*)(reader + 10) = 0;
((delegate* unmanaged<nint, nint, void>)&Serialize)(c, reader);
if (*(nint*)(memory + 0x8500 + 11 * 8) != 124 || *(int*)(reader + 24) != 0) return 26;
*(byte*)(reader + 10) = 1;
((delegate* unmanaged<nint, nint, void>)&Serialize)(b, reader);
((delegate* unmanaged<nint, nint, nint>)&Assign)(a, b);
if (!Configs.TryGetValue(a, out var copiedBanks) || copiedBanks.Banks?[0][1] != 111) return 27;
// A missing middle choice must not silently renumber the later skill.
*(nint*)(memory + 0x8500 + 8) = 0;
((delegate* unmanaged<nint, nint, void>)&Serialize)(c, reader);
if (Configs.ContainsKey(c) || *(int*)(reader + 24) != 0) return 28;
*(int*)(reader + 64) = 0;
*(byte*)(reader + 96) = 0;
Configs.Remove(b);
// Reusing a config address without the extension must erase its cached plan.
*(nint*)(reader + 32) = 0;
((delegate* unmanaged<nint, nint, void>)&Serialize)(c, reader);
if (Configs.ContainsKey(c) || *(int*)(reader + 24) != 0) return 18;
((delegate* unmanaged<nint, void>)&Destroy)(a);
((delegate* unmanaged<nint, void>)&SuitDestroy)(suit);
if (Configs.Count != 0 || Suits.Count != 0) return 19;
return 0;
}
finally
{
Configs.Clear(); Suits.Clear(); BankStates.Clear(); KingConfigs.Clear(); KingSuits.Clear(); NativeMemory.Free((void*)memory);
serialize = copy = move = assign = moveAssign = destroy = buildEquippedItem = set = apply = initialize = rank = typeSerialize = validate = suitDestroy = 0;
}
}
[UnmanagedCallersOnly] private static void TestName(nint reader, nint name) => *(nint*)(reader + 16) = name;
[UnmanagedCallersOnly] private static void TestType(nint reader, byte type) { }
[UnmanagedCallersOnly] private static void TestKing(nint reader, byte* value)
{
if (*(byte*)(reader + 10) == 1) *value = *(byte*)(reader + 96);
else *(byte*)(reader + 96) = *value;
}
[UnmanagedCallersOnly] private static byte TestExists(nint reader) =>
(byte)((*(nint*)(reader + 16) == Names["XFEAbilityOverrides"] && *(nint*)(reader + 32) != 0) ||
(*(int*)(reader + 64) != 0 && (*(nint*)(reader + 16) == Names["XFEAbilityBanks"] || *(int*)(reader + 72) != 0)) ? 1 : 0);
[UnmanagedCallersOnly] private static void TestBegin(nint reader)
{
++*(int*)(reader + 24);
if (*(nint*)(reader + 16) == Names["XFEAbilityBanks"]) *(int*)(reader + 72) = 1;
else if (*(int*)(reader + 72) != 0) *(int*)(reader + 68) = **(byte**)(*(nint*)(reader + 16) + 8) - (byte)'0';
}
[UnmanagedCallersOnly] private static void TestEnd(nint reader)
{
if (--*(int*)(reader + 24) == 0) *(int*)(reader + 72) = 0;
}
[UnmanagedCallersOnly] private static void TestSerialize(nint config, nint reader) { }
[UnmanagedCallersOnly] private static void TestReadAbility(nint* value, nint reader, nint root)
{
var name = *(byte**)(*(nint*)(reader + 16) + 8);
var slot = *name - (byte)'0';
if (slot is not (>= 0 and < 4)) { *value = 0; return; }
var address = *(int*)(reader + 72) == 0 ? reader + 32 + slot * 8 :
*(nint*)(reader + 80) + (*(int*)(reader + 68) * 3 + slot) * 8;
if (*(byte*)(reader + 10) == 0) *(nint*)address = *value;
else *value = *(nint*)address;
}
[UnmanagedCallersOnly] private static nint TestValidate(nint* value) => (nint)value;
[UnmanagedCallersOnly] private static nint TestCopy(nint destination, nint source)
{
*(nint*)(destination + OverrideOffset) = *(nint*)(source + OverrideOffset);
*(byte*)(destination + OverrideOffset + 8) = *(byte*)(source + OverrideOffset + 8); return destination;
}
[UnmanagedCallersOnly] private static void TestDestroy(nint value) { }
[UnmanagedCallersOnly]
private static void TestBuildEquippedItem(nint profile, nint loadout, nint inventory, byte mode, byte slot,
nint items, nint preset, nint equipped, byte force, byte option)
{
// Intentionally bypass the copy callback, as the game's flattening code does.
var source = *(nint*)(*(nint*)items + 0x68) + (nint)(*(nuint*)(equipped + 0x130) * 0x330);
var destination = *(nint*)(equipped + 0x68);
*(nint*)(destination + OverrideOffset) = *(nint*)(source + OverrideOffset);
*(byte*)(destination + OverrideOffset + 8) = *(byte*)(source + OverrideOffset + 8);
*(uint*)(equipped + 0x70) = 0x330;
*(int*)(equipped + 0x150) = profile == 17 && loadout == 18 && inventory == 19 && mode == 0 && slot == 0 &&
preset == 20 && force == 1 && option == 1 ? 1 : 0;
}
[UnmanagedCallersOnly] private static void TestSet(nint suit, nint value)
{
*(nint*)(suit + SuitOverrideOffset) = *(nint*)value;
*(byte*)(suit + SuitOverrideOffset + 8) = *(byte*)(value + 8);
if (*(int*)(suit + 0x3450) != 0) ((delegate* unmanaged<nint, void>)&TestApply)(suit);
}
[UnmanagedCallersOnly] private static void TestApply(nint suit)
{
var slot = *(byte*)(suit + SuitOverrideOffset + 8);
if (slot < 4) *(nint*)(*(nint*)(suit + 0x3448) + slot * 8) = *(nint*)(suit + SuitOverrideOffset);
}
[UnmanagedCallersOnly] private static void TestInitialize(nint suit)
{
if (*(int*)(suit + 0x3450) == 0)
{
for (var i = 0; i < 4; i++) *(nint*)(*(nint*)(suit + 0x3448) + i * 8) = 11 + i;
*(int*)(suit + 0x3450) = 32;
((delegate* unmanaged<nint, void>)&TestApply)(suit);
}
((delegate* unmanaged<nint, int>)&BeforeRanks)(suit);
}
[UnmanagedCallersOnly] private static int TestRank(nint suit)
{
var total = 0;
for (var i = 0; i < 4; i++) total += (int)*(nint*)(*(nint*)(suit + 0x3448) + i * 8);
*(int*)(suit + 0x4000) = total; return 30;
}
}
using System.Runtime.InteropServices;
namespace XFEWarframe.Bridge;
internal static unsafe partial class ExperimentalAbilityRuntime
{
// Exercises the production callbacks across the real unmanaged calling boundary.
// The fixture models the audited layouts; this does not substitute for gameplay validation.
internal static uint SelfTest()
{
if (Available) return 10; // Only run in the standalone installer self-test process.
var memory = (nint)NativeMemory.AllocZeroed(0x9000);
try
{
var reader = memory; var table = memory + 0x100; var a = memory + 0x400;
var b = memory + 0x800; var c = memory + 0xC00; var suit = memory + 0x1000;
*(nint*)reader = table; *(byte*)(reader + 10) = 1;
*(nint*)(table + 0x138) = (nint)(delegate* unmanaged<nint, nint, void>)&TestName;
*(nint*)(table + 0x158) = (nint)(delegate* unmanaged<nint, byte, void>)&TestType;
*(nint*)(table + 0x48) = (nint)(delegate* unmanaged<nint, byte>)&TestExists;
*(nint*)(table + 0x58) = (nint)(delegate* unmanaged<nint, void>)&TestBegin;
*(nint*)(table + 0x60) = (nint)(delegate* unmanaged<nint, void>)&TestEnd;
*(nint*)(table + 0xD0) = (nint)(delegate* unmanaged<nint, byte*, void>)&TestKing;
serialize = (nint)(delegate* unmanaged<nint, nint, void>)&TestSerialize;
typeSerialize = (nint)(delegate* unmanaged<nint*, nint, nint, void>)&TestReadAbility;
validate = (nint)(delegate* unmanaged<nint*, nint>)&TestValidate;
copy = move = assign = moveAssign = (nint)(delegate* unmanaged<nint, nint, nint>)&TestCopy;
destroy = suitDestroy = (nint)(delegate* unmanaged<nint, void>)&TestDestroy;
buildEquippedItem = (nint)(delegate* unmanaged<nint, nint, nint, byte, byte, nint, nint, nint, byte, byte, void>)&TestBuildEquippedItem;
set = (nint)(delegate* unmanaged<nint, nint, void>)&TestSet;
apply = (nint)(delegate* unmanaged<nint, void>)&TestApply;
initialize = (nint)(delegate* unmanaged<nint, void>)&TestInitialize;
rank = (nint)(delegate* unmanaged<nint, int>)&TestRank;
*(nint*)(a + OverrideOffset) = 101; *(byte*)(a + OverrideOffset + 8) = 0;
for (var i = 0; i < 4; i++) *(nint*)(reader + 32 + i * 8) = 101 + i;
((delegate* unmanaged<nint, nint, void>)&Serialize)(a, reader);
if (!Configs.TryGetValue(a, out var plan) || plan.Abilities[3] != 104 || *(int*)(reader + 24) != 0) return 11;
*(byte*)(reader + 96) = 1;
((delegate* unmanaged<nint, nint, void>)&Serialize)(a, reader);
if (!KingConfigs.Contains(a)) return 31;
// Configs are also cloned through serializers, not only C++ copy constructors.
*(byte*)(reader + 10) = 0;
for (var i = 0; i < 4; i++) *(nint*)(reader + 32 + i * 8) = 0;
((delegate* unmanaged<nint, nint, void>)&Serialize)(a, reader);
if (*(nint*)(reader + 32) != 101 || *(nint*)(reader + 56) != 104 || *(int*)(reader + 24) != 0) return 20;
*(nint*)(b + OverrideOffset) = 101; *(byte*)(b + OverrideOffset + 8) = 0;
*(byte*)(reader + 10) = 1;
((delegate* unmanaged<nint, nint, void>)&Serialize)(b, reader);
if (!Configs.TryGetValue(b, out var roundTrip) || !roundTrip.Abilities.SequenceEqual(plan.Abilities)) return 21;
((delegate* unmanaged<nint, nint, nint>)&Copy)(b, a);
((delegate* unmanaged<nint, nint, nint>)&Move)(c, b);
if (!Configs.ContainsKey(a) || !Configs.ContainsKey(c) || Configs.ContainsKey(b)) return 12;
if (!KingConfigs.Contains(a) || !KingConfigs.Contains(c) || KingConfigs.Contains(b)) return 32;
((delegate* unmanaged<nint, nint, nint>)&Assign)(b, c);
((delegate* unmanaged<nint, nint, nint>)&MoveAssign)(c, c);
if (!Configs.ContainsKey(b) || !Configs.ContainsKey(c)) return 13;
var types = memory + 0x6000; var reference = memory + 0x6100; var instances = memory + 0x6200;
*(nint*)reference = 1;
for (var i = 0; i < 4; i++) { *(nint*)(types + i * 16) = reference; *(nint*)(types + i * 16 + 8) = 11 + i; }
*(nint*)(suit + 0x3418) = types; *(int*)(suit + 0x3420) = 64; *(nint*)(suit + 0x3448) = instances;
((delegate* unmanaged<nint, nint, void>)&Set)(suit, c + OverrideOffset);
((delegate* unmanaged<nint, void>)&Initialize)(suit);
// Rank processing must observe all four replacements, not the defaults.
if (*(int*)(suit + 0x4000) != 410 || *(nint*)(suit + SuitOverrideOffset) != 101) return 14;
for (var i = 0; i < 4; i++) if (*(nint*)(instances + i * 8) != 101 + i) return 15;
// Switching to another config with the SAME primary must clear the extra slots.
Configs.Remove(b);
((delegate* unmanaged<nint, nint, void>)&Set)(suit, b + OverrideOffset);
if (*(nint*)instances != 101 || *(nint*)(instances + 8) != 12 || *(nint*)(instances + 24) != 14 || Suits.ContainsKey(suit)) return 16;
((delegate* unmanaged<nint, nint, void>)&Set)(suit, c + OverrideOffset);
if (*(nint*)(instances + 24) != 104) return 17;
// Actual game loadouts merge MOD and appearance configs field by field.
// A and C deliberately share the same primary, and appearance stays on C.
var items = memory + 0x6500; var sourceItem = memory + 0x6600; var equipped = memory + 0x6800;
var sourceConfigs = memory + 0x7000; var merged = memory + 0x7C00;
*(nint*)items = sourceItem; *(uint*)(items + 8) = 0x130;
*(ulong*)(sourceItem + 0x20) = *(ulong*)(equipped + 0x20) = 1234;
*(uint*)(sourceItem + 0x28) = *(uint*)(equipped + 0x28) = 5678;
*(nint*)(sourceItem + 0x68) = sourceConfigs; *(uint*)(sourceItem + 0x70) = 3 * 0x330;
*(nint*)(equipped + 0x68) = merged; *(nuint*)(equipped + 0x138) = 2;
*(nint*)(sourceConfigs + OverrideOffset) = *(nint*)(sourceConfigs + 2 * 0x330 + OverrideOffset) = 101;
Configs[sourceConfigs] = plan with { Abilities = [101, 102, 0, 0] };
Configs[sourceConfigs + 2 * 0x330] = plan;
for (nuint i = 0; i < 3; i++)
{
*(nuint*)(equipped + 0x130) = i;
((delegate* unmanaged<nint, nint, nint, byte, byte, nint, nint, nint, byte, byte, void>)&BuildEquippedItem)
(17, 18, 19, 0, 0, items, 20, equipped, 1, 1);
var expected = i == 0 ? 2 : i == 1 ? 0 : 4;
if ((Configs.TryGetValue(merged, out var selected) ? selected.Abilities.Count(x => x != 0) : 0) != expected ||
*(int*)(equipped + 0x150) != 1) return 22;
}
// Wrong ID or Conclave must never recover a plan based on the shared primary.
*(uint*)(equipped + 0x28) = 999;
if (SelectedPlan(items, equipped, 0) is not null) return 23;
*(uint*)(equipped + 0x28) = 5678;
if (SelectedPlan(items, equipped, 3) is not null) return 24;
Configs.Remove(sourceConfigs); Configs.Remove(sourceConfigs + 2 * 0x330); Configs.Remove(merged);
// Four banks, three choices each, across the production JSON callbacks.
*(int*)(reader + 64) = 1;
*(nint*)(reader + 80) = memory + 0x8500;
for (var slot = 0; slot < 4; slot++)
for (var choice = 0; choice < 3; choice++)
*(nint*)(memory + 0x8500 + (slot * 3 + choice) * 8) = 101 + slot + choice * 10;
((delegate* unmanaged<nint, nint, void>)&Serialize)(c, reader);
if (!Configs.TryGetValue(c, out var bankPlan) || bankPlan.Banks?[3].Length != 3 || bankPlan.Banks[3][2] != 124) return 25;
new Span<byte>((void*)(memory + 0x8500), 96).Clear();
*(byte*)(reader + 10) = 0;
((delegate* unmanaged<nint, nint, void>)&Serialize)(c, reader);
if (*(nint*)(memory + 0x8500 + 11 * 8) != 124 || *(int*)(reader + 24) != 0) return 26;
*(byte*)(reader + 10) = 1;
((delegate* unmanaged<nint, nint, void>)&Serialize)(b, reader);
((delegate* unmanaged<nint, nint, nint>)&Assign)(a, b);
if (!Configs.TryGetValue(a, out var copiedBanks) || copiedBanks.Banks?[0][1] != 111) return 27;
// A missing middle choice must not silently renumber the later skill.
*(nint*)(memory + 0x8500 + 8) = 0;
((delegate* unmanaged<nint, nint, void>)&Serialize)(c, reader);
if (Configs.ContainsKey(c) || *(int*)(reader + 24) != 0) return 28;
*(int*)(reader + 64) = 0;
*(byte*)(reader + 96) = 0;
Configs.Remove(b);
// Reusing a config address without the extension must erase its cached plan.
*(nint*)(reader + 32) = 0;
((delegate* unmanaged<nint, nint, void>)&Serialize)(c, reader);
if (Configs.ContainsKey(c) || *(int*)(reader + 24) != 0) return 18;
((delegate* unmanaged<nint, void>)&Destroy)(a);
((delegate* unmanaged<nint, void>)&SuitDestroy)(suit);
if (Configs.Count != 0 || Suits.Count != 0) return 19;
return 0;
}
finally
{
Configs.Clear(); Suits.Clear(); BankStates.Clear(); KingConfigs.Clear(); KingSuits.Clear(); NativeMemory.Free((void*)memory);
serialize = copy = move = assign = moveAssign = destroy = buildEquippedItem = set = apply = initialize = rank = typeSerialize = validate = suitDestroy = 0;
}
}
[UnmanagedCallersOnly] private static void TestName(nint reader, nint name) => *(nint*)(reader + 16) = name;
[UnmanagedCallersOnly] private static void TestType(nint reader, byte type) { }
[UnmanagedCallersOnly] private static void TestKing(nint reader, byte* value)
{
if (*(byte*)(reader + 10) == 1) *value = *(byte*)(reader + 96);
else *(byte*)(reader + 96) = *value;
}
[UnmanagedCallersOnly] private static byte TestExists(nint reader) =>
(byte)((*(nint*)(reader + 16) == Names["XFEAbilityOverrides"] && *(nint*)(reader + 32) != 0) ||
(*(int*)(reader + 64) != 0 && (*(nint*)(reader + 16) == Names["XFEAbilityBanks"] || *(int*)(reader + 72) != 0)) ? 1 : 0);
[UnmanagedCallersOnly] private static void TestBegin(nint reader)
{
++*(int*)(reader + 24);
if (*(nint*)(reader + 16) == Names["XFEAbilityBanks"]) *(int*)(reader + 72) = 1;
else if (*(int*)(reader + 72) != 0) *(int*)(reader + 68) = **(byte**)(*(nint*)(reader + 16) + 8) - (byte)'0';
}
[UnmanagedCallersOnly] private static void TestEnd(nint reader)
{
if (--*(int*)(reader + 24) == 0) *(int*)(reader + 72) = 0;
}
[UnmanagedCallersOnly] private static void TestSerialize(nint config, nint reader) { }
[UnmanagedCallersOnly] private static void TestReadAbility(nint* value, nint reader, nint root)
{
var name = *(byte**)(*(nint*)(reader + 16) + 8);
var slot = *name - (byte)'0';
if (slot is not (>= 0 and < 4)) { *value = 0; return; }
var address = *(int*)(reader + 72) == 0 ? reader + 32 + slot * 8 :
*(nint*)(reader + 80) + (*(int*)(reader + 68) * 3 + slot) * 8;
if (*(byte*)(reader + 10) == 0) *(nint*)address = *value;
else *value = *(nint*)address;
}
[UnmanagedCallersOnly] private static nint TestValidate(nint* value) => (nint)value;
[UnmanagedCallersOnly] private static nint TestCopy(nint destination, nint source)
{
*(nint*)(destination + OverrideOffset) = *(nint*)(source + OverrideOffset);
*(byte*)(destination + OverrideOffset + 8) = *(byte*)(source + OverrideOffset + 8); return destination;
}
[UnmanagedCallersOnly] private static void TestDestroy(nint value) { }
[UnmanagedCallersOnly]
private static void TestBuildEquippedItem(nint profile, nint loadout, nint inventory, byte mode, byte slot,
nint items, nint preset, nint equipped, byte force, byte option)
{
// Intentionally bypass the copy callback, as the game's flattening code does.
var source = *(nint*)(*(nint*)items + 0x68) + (nint)(*(nuint*)(equipped + 0x130) * 0x330);
var destination = *(nint*)(equipped + 0x68);
*(nint*)(destination + OverrideOffset) = *(nint*)(source + OverrideOffset);
*(byte*)(destination + OverrideOffset + 8) = *(byte*)(source + OverrideOffset + 8);
*(uint*)(equipped + 0x70) = 0x330;
*(int*)(equipped + 0x150) = profile == 17 && loadout == 18 && inventory == 19 && mode == 0 && slot == 0 &&
preset == 20 && force == 1 && option == 1 ? 1 : 0;
}
[UnmanagedCallersOnly] private static void TestSet(nint suit, nint value)
{
*(nint*)(suit + SuitOverrideOffset) = *(nint*)value;
*(byte*)(suit + SuitOverrideOffset + 8) = *(byte*)(value + 8);
if (*(int*)(suit + 0x3450) != 0) ((delegate* unmanaged<nint, void>)&TestApply)(suit);
}
[UnmanagedCallersOnly] private static void TestApply(nint suit)
{
var slot = *(byte*)(suit + SuitOverrideOffset + 8);
if (slot < 4) *(nint*)(*(nint*)(suit + 0x3448) + slot * 8) = *(nint*)(suit + SuitOverrideOffset);
}
[UnmanagedCallersOnly] private static void TestInitialize(nint suit)
{
if (*(int*)(suit + 0x3450) == 0)
{
for (var i = 0; i < 4; i++) *(nint*)(*(nint*)(suit + 0x3448) + i * 8) = 11 + i;
*(int*)(suit + 0x3450) = 32;
((delegate* unmanaged<nint, void>)&TestApply)(suit);
}
((delegate* unmanaged<nint, int>)&BeforeRanks)(suit);
}
[UnmanagedCallersOnly] private static int TestRank(nint suit)
{
var total = 0;
for (var i = 0; i < 4; i++) total += (int)*(nint*)(*(nint*)(suit + 0x3448) + i * 8);
*(int*)(suit + 0x4000) = total; return 30;
}
}