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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
namespace XFEEDToolBox.Plugin;

internal static unsafe partial class Navigation
{
    // Original 6891 image: 3B26700/3C415F0 (colour), 3C3F5A0/3C3F440
    // (luminosity/subclass palette), 3B28090/3B3E640 (size). These methods
    // copy only already loaded records. In particular, the original tree
    // operator 3C284E0 INSERTS a missing key, so it is never invoked here.
    internal readonly record struct GalaxyAppearance(uint typeRgba,uint? realisticRgba,float? colourBlend,
        uint rawScale,ushort classification,float size,float smallSize,bool? smallSizeActive)
    {
        internal bool? navRouteMembership {get;init;}
        internal int? navRouteMembershipMode {get;init;}
        internal int? navRouteViewMode {get;init;}
    }
    readonly record struct GalaxyAppearanceSource(nuint registry,nuint expectedImage,ulong tick,float? blend,bool? small);
    static GalaxyAppearanceSource galaxyAppearanceSource;

    static GalaxyAppearanceSource GalaxyAppearanceSettings(GalaxyServices services)
    {
        var now=GetTickCount64();var cached=galaxyAppearanceSource;
        if(cached.registry==services.registry&&cached.expectedImage==image&&now-cached.tick<100)return cached;
        float? blend=null;bool? small=null;
        try
        {
            // 3A06740: base+58 main interface; base+60 type interface.
            // 3A55CF0 (main vtable+D8) returns float(main+99C).
            var component=RegisteredComponent(services.registry,Read<uint>(image+0x5FA0A10));
            if(component>=0x10060&&Pointer(component)==image+0x551F518&&Pointer(component-8)==image+0x551F3D8&&
               Pointer(component-0x60)==image+0x551F2A8&&Pointer(component-0x58)==image+0x551F2D8)
            {
                var value=Read<float>(component-8+0x99C);
                if(float.IsFinite(value)&&value>=0&&value<=1)blend=value;
            }
            // 3B3E640 calls the same settings interface+158. The live
            // implementation 33D8280 reads byte(iface+624), while the
            // alternate 552AE90 implementation returns constant true.
            component=RegisteredComponent(services.registry,Read<uint>(image+0x5F05DD4));
            if(component>=0x10000)
            {
                var table=Pointer(component);
                if(table==image+0x552A9B8&&Pointer(table+0x158)==image+0x33D8220)
                {
                    var value=Read<byte>(component+0x624);if(value<=1)small=value!=0;
                }
                else if(table==image+0x552AE60&&Pointer(table+0x158)==image+0x201130)small=true;
            }
        }
        catch{}
        var result=new GalaxyAppearanceSource(services.registry,image,now,blend,small);
        galaxyAppearanceSource=result;return result;
    }

    static bool GalaxyAppearanceTree(nuint tree,int type,byte subclass,out uint rgba)
    {
        rgba=0;
        try
        {
            var count=Read<ulong>(tree+8);var root=Pointer(tree+0x10);
            if(count==0||count>65536||root<0x10000)return false;
            var node=root;
            for(var step=0;step<128&&node!=0;step++)
            {
                if(node<0x10000||node==tree+0x10)return false;
                var key=Read<int>(node+0x1C);var minor=Read<byte>(node+0x20);
                if(type==key&&subclass==minor)
                {
                    var value=Read<uint>(node+0x2C);
                    if(Pointer(tree+0x10)!=root||Read<ulong>(tree+8)!=count||
                       Read<int>(node+0x1C)!=key||Read<byte>(node+0x20)!=minor||Read<uint>(node+0x2C)!=value)return false;
                    rgba=value;return true;
                }
                var next=Pointer(node+(type<key||type==key&&subclass<minor?8u:0u));
                if(next==node)return false;node=next;
            }
        }
        catch{}
        return false;
    }

    static int GalaxyAppearanceLuminosityGroup(ushort classification)
    {
        var luminosity=(classification&0x7C00)>>10;
        return luminosity switch
        {
            0 or 2=>1,1 or 3 or 5=>2,4=>3,
            >=6 and <=9=>7,>=10 and <=13=>4,>=14 and <=17=>5,
            >=18 and <=22=>0,23=>6,_=>-1
        };
    }

    static bool GalaxyAppearanceTemperatureKey(nuint expectedImage,uint rawTemperature,out int type,out byte subclass)
    {
        type=0;subclass=0;
        try
        {
            var first=Read<uint>(expectedImage+0x5FDF490);var second=Read<uint>(expectedImage+0x5FDF494);
            if(first==0||second<=first)return false;
            ulong key;
            if(rawTemperature>=second)key=Read<ulong>(expectedImage+0x5FDF4C8);
            else
            {
                var index=rawTemperature<first?0u:1u;
                var descriptor=expectedImage+0x5FDF498+index*0x18;
                var minimum=Read<uint>(descriptor);var values=Pointer(descriptor+8);
                var step=Read<uint>(expectedImage+0x5539320+index*4);
                if(step is not(10 or 100)||values<0x10000)return false;
                var bucket=rawTemperature/step;
                if(bucket<minimum||bucket-minimum>4095)return false;
                // This is the exact address expression used by 3C3F5A0.
                // Native reads are guarded by XfeCopy; no search/load occurs.
                var at=values+(nuint)(bucket-minimum)*8;key=Read<ulong>(at);
                if(Pointer(descriptor+8)!=values||Read<uint>(descriptor)!=minimum||Read<ulong>(at)!=key)return false;
            }
            type=(int)(uint)key;subclass=(byte)(key>>32);
            return type is >=0 and <44&&subclass<=9&&Read<uint>(expectedImage+0x5FDF490)==first&&Read<uint>(expectedImage+0x5FDF494)==second;
        }
        catch{return false;}
    }

    static bool GalaxyAppearancePhysicalColour(nuint expectedImage,ushort classification,uint rawTemperature,uint typeRgba,out uint rgba)
    {
        rgba=typeRgba;var group=GalaxyAppearanceLuminosityGroup(classification);
        if(group<0)return true;
        var type=classification&63;var subclass=(byte)Math.Min((classification>>6)&15,9);
        if(type is 9 or 10&&!GalaxyAppearanceTemperatureKey(expectedImage,rawTemperature,out type,out subclass))return false;
        var offset=group switch{0=>0x5FDF460u,1=>0x5FDF2A0u,2=>0x5FDF100u,3=>0x5FDF4E0u,
            4=>0x5FDF190u,5=>0x5FDF330u,6=>0x5FDF560u,7=>0x5FDF3E0u,_=>0u};
        return GalaxyAppearanceTree(expectedImage+offset,type,subclass,out rgba);
    }

    static uint GalaxyAppearanceMix(uint typeRgba,uint physicalRgba,float blend)
    {
        var result=0u;var inverse=1f-blend;
        for(var channel=0;channel<4;channel++)
        {
            var shift=channel*8;
            var first=((typeRgba>>shift)&255)*(1f/255f);
            var second=((physicalRgba>>shift)&255)*(1f/255f);
            var value=(uint)(int)Math.Clamp((first*inverse+second*blend)*255f,0f,255f);
            result|=value<<shift;
        }
        return result;
    }

    static float GalaxyAppearanceSize(uint rawScale,int type,bool small)
    {
        // Raw field +34 is kept under its original neutral name. Original
        // 48B5BC0 is the scalar natural logarithm (near-one path x-x^2/2...).
        var value=type<40||type==42?rawScale*(1f/32768f):type==40?rawScale*(1f/32768f)*1.4e-6f:1.1920928955078125e-7f;
        var log=MathF.Log(value);var normal=(Math.Clamp(log,-3f,8f)+3f)/11f;
        return normal*(small?1.5f-.05f:1.2f-.3f)+(small?.05f:.3f);
    }

    static bool TryCopyGalaxyAppearance(GalaxyServices services,nuint compact,out GalaxyAppearance result)
    {
        result=default;
        if(!Enabled()||!inGame||owningThread==0||GetCurrentThreadId()!=owningThread||compact<0x10000||services.registry<0x10000)return false;
        try
        {
            var id=Read<ulong>(compact);var classification=Read<ushort>(compact+0x40);var type=classification&63;
            if(id==0||id>=0x80000000000000UL||type>=48)return false;
            var temperature=Read<uint>(compact+0x30);var rawScale=Read<uint>(compact+0x34);
            var typeColour=Read<uint>(image+0x5539330+(nuint)type*4);
            var source=GalaxyAppearanceSettings(services);uint? realistic=null;
            if(source.blend is{} blend&&GalaxyAppearancePhysicalColour(image,classification,temperature,typeColour,out var physical))
                realistic=GalaxyAppearanceMix(typeColour,physical,blend);
            if(Read<ulong>(compact)!=id||Read<ushort>(compact+0x40)!=classification||
               Read<uint>(compact+0x30)!=temperature||Read<uint>(compact+0x34)!=rawScale)return false;
            var membership=ReadGalaxyNavRouteMembership(services,id,out var routeViewMode);
            result=new(typeColour,realistic,source.blend,rawScale,classification,
                GalaxyAppearanceSize(rawScale,type,false),GalaxyAppearanceSize(rawScale,type,true),source.small)
                {navRouteMembership=membership,navRouteViewMode=routeViewMode,navRouteMembershipMode=routeViewMode is{} view?view==1?1:0:null};
            return true;
        }
        catch{return false;}
    }
}