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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json;
using System.Runtime.CompilerServices;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;

namespace XFEEDToolBox.Core.Galaxy;

public sealed partial class NativeGalaxyCatalog
{
    private sealed record PointPage(int ProcessId,long Generation,ulong Tick,int? View,bool D50Known,bool NavKnown,
        int OverrideOffset,int OverrideTotal,int NavOffset,int NavTotal,GalaxyOriginalPointOverride[] Overrides,long[] Members);
    private sealed record PointContinuation(long Generation,JsonElement FirstPage);
    private readonly ConditionalWeakTable<GalaxyCatalogueProfile,PointContinuation> pointContinuations=new();

    private static PointPage ReadPointPage(JsonElement p)
    {
        if(p.ValueKind!=JsonValueKind.Object||p.GetProperty("scope").GetString()!="published-renderer-d50+local-navroute")
            throw new InvalidDataException("原星点覆写来源未知。");
        int Number(string name,int max)
        {
            if(!p.GetProperty(name).TryGetInt32(out var value)||value<0||value>max)throw new InvalidDataException("原星点覆写分页无效。");
            return value;
        }
        long Address(JsonElement id)=>id.ValueKind==JsonValueKind.String&&long.TryParse(id.GetString(),out var value)&&value>0&&value<1L<<55
            ?value:throw new InvalidDataException("原星点覆写地址无效。");
        int? view=null;var mode=p.GetProperty("navRouteViewMode");
        if(mode.ValueKind!=JsonValueKind.Null)
        {if(!mode.TryGetInt32(out var value))throw new InvalidDataException("原星点导航视图无效。");view=value;}
        var process=Number("processId",int.MaxValue);var generation=p.GetProperty("sourceGeneration").GetInt64();var tick=p.GetProperty("capturedAtTick").GetUInt64();
        if(process<=0||generation<=0)throw new InvalidDataException("原星点覆写代际无效。");
        var d50=p.GetProperty("d50Complete").GetBoolean();var nav=p.GetProperty("navRouteComplete").GetBoolean();
        if(nav&&view is null)throw new InvalidDataException("原导航成员上下文缺失。");
        var rows=new List<GalaxyOriginalPointOverride>();
        foreach(var row in p.GetProperty("overrides").EnumerateArray())
        {
            var id=Address(row.GetProperty("systemAddress"));var minimum=row.GetProperty("minimumExtra").GetSingle();
            var position=row.GetProperty("position");if(position.ValueKind!=JsonValueKind.Array||position.GetArrayLength()!=3)throw new InvalidDataException("原星点覆写坐标无效。");
            var point=new Coordinates(position[0].GetDouble(),position[1].GetDouble(),position[2].GetDouble());
            var name=row.GetProperty("name").ValueKind==JsonValueKind.Null?null:row.GetProperty("name").GetString();
            var star=row.GetProperty("starClass").GetString()??"";GalaxyOriginalStellarAppearance? appearance=null;
            if(row.TryGetProperty("originalAppearance",out var a)&&a.ValueKind!=JsonValueKind.Null)
            {
                var type=a.GetProperty("typeRgba").GetUInt32();var raw=a.GetProperty("rawScale").GetUInt32();var classification=a.GetProperty("classification").GetUInt16();
                uint? realistic=a.GetProperty("realisticRgba").ValueKind==JsonValueKind.Null?null:a.GetProperty("realisticRgba").GetUInt32();
                float? blend=a.GetProperty("colourBlend").ValueKind==JsonValueKind.Null?null:a.GetProperty("colourBlend").GetSingle();
                var size=a.GetProperty("size").GetSingle();var small=a.GetProperty("smallSize").GetSingle();
                bool? active=a.GetProperty("smallSizeActive").ValueKind==JsonValueKind.Null?null:a.GetProperty("smallSizeActive").GetBoolean();
                if(!float.IsFinite(size)||size is <0 or >4||!float.IsFinite(small)||small is <0 or >4||
                   blend is{} b&&(!float.IsFinite(b)||b is <0 or >1))throw new InvalidDataException("原星点覆写外观无效。");
                appearance=new(type,realistic,blend,raw,classification,size,small,active);
            }
            if(!float.IsFinite(minimum)||minimum<0||!point.IsValid||Math.Max(Math.Abs(point.X),Math.Max(Math.Abs(point.Y),Math.Abs(point.Z)))>1000000||name is{Length:0 or >159})
                throw new InvalidDataException("原星点覆写内容无效。");
            rows.Add(new(id,minimum,row.GetProperty("farFade").GetBoolean(),row.GetProperty("pending").GetBoolean(),point,name,star,appearance));
            if(rows.Count>64)throw new InvalidDataException("原星点覆写分页超出限制。");
        }
        var members=p.GetProperty("navRouteMembers").EnumerateArray().Select(Address).ToArray();
        if(members.Length>512)throw new InvalidDataException("原导航成员分页超出限制。");
        var offset=Number("overrideOffset",4096);var total=Number("overrideTotal",4096);
        var navOffset=Number("navRouteOffset",16384);var navTotal=Number("navRouteTotal",16384);
        if(offset+rows.Count>total||navOffset+members.Length>navTotal||!d50&&(total!=0||rows.Count!=0)||!nav&&(navTotal!=0||members.Length!=0))
            throw new InvalidDataException("原星点覆写完整性不匹配。");
        return new(process,generation,tick,view,d50,nav,offset,total,navOffset,navTotal,rows.ToArray(),members);
    }

    /// <summary>Strictly join pages of one immutable copied snapshot. Missing pages keep absence unknown.</summary>
    public static GalaxyOriginalPointOverrides ReadPointOverridesPages(IReadOnlyList<JsonElement> pages)
    {
        if(pages.Count is <1 or >96)throw new InvalidDataException("原星点覆写分页数量无效。");
        var first=ReadPointPage(pages[0]);List<GalaxyOriginalPointOverride> rows=[];List<long> members=[];
        foreach(var json in pages)
        {
            var p=ReadPointPage(json);
            if(p.ProcessId!=first.ProcessId||p.Generation!=first.Generation||p.Tick!=first.Tick||p.View!=first.View||
               p.D50Known!=first.D50Known||p.NavKnown!=first.NavKnown||p.OverrideTotal!=first.OverrideTotal||p.NavTotal!=first.NavTotal||
               p.OverrideOffset!=rows.Count||p.NavOffset!=members.Count)
                throw new InvalidDataException("原星点覆写分页来源已变化。");
            rows.AddRange(p.Overrides);members.AddRange(p.Members);
        }
        if(rows.Select(r=>r.SystemAddress).Distinct().Count()!=rows.Count||members.Distinct().Count()!=members.Count)
            throw new InvalidDataException("原星点覆写分页包含重复地址。");
        return new(first.ProcessId,first.Generation,first.Tick,first.View,first.D50Known&&rows.Count==first.OverrideTotal,
            first.NavKnown&&members.Count==first.NavTotal,rows,members);
    }

    private GalaxyCatalogueProfile ReadFirstProfilePage(JsonElement result,long generation)
    {
        var profileJson=result.GetProperty("profile");var profile=ReadCatalogueProfile(profileJson);
        if(!profileJson.TryGetProperty("pointOverrides",out var points)||points.ValueKind==JsonValueKind.Null)return profile;
        var first=ReadPointPage(points);
        if(first.ProcessId!=ProcessId||first.Generation!=generation)throw new InvalidDataException("原星点覆写进程或代际已变化。");
        if(first.Overrides.Length<first.OverrideTotal||first.Members.Length<first.NavTotal)
            pointContinuations.Add(profile,new(generation,points.Clone()));
        return profile;
    }

    private async Task<GalaxyCatalogueProfile> ReadCompleteProfileAsync(JsonElement result,long generation,CancellationToken token)
    {
        var profile=ReadFirstProfilePage(result,generation);
        var points=await CompletePointOverridesAsync(profile,token);
        return profile with{PointOverrides=points??profile.PointOverrides};
    }

    /// <summary>Complete only this copied profile's auxiliary pages. A superseding profile returns null and is never cancelled by this reader.</summary>
    public async Task<GalaxyOriginalPointOverrides?> CompletePointOverridesAsync(GalaxyCatalogueProfile profile,CancellationToken token=default)
    {
        ArgumentNullException.ThrowIfNull(profile);
        if(profile.PointOverrides is{} firstSnapshot&&(firstSnapshot.ProcessId!=ProcessId||
           firstSnapshot.SourceGeneration!=Volatile.Read(ref latestCatalogueProfileGeneration)))return null;
        if(!pointContinuations.TryGetValue(profile,out var pending))return profile.PointOverrides;
        var generation=pending.Generation;
        if(Volatile.Read(ref latestCatalogueProfileGeneration)!=generation)return null;
        List<JsonElement> pages=[pending.FirstPage];var first=ReadPointPage(pending.FirstPage);
        // Independent IPC serialization: an optional slow page must not hold
        // the basic-profile or catalogue-cell client's per-call gate.
        var auxiliaryPipe=new NavigationPipeClient(ProcessId);
        using var deadline=CancellationTokenSource.CreateLinkedTokenSource(token);deadline.CancelAfter(TimeSpan.FromSeconds(8));
        var offset=first.Overrides.Length;var navOffset=first.Members.Length;
        while(offset<first.OverrideTotal||navOffset<first.NavTotal)
        {
            deadline.Token.ThrowIfCancellationRequested();
            if(Volatile.Read(ref latestCatalogueProfileGeneration)!=generation)return null;
            JsonElement result;
            try{result=await auxiliaryPipe.CallAsync(new{op="galaxy.catalogue.profile.result",generation,overrideOffset=offset,navRouteOffset=navOffset},deadline.Token);}
            catch(InvalidOperationException)when(Volatile.Read(ref latestCatalogueProfileGeneration)!=generation){return null;}
            if(Volatile.Read(ref latestCatalogueProfileGeneration)!=generation)return null;
            if(result.GetProperty("generation").GetInt64()!=generation||!result.GetProperty("done").GetBoolean()||
               result.TryGetProperty("cancelled",out var cancelled)&&cancelled.ValueKind==JsonValueKind.True||
               result.TryGetProperty("error",out var error)&&!string.IsNullOrEmpty(error.GetString()))
                throw new InvalidDataException("原星点覆写快照已变化。");
            var points=result.GetProperty("profile").GetProperty("pointOverrides");var next=ReadPointPage(points);
            if(next.OverrideOffset!=offset||next.NavOffset!=navOffset||next.Overrides.Length+next.Members.Length==0)
                throw new InvalidDataException("原星点覆写分页没有进展。");
            offset+=next.Overrides.Length;navOffset+=next.Members.Length;pages.Add(points.Clone());
            if(pages.Count>96)throw new InvalidDataException("原星点覆写分页超出限制。");
        }
        return Volatile.Read(ref latestCatalogueProfileGeneration)==generation?ReadPointOverridesPages(pages):null;
    }
}