using System.Buffers.Binary;
using System.Reflection.PortableExecutable;
namespace XFEEliteDangerous.OfflineRuntime;
/// Maps native return addresses to the pinned image's actual unwind function ranges.
internal static class NativeCodeMap
{
private sealed record Range(uint Begin,uint End,uint Family);
private static Range[] ranges=[];
private static nint image;
private static int size;
internal static void Initialize(string executable,nint module)
{
using var file=File.OpenRead(executable);using var pe=new PEReader(file);
image=module;size=pe.PEHeaders.PEHeader!.SizeOfImage;
var directory=pe.PEHeaders.PEHeader.ExceptionTableDirectory;
var bytes=pe.GetSectionData(directory.RelativeVirtualAddress).GetContent(0,directory.Size).AsSpan();
var result=new List();
for(var index=0;index+12<=bytes.Length;index+=12)
{
var begin=BinaryPrimitives.ReadUInt32LittleEndian(bytes[index..]);
var end=BinaryPrimitives.ReadUInt32LittleEndian(bytes[(index+4)..]);
var unwind=BinaryPrimitives.ReadUInt32LittleEndian(bytes[(index+8)..]);
if(begin>=end||end>size)continue;
var family=begin;
for(var depth=0;depth<16;depth++)
{
var header=pe.GetSectionData((int)unwind).GetContent(0,4).AsSpan();
if((header[0]>>3&4)==0)break;
var chained=pe.GetSectionData((int)unwind+4+((header[2]+1)&~1)*2).GetContent(0,12).AsSpan();
family=BinaryPrimitives.ReadUInt32LittleEndian(chained);
unwind=BinaryPrimitives.ReadUInt32LittleEndian(chained[8..]);
}
result.Add(new(begin,end,family));
}
ranges=result.OrderBy(value=>value.Begin).ToArray();
}
internal static string Rva(nint address)=>address>=image&&address Locate(nint address)
{
var result=new Dictionary {["nativeReturnRva"]=Rva(address),["nativeImage"]="EliteDangerous64.exe"};
if(address=image+size)return result;
var rva=(uint)(address-image);var low=0;var high=ranges.Length-1;
while(low<=high)
{
var middle=(low+high)/2;var range=ranges[middle];
if(rva=range.End)low=middle+1;
else
{
result["nativeFunctionRva"]="0x"+range.Family.ToString("X");
result["nativeFunctionOffset"]="0x"+(rva-range.Family).ToString("X");
result["nativeSymbol"]="sub_"+range.Family.ToString("X8");
break;
}
}
return result;
}
}