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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Buffers.Binary;

namespace XFEEDToolBox.Plugin;

internal static unsafe partial class Navigation
{
    // Pinned original build: 3AC8AC0 binds these same two interfaces for the
    // galaxy map. This reader copies existing data on the game owning thread;
    // it never requests another player's data or invokes a social action.
    internal sealed record GalaxyAccountSocialEntry(ulong systemAddress,ulong commanderId,int userId,
        string commanderName,string kind,int status,bool? locationCurrent)
    {
        internal string statusName=>kind=="friend"?status switch
        {
            0=>"offline",1=>"main_menu",2=>"solo",3=>"cqc",4=>"privategroup_other",5=>"privategroup_current",
            6=>"open",8=>"multicrew",9=>"threepointx",10=>"odyssey",11=>"38in40",12=>"training_simulation",
            13=>"operation",_=>"unknown"
        }:"original-wing-travel-state-"+status;
    }
    internal sealed record GalaxyAccountSocial(ulong tick,ulong entity,bool friendsReady,bool wingsReady,GalaxyAccountSocialEntry[] items)
    {
        // A structurally readable cache is not proof that all server replies
        // have arrived. Never turn an empty cache into an exhaustive negative.
        internal bool complete=>false;
        internal string source=>"original-friends-and-wing-components";
    }

    static bool GalaxySocialAddress(ulong address)=>address>0&&address<0x80000000000000UL;
    static bool? GalaxyFriendMarkerCurrent(int status)=>GalaxyFriendMarkerCurrent(status,null,null);

    static bool CopyGalaxyFriendsManager(nuint manager,nuint owner,nuint expectedImage,out GalaxyAccountSocialEntry[] items,int? mode=null,bool? gameplay=null)
    {
        items=[];
        try
        {
            // 1CFFE00 constructs this component and 1D50740 identifies its
            // +58 interface as 5F05CBC. 1D6DCA0 feeds its +200 work map to
            // JSON decoder33FF020. Crucially original marker producer
            // 3AFF080 uses vfunc28/58DD30, the published +220 map instead.
            if(manager==0||owner==0||Pointer(manager)!=expectedImage+0x5238F90||
               Pointer(manager+8)!=expectedImage+0x5238FC0||Pointer(manager+0x58)!=expectedImage+0x52390C0||
               Pointer(manager+0x10)!=owner)return false;
            var map=manager+0x220;var buckets=Pointer(map);var bucketCount=Read<ulong>(map+8);var count=Read<ulong>(map+0x18);
            if(bucketCount==0||bucketCount>65536||count>16384||buckets<0x10000)return false;
            var heads=Platform.ReadBytes(buckets,checked((int)bucketCount*8));
            List<GalaxyAccountSocialEntry> result=[];HashSet<nuint> nodes=[];HashSet<int> users=[];
            for(nuint bucket=0;bucket<(nuint)bucketCount;bucket++)
            {
                var sentinel=buckets+bucket*8;var node=(nuint)BinaryPrimitives.ReadUInt64LittleEndian(heads.AsSpan((int)bucket*8));
                while(node!=sentinel)
                {
                    if(node<0x10000||(ulong)nodes.Count>=count||!nodes.Add(node))return false;
                    var row=Platform.ReadBytes(node,0x78);
                    ulong U64(int at)=>BinaryPrimitives.ReadUInt64LittleEndian(row.AsSpan(at));
                    int I32(int at)=>BinaryPrimitives.ReadInt32LittleEndian(row.AsSpan(at));
                    var user=I32(8);var relation=I32(0x20);var state=I32(0x28);var address=U64(0x40);
                    if(user<0||I32(0x10)!=user||!users.Add(user)||relation is <0 or >3||row[0x24]>1||state is <0 or >13)return false;
                    // The cache may also contain pending requests and ignored
                    // users. Original 3AFF080 permits relationship2/3. Keep
                    // only those positive rows, retaining historical status.
                    if(relation is 2 or 3&&row[0x24]==0&&GalaxySocialAddress(address))
                    {
                        if(!GalaxyAccountString((nuint)U64(0x18),out var name))return false;
                        var commander=U64(0x30);if(commander==ulong.MaxValue)commander=0;
                        result.Add(new(address,commander,user,name,"friend",state,GalaxyFriendMarkerCurrent(state,mode,gameplay)));
                    }
                    if(!Platform.ReadBytes(node,0x78).AsSpan().SequenceEqual(row))return false;
                    node=(nuint)U64(0);
                }
            }
            if((ulong)nodes.Count!=count||Pointer(map)!=buckets||Read<ulong>(map+8)!=bucketCount||Read<ulong>(map+0x18)!=count||
               Pointer(manager+0x10)!=owner||Pointer(manager+0x58)!=expectedImage+0x52390C0||
               !Platform.ReadBytes(buckets,heads.Length).AsSpan().SequenceEqual(heads))return false;
            items=result.ToArray();return true;
        }
        catch{return false;}
    }

    static bool CopyGalaxyWingManager(nuint manager,nuint owner,nuint expectedImage,uint memberTypeId,out GalaxyAccountSocialEntry[] items,int? ownUserId=null)
    {
        items=[];
        try
        {
            // 25C4580 + 26074D0: six fixed 0x40-byte roster slots. 26075B0
            // resolves slot+18's weak life, then the member's registered
            // 5F771DC interface. 647EF0 proves the life+C8 registry link.
            if(manager==0||owner==0||Pointer(manager)!=expectedImage+0x52A5498||Pointer(manager+8)!=expectedImage+0x52A54C8||
               Pointer(manager+0x58)!=expectedImage+0x52A55C8||Pointer(manager+0x10)!=owner||memberTypeId==0)return false;
            var slotLimit=Read<uint>(manager+0x59C);var phase=Read<byte>(manager+0x590);
            if(slotLimit>6)return false;
            var roster=Platform.ReadBytes(manager+0x2A8,6*0x40);List<GalaxyAccountSocialEntry> result=[];HashSet<ulong> identities=[];
            var any=false;
            for(var index=0;index<slotLimit;index++)
                any|=BinaryPrimitives.ReadUInt64LittleEndian(roster.AsSpan(index*0x40))!=ulong.MaxValue;
            for(var index=0;index<6;index++)
            {
                var slot=roster.AsSpan(index*0x40,0x40);var identity=BinaryPrimitives.ReadUInt64LittleEndian(slot);
                if(identity==ulong.MaxValue)continue;
                if(!identities.Add(identity))return false;
                var life=(nuint)BinaryPrimitives.ReadUInt64LittleEndian(slot[0x18..]);
                // A not-yet-loaded member is a partial answer, not evidence
                // that the member has no location.
                if(life<0x10000||Read<uint>(life+0xC0)!=8)continue;
                var components=Pointer(life+0xC8);if(components<0x10030)continue;
                var member=RegisteredComponent(components-0x30,memberTypeId);
                if(member<0x10058||Pointer(member)!=expectedImage+0x52A8668)continue;
                var obj=member-0x58;var memberOwner=Pointer(obj+0x10);
                if(memberOwner==0||Pointer(obj)!=expectedImage+0x52A8538||Pointer(obj+8)!=expectedImage+0x52A8568)return false;
                var rawAddress=Read<ulong>(obj+0x80);var travelState=Read<int>(obj+0x8C);
                // 2644BF0 stores the original body/system address then masks
                // exactly these 55 bits for the galaxy system lookup.
                var address=rawAddress&0x7FFFFFFFFFFFFFUL;
                if(rawAddress!=ulong.MaxValue&&GalaxySocialAddress(address))
                {
                    // Slot identity has not been proven to be commander_id;
                    // leave that optional identity unknown rather than guess.
                    var user=BinaryPrimitives.ReadInt32LittleEndian(slot[0x10..]);
                    // Original 3AFF080 requires vfunc48 (nonempty roster),
                    // vfunc2C0(2)==false (26178A0, manager+590 !=2), and
                    // excludes the local user ID before appending a marker.
                    bool? current=user<0||!any||phase==2?false:ownUserId is int self?user!=self:null;
                    result.Add(new(address,0,user,"","wing",travelState,current));
                }
                if(Read<ulong>(obj+0x80)!=rawAddress||Read<int>(obj+0x8C)!=travelState||Pointer(obj+0x10)!=memberOwner||
                   Pointer(member)!=expectedImage+0x52A8668||Pointer(life+0xC8)!=components||Read<uint>(life+0xC0)!=8)return false;
            }
            if(Pointer(manager+0x10)!=owner||Pointer(manager+0x58)!=expectedImage+0x52A55C8||
               Read<uint>(manager+0x59C)!=slotLimit||Read<byte>(manager+0x590)!=phase||
               !Platform.ReadBytes(manager+0x2A8,roster.Length).AsSpan().SequenceEqual(roster))return false;
            items=result.ToArray();return true;
        }
        catch{return false;}
    }

    static bool TryReadGalaxyAccountSocial(Snapshot frame,out GalaxyAccountSocial result)
    {
        result=new(0,0,false,false,[]);
        if(!Enabled()||!inGame||!frame.valid||frame.physics==0||owningThread==0||GetCurrentThreadId()!=owningThread)return false;
        try
        {
            if(!TryGetGalaxyAccountRegistry(frame,out var registry))return false;
            var friends=RegisteredComponent(registry,Read<uint>(image+0x5F05CBC));
            var wings=RegisteredComponent(registry,Read<uint>(image+0x5F2E7F8));
            var flow=RegisteredComponent(registry,Read<uint>(image+0x5F05BC4));int? mode=null;bool? gameplay=null;
            if(!CopyGalaxySocialModes(flow,image,Read<uint>(image+0x5F2E7E8),out mode,out gameplay)){mode=null;gameplay=null;}
            // 26008D0 identifies the original local user interface; its
            // vfunc28/2600E90 returns +1AC, used by the same marker loop.
            var user=RegisteredComponent(registry,Read<uint>(image+0x5F05BAC));int? ownUserId=null;
            if(user>=0x10000&&Pointer(user)==image+0x52A0258&&Read<int>(user+0x1AC)>=0)ownUserId=Read<int>(user+0x1AC);
            GalaxyAccountSocialEntry[] friendItems=[],wingItems=[];
            var friendsReady=friends>=0x10058&&Pointer(friends)==image+0x52390C0&&
                CopyGalaxyFriendsManager(friends-0x58,Pointer(friends-0x48),image,out friendItems,mode,gameplay);
            var wingsReady=wings>=0x10058&&Pointer(wings)==image+0x52A55C8&&
                CopyGalaxyWingManager(wings-0x58,Pointer(wings-0x48),image,Read<uint>(image+0x5F771DC),out wingItems,ownUserId);
            if(!friendsReady&&!wingsReady)return false;
            result=new(frame.tick,frame.entity,friendsReady,wingsReady,[..friendItems,..wingItems]);return true;
        }
        catch{return false;}
    }
}