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;}
}
}
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;}
}
}