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
{
    internal sealed record GalaxyAccountMissionItem(ulong missionId,ulong[] systemAddresses,ulong? selectedSystemAddress)
    {
        // Original map marker4 versus marker45, not a guessed mission category.
        internal int markerType{get;init;}=4;
    }
    internal sealed record GalaxyAccountMissions(ulong tick,ulong entity,GalaxyAccountMissionItem[] items)
    {
        internal bool complete=>false;
        internal string source=>"original-external-panel-mission-list";
    }

    // A bounded copy with a final stability check; never call a game virtual,
    // parse arbitrary mission prose, or retain pointers in the returned model.
    sealed class GalaxyMissionCopy
    {
        readonly Dictionary<(nuint,int),byte[]> copies=[];
        int bytes;
        internal byte[] Bytes(nuint pointer,int length)
        {
            if(length==0)return [];
            if(pointer<0x10000||length<0||length>0x200000||pointer>nuint.MaxValue-(nuint)length)throw new InvalidDataException();
            var key=(pointer,length);
            if(copies.TryGetValue(key,out var found))return found;
            if(bytes>0x800000-length)throw new InvalidDataException();
            var result=Platform.ReadBytes(pointer,length);
            if(result.Length!=length)throw new InvalidDataException();
            copies.Add(key,result);bytes+=length;return result;
        }
        internal ulong U64(nuint pointer)=>BinaryPrimitives.ReadUInt64LittleEndian(Bytes(pointer,8));
        internal nuint Ptr(nuint pointer)=>(nuint)U64(pointer);
        internal byte Byte(nuint pointer)=>Bytes(pointer,1)[0];
        internal bool Stable()=>copies.All(pair=>Platform.ReadBytes(pair.Key.Item1,pair.Key.Item2).AsSpan().SequenceEqual(pair.Value));
        internal byte[] StringBytes(nuint pointer)
        {
            if(pointer==0)return [];
            var length=U64(pointer);if(length>4096)throw new InvalidDataException();
            return Bytes(pointer+0x14,checked((int)length));
        }
    }

    static bool GalaxyMissionAddress(ulong value)=>value is >0 and <0x80000000000000UL;

    static void CopyGalaxyMissionVector(GalaxyMissionCopy copy,nuint header,List<ulong> addresses)
    {
        var capacity=copy.U64(header);var count=copy.U64(header+0x10);var data=copy.Ptr(header+0x18);
        if(count>4096||count>capacity||capacity>0x100000||addresses.Count>4096-(long)count)throw new InvalidDataException();
        var bytes=copy.Bytes(data,checked((int)count*8));
        for(var index=0;index<(int)count;index++)
        {
            var address=BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(index*8,8));
            if(!GalaxyMissionAddress(address))throw new InvalidDataException();
            addresses.Add(address);
        }
    }

    static ulong GalaxyMissionNumber(ReadOnlySpan<byte> value,byte separator)
    {
        // Original20D1A00 splits at the key's first byte (default ':').
        // Original52D8F0 skips C whitespace, then consumes decimal digits;
        // suffix text does not become a second address. Overflow is rejected
        // here rather than manufacturing a navigable truncated prefix.
        var end=value.IndexOf(separator);if(end>=0)value=value[..end];
        var index=0;while(index<value.Length&&(value[index]==32||value[index] is >=9 and <=13))index++;
        var digits=index;ulong number=0;
        for(;index<value.Length&&value[index] is >=48 and <=57;index++)
        {
            var digit=(uint)(value[index]-48);
            if(number>(ulong.MaxValue-digit)/10)throw new InvalidDataException();
            number=number*10+digit;
        }
        if(index==digits||!GalaxyMissionAddress(number))throw new InvalidDataException();
        return number;
    }

    static void CopyGalaxyMissionText(GalaxyMissionCopy copy,nuint header,List<ulong> addresses)
    {
        // Original20D1A00 ->233A70 identity: a structured-text entry is24B;
        // only type22 has the original system-address semantics. Header+0
        // also contains text attributes and is not treated as vector capacity.
        var count=copy.U64(header+0x10);var data=copy.Ptr(header+0x18);
        if(count>4096)throw new InvalidDataException();
        var rows=copy.Bytes(data,checked((int)count*24));
        for(var index=0;index<(int)count;index++)
        {
            var row=rows.AsSpan(index*24,24);if(row[16]!=22)continue;
            if(addresses.Count==4096)throw new InvalidDataException();
            var key=copy.StringBytes((nuint)BinaryPrimitives.ReadUInt64LittleEndian(row));
            var value=copy.StringBytes((nuint)BinaryPrimitives.ReadUInt64LittleEndian(row[8..]));
            addresses.Add(GalaxyMissionNumber(value,key.Length==0?(byte)':':key[0]));
        }
    }

    static bool CopyGalaxyMissionsManager(nuint manager,nuint owner,nuint expectedImage,out GalaxyAccountMissionItem[] items)
    {
        items=[];
        try
        {
            var copy=new GalaxyMissionCopy();
            // Old208CD90 installs these three tables; current ED5A bindings
            // are whole-body/VT-slot checked in missions-compatibility.json.
            if(manager<0x10000||owner<0x10000||copy.Ptr(manager)!=expectedImage+0x525EE18||
               copy.Ptr(manager+8)!=expectedImage+0x525EE48||copy.Ptr(manager+0x58)!=expectedImage+0x525EF48||
               copy.Ptr(manager+0x10)!=owner)return false;
            // Original iface+70 is lea rcx+40: base98 pointer vector.
            var capacity=copy.U64(manager+0x98);var count=copy.U64(manager+0xA8);var rows=copy.Ptr(manager+0xB0);
            if(count>4096||count>capacity||capacity>0x100000)return false;
            var vector=copy.Bytes(rows,checked((int)count*8));
            List<GalaxyAccountMissionItem> result=[];HashSet<ulong> liveIds=[];
            // Original3A8A190 and3A69D00 both traverse live entries backwards.
            // No D8/E0 exclusion is applied to this selectable list.
            for(var index=(int)count-1;index>=0;index--)
            {
                var mission=(nuint)BinaryPrimitives.ReadUInt64LittleEndian(vector.AsSpan(index*8,8));
                var table=copy.Ptr(mission);List<ulong> addresses=[];ulong id;int marker;
                if(table==expectedImage+0x5260A18)
                {
                    // Oldnormal iface5260A48 is object+1A8. +38 reads-C0;
                    // +78 is typed text-B0 followed by the vector-90.
                    if(mission<0x101A8||copy.Ptr(mission-0x1A8)!=expectedImage+0x52609C8)return false;
                    id=copy.U64(mission-0xC0);
                    CopyGalaxyMissionText(copy,mission-0xB0,addresses);
                    CopyGalaxyMissionVector(copy,mission-0x90,addresses);
                    var special=copy.Byte(mission-0x27);if(special>1)return false;
                    marker=special!=0||copy.Byte(mission-0x20)==1?45:4;
                }
                else if(table==expectedImage+0x5260BC8)
                {
                    // Oldfailed iface5260BF8 is object+118; original D8=false
                    // and E0=2, so it retains the ordinary marker4 semantics.
                    if(mission<0x10118||copy.Ptr(mission-0x118)!=expectedImage+0x5260B78)return false;
                    id=copy.U64(mission-0x40);marker=4;
                    CopyGalaxyMissionVector(copy,mission+0xE0,addresses);
                }
                else return false;
                // Match the selection handler's first reverse live ID before
                // considering its fallback summary-map lookup.
                if(liveIds.Add(id))result.Add(new(id,addresses.ToArray(),addresses.Count==0?null:addresses[^1]){markerType=marker});
            }
            // Originaliface+78 exposes the baseC0 open hash map. Each occupied
            // 88B slot is [u64 MissionID,80B summary]; header+8 is count,
            // +10 capacity and+18 allocation containing its occupancy bitmap.
            var mapCount=copy.U64(manager+0xC8);var mapCapacity=copy.U64(manager+0xD0);var map=copy.Ptr(manager+0xD8);
            if(mapCount>4096||mapCount>mapCapacity||mapCapacity>8192||
               mapCapacity!=0&&(mapCapacity<8||(mapCapacity&(mapCapacity-1))!=0))return false;
            if(mapCapacity!=0)
            {
                var bitmapBytes=checked((int)(((mapCapacity>>3)+7)&~7UL));
                var memory=copy.Bytes(map,checked(bitmapBytes+(int)mapCapacity*0x88));var occupied=0;
                for(var index=0;index<(int)mapCapacity;index++)
                {
                    if((memory[index>>3]&(1<<(index&7)))==0)continue;
                    occupied++;var offset=bitmapBytes+index*0x88;var id=BinaryPrimitives.ReadUInt64LittleEndian(memory.AsSpan(offset,8));
                    if(liveIds.Contains(id))continue;
                    // Original3A3FFD0 only publishes summaries with nonempty
                    // first title field. Read its length, never its private name.
                    var title=(nuint)BinaryPrimitives.ReadUInt64LittleEndian(memory.AsSpan(offset+8,8));
                    if(title==0||copy.U64(title)==0)continue;
                    List<ulong> addresses=[];CopyGalaxyMissionText(copy,map+(nuint)offset+0x28,addresses);
                    if(result.Count==4096)return false;
                    result.Add(new(id,addresses.ToArray(),addresses.Count==0?null:addresses[^1]));
                }
                if((ulong)occupied!=mapCount)return false;
            }
            if(!copy.Stable())return false;
            items=result.ToArray();return true;
        }
        catch{items=[];return false;}
    }

    static bool TryReadGalaxyAccountMissions(Snapshot frame,out GalaxyAccountMissions result)
    {
        result=new(0,0,[]);
        if(!Enabled()||!inGame||!frame.valid||frame.physics==0||owningThread==0||GetCurrentThreadId()!=owningThread)return false;
        try
        {
            if(!TryGetGalaxyAccountRegistry(frame,out var registry))return false;
            var type=Read<uint>(image+0x5F53244);if(type==0)return false;
            var component=RegisteredComponent(registry,type);
            if(component<0x10058||Pointer(component)!=image+0x525EF48||
               !CopyGalaxyMissionsManager(component-0x58,Pointer(component-0x48),image,out var items))return false;
            if(!TryGetGalaxyAccountRegistry(frame,out var latest)||latest!=registry||Current().entity!=frame.entity||
               Read<uint>(image+0x5F53244)!=type||RegisteredComponent(registry,type)!=component)return false;
            result=new(frame.tick,frame.entity,items);return true;
        }
        catch{return false;}
    }
}