XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEWarframe

【WinUI】基于Warframe游戏本体的公开服务器以及安装器

公开
关注 0 Fork 0 Star 0
UTF-8
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;
    }
}