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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;

if (args.Length is < 1 or > 3) throw new ArgumentException("Pass the repository root, optional navigation DLL and report path.");
var root = Path.GetFullPath(args[0]);
var results = new List<object>();
var loader = Path.Combine(root, "artifacts/plugin-build/dinput8.dll");
var navigation = args.Length >= 2 ? Path.GetFullPath(args[1]) : Path.Combine(root, "artifacts/navigation-native-build/XFEEDNavigation.dll");
var interaction = Path.Combine(root, "artifacts/interaction-native-build/XFEEDInteraction.dll");
var platform = Path.Combine(root, "artifacts/managed-platform-validation/XFEEDPluginValidation.dll");
var paths = new[] { loader, navigation, interaction, platform };
var handles = new Dictionary<string, nint>(StringComparer.OrdinalIgnoreCase);

foreach (var path in paths)
{
    using var stream = File.OpenRead(path); using var pe = new PEReader(stream);
    Require(pe.PEHeaders.CorHeader is null, "Expected a native PE DLL, not an IL assembly: " + path);
    Require(pe.PEHeaders.CoffHeader.Machine == System.Reflection.PortableExecutable.Machine.Amd64, "Expected x64: " + path);
    // AOT DLLs stay loaded for this fixture process's lifetime. Never FreeLibrary them.
    handles[path] = NativeLibrary.Load(path);
}
using var abiContract = JsonDocument.Parse(File.ReadAllText(Path.Combine(root, "native/ManagedPlugin/abi-exports.json")));
Require(abiContract.RootElement.GetProperty("schema").GetInt32() == 1 && abiContract.RootElement.GetProperty("abiVersion").GetInt32() == 1, "Original ABI contract version");
foreach (var library in abiContract.RootElement.GetProperty("libraries").EnumerateArray())
{
    var path = paths.Single(value => Path.GetFileName(value) == library.GetProperty("file").GetString());
    var names = library.GetProperty("exports").EnumerateArray().Select(value => value.GetString()!).ToArray();
    Require(names.Length > 0 && names.Distinct().Count() == names.Length, "Original export contract must be nonempty and unique");
    foreach (var name in names) Require(NativeLibrary.TryGetExport(handles[path], name, out _), "Missing original ABI: " + name);
    results.Add(new { check = "originalExports", file = Path.GetFileName(path), names });
}
Require(Call(loader, "XFEPluginGetAbiVersion") == 1, "Plugin ABI");
Require(Call(navigation, "XFENavigationGetAbiVersion") == 1, "Navigation ABI");
var checks = new (string Suffix, uint Expected)[]
{
    ("CommerceBatch",255),("CommunityGoal",255),("LocalDockingKind",1023),("NormalBoost",7),
    ("DockingInputMapper",255),("DockingFixture",255),
    ("Observer",7),("CommerceDocked",127),("LocalDockingHandoff",16777215),("DockingZeroUpdate",255),("ShipHealth",255),("HealthFlow",255),("LocalCooldown",255),("ServiceQuotes",255),("DockingSourceZero",255),("LocalTargets",255),("LocalEntityDirectory",255),("LocalSelection",255),("LocalDockingAvailability",31),("LocalPlayerIdentity",255),
    ("OverlayLifetime",31),("ComponentLookup",63),("FsdCancellation",127),("ThrottleControl",255),
    ("TargetLookup",63),("ActorSelection",63),("FsdReturn",3),("CommitGate",31),("OutwardThrottle",31),
    ("RouteArguments",31),("StellarSpeed",255),("ReferenceGeometry",524287),("AdaptiveMotion",1023),
    ("ScoopGeometry",63),("HeatNormalization",31),("ApplicationAxes",31),("SessionMenu",4095),
    ("Commerce",255),("InterfaceProtocol",4095),("GalaxyCoverage",8191),("GalaxyAccount",255),("GalaxyAccountWire",63),("GalaxyTradeData",255),("GalaxyColonisation",255),("GalaxyFilterFacts",255),("GalaxyFilterFactsQueue",255),("GalaxyVisitedFacts",255),("GalaxyColonisationFacts",255),("GalaxySocial",65535),("GalaxySocialModes",8191),("GalaxyPowerOptions",65535),("GalaxyAppearance",255),("GalaxyPowerActivity",255),("GalaxyPowerActivityWire",255),("GalaxyThargoid",65535),("GalaxyAccountCommunityGoals",255),("GalaxyAccountFleet",65535),("GalaxyAccountFleetWire",255),("GalaxyAccountMissions",65535),("GalaxyAccountMissionsWire",255),("GalaxyPowerOuter",65535),("GalaxyPowerOuterWire",255),("GalaxyPowerTerritory",255),("GalaxyPowerTerritoryWire",255),("GalaxyPowerStatus",255),("GalaxyPowerStatusWire",255),("GalaxyCommodityOptions",255),("GalaxyCommodityOptionsWire",255),("GalaxyCommodityEligibility",255),("GalaxyCommodityBalance",255)
    ,("GalaxyCommodityFilter",255),("GalaxyCommodityFilterWire",255),("GalaxyCommodityRebuild",255),("GalaxyCurrentTradeFlows",255),("GalaxyCurrentTradeFlowsWire",255)
    ,("GalaxyCells",65535),("GalaxyPowerOuterStyle",255),("GalaxyPowerOuterExtent",255),("GalaxyCatalogueQuality",255),("GalaxyCatalogueProfile",65535)
    ,("GalaxyBasicSystemFacts",255),("GalaxyBasicSystemFactsWire",255)
    ,("GalaxyAllegianceDefinitions",65535),("GalaxyCellBudget",255)
    ,("GalaxyNavRouteMembership",255)
    ,("GalaxyPointOverrides",255)
    ,("GalaxyRouteOptions",255)
    ,("GalaxyRouteStarClassFilter",255),("GalaxyCivilizationFacts",255),("GalaxyServiceFacts",255)
    ,("GalaxyCarrierFacts",255)
    ,("GalaxyDustInputs",255),("GalaxyRouteMapFilter",255),("GalaxyStateFacts",65535),("GalaxyColonisationOwnership",255),("GalaxyColonisationHighlight",255),("GalaxyPowerInfluence",255)
};
foreach (var (suffix, expected) in checks)
{
    var name = "XFENavigation" + suffix + "SelfTest";
    var values = Enumerable.Range(0, 8).Select(_ => Call(navigation, name)).ToArray();
    Require(values.All(value => value == expected), name + " expected " + expected + ", received " + string.Join(',', values));
    results.Add(new { check = name, expected, masks = values });
}
foreach (var (name, expected) in new[] { ("XFEPlatformSelfTest", 2047u), ("XFEInteractionSelfTest", 255u) })
{
    var values = Enumerable.Range(0, 8).Select(_ => Call(platform, name)).ToArray();
    Require(values.All(value => value == expected), name + " failed: " + string.Join(',', values));
    results.Add(new { check = name, expected, masks = values });
}
// Unsupported fixture images must never install game hooks or start the input service.
Require(Call(navigation, "XFENavigationStart") == 0, "A fixture executable was accepted as the supported game");
var start = Marshal.GetDelegateForFunctionPointer<Start>(NativeLibrary.GetExport(handles[interaction], "XFEInteractionStart"));
foreach (var thread in new nuint[] { 0, nuint.MaxValue, 1 }) Require(start(thread) == 0, "Invalid interaction start was accepted");
var report = new
{
    passed = true, gameAccess = false, processScope = "own fixture process and allocations", iterations = 8,
    checks = results,
    binaries = paths.Select(path => new { file = Path.GetFileName(path), sha256 = Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(path))) }).ToArray()
};
var reportPath = args.Length >= 3 ? Path.GetFullPath(args[2]) : Path.Combine(root, "artifacts/managed-plugin-validation.json");
File.WriteAllText(reportPath, JsonSerializer.Serialize(report, new JsonSerializerOptions { WriteIndented = true }), new UTF8Encoding(false));
Console.WriteLine($"Passed {checks.Length * 8 + 16} native self-test invocations; original exports, PE format, ABI and unsupported-build rejection passed.");
Console.WriteLine(reportPath);

uint Call(string path, string name) => Marshal.GetDelegateForFunctionPointer<Probe>(NativeLibrary.GetExport(handles[path], name))();
void Require(bool value, string message) { if (!value) throw new InvalidOperationException(message); }
[UnmanagedFunctionPointer(CallingConvention.Winapi)] delegate uint Probe();
[UnmanagedFunctionPointer(CallingConvention.Winapi)] delegate uint Start(nuint thread);