using System.Text.Json;
namespace XFEEDToolBox.Plugin;
internal static unsafe partial class Navigation
{
internal sealed record GameRouteOptions(int? mode,bool? useJetConeBoost,int? fsdBoostLevel,float? virtualCargoMass,uint? starClassMask=null,GalaxyRouteMapFilterRequest? mapFilter=null);
internal readonly record struct GameRouteOptionsSnapshot(int mode,bool jetCone,int boostLevel,float cargoMass,bool boostEnabled);
static GameRouteOptions? ParseGameRouteOptions(JsonElement request)
{
if(!request.TryGetProperty("routeOptions",out var input)||input.ValueKind==JsonValueKind.Null)return null;
if(input.ValueKind!=JsonValueKind.Object)throw new InvalidDataException("Invalid original route options");
int? Integer(string name,int maximum)
{
if(!input.TryGetProperty(name,out var value)||value.ValueKind==JsonValueKind.Null)return null;
if(value.ValueKind!=JsonValueKind.Number||!value.TryGetInt32(out var number)||number<0||number>maximum)
throw new InvalidDataException("Invalid original route option: "+name);
return number;
}
var mode=Integer("mode",2);var boost=Integer("fsdBoostLevel",3);var starMask=Integer("starClassMask",0xFFFF);bool? jet=null;float? cargo=null;
if(input.TryGetProperty("useJetConeBoost",out var j)&&j.ValueKind!=JsonValueKind.Null)
{if(j.ValueKind is not(JsonValueKind.True or JsonValueKind.False))throw new InvalidDataException("Invalid jet cone option");jet=j.GetBoolean();}
if(input.TryGetProperty("virtualCargoMass",out var c)&&c.ValueKind!=JsonValueKind.Null)
{if(c.ValueKind!=JsonValueKind.Number||!c.TryGetSingle(out var mass)||!float.IsFinite(mass)||mass is <0 or >2048)throw new InvalidDataException("Invalid route cargo mass");cargo=mass;}
if(boost is not null&&mode!=2)throw new InvalidDataException("FSD boost level requires original boost mode");
var mapFilter=ParseGalaxyRouteMapFilter(input);
return mode is null&&boost is null&&jet is null&&cargo is null&&starMask is null&&mapFilter is null?null:new(mode,jet,boost,cargo,(uint?)starMask,mapFilter);
}
// Original Settings+1F0 ready branch: active profile stride248, uint array54.
// No original getter, fallback, loader or setter is called by this reader.
static bool CopyGameRouteJetCone(nuint settings,nuint expectedImage,out bool jet)
{
jet=false;
try
{
if(settings<0x10000)return false;var table=Pointer(settings);
if(table<0x10000||Pointer(table+0x1F0)!=expectedImage+0x2601B60||Pointer(table+0x200)!=expectedImage+0x2646760||Read<int>(settings+0x5F8)!=2)return false;
var count=Read<uint>(settings+0x530);var index=Read<uint>(settings+0x560);var rows=Pointer(settings+0x538);
if(count is <1 or >128||index>=count||rows<0x10000)return false;
var at=rows+(nuint)index*0x248+0x54+29*4;var value=Read<uint>(at);
if(value>1||Pointer(settings)!=table||Read<int>(settings+0x5F8)!=2||Read<uint>(settings+0x530)!=count||
Read<uint>(settings+0x560)!=index||Pointer(settings+0x538)!=rows||Read<uint>(at)!=value)return false;
jet=value!=0;return true;
}
catch{return false;}
}
static bool CopyGameRouteOptions(nuint component,nuint expectedImage,out nuint controller,out GameRouteOptionsSnapshot snapshot)
{
controller=0;snapshot=default;
try
{
if(component<0x10058||Pointer(component)!=expectedImage+0x551FE98||
Pointer(expectedImage+0x551FE98)!=expectedImage+0x3A4C030)return false;
var main=component+0x30;var table=expectedImage+0x551FFC0;
if(Pointer(main)!=table||Pointer(table+0xF0)!=expectedImage+0x3A6AEB0||Pointer(table+0xF8)!=expectedImage+0x3A6AFA0||
Pointer(table+0x100)!=expectedImage+0x3A6AE10||Pointer(table+0x108)!=expectedImage+0x3A6AD80)return false;
var settings=Pointer(main+0x1B8);if(!CopyGameRouteJetCone(settings,expectedImage,out var jet))return false;
var mode=Read<int>(main+0x5AC);var cargo=Read<float>(main+0x5B0);var active=Read<byte>(main+0x5B4);var boost=Read<int>(main+0x5B8);
if(mode is <0 or >2||!float.IsFinite(cargo)||cargo is <0 or >2048||active>1||boost is <0 or >3)return false;
if(Pointer(component)!=expectedImage+0x551FE98||Pointer(main)!=table||Pointer(main+0x1B8)!=settings||
Read<int>(main+0x5AC)!=mode||Read<float>(main+0x5B0)!=cargo||Read<byte>(main+0x5B4)!=active||Read<int>(main+0x5B8)!=boost)return false;
controller=main;snapshot=new(mode,jet,boost,cargo,active!=0);return true;
}
catch{controller=0;snapshot=default;return false;}
}
static bool GameRouteOptionsAllowed(nuint main,nuint expectedImage,GameRouteOptions options)
{
// All four original setters reject view1; only the mode setter also
// rejects view3. Preserve that distinction instead of blanket gating.
if(Read<int>(main+0x220)!=-1)return true;
var navigation=Pointer(main+0x228);if(navigation==0)return true;
var view=CopyGalaxyNavRouteViewMode(navigation,expectedImage);
return view is not null and not 1&&(options.mode is null||view!=3);
}
static bool ApplyGameRouteOptions(Snapshot frame,GameRouteOptions options)
{
if(!Enabled()||!inGame||GetCurrentThreadId()!=owningThread||!frame.valid||!FindGalaxyServices(frame,out var services))return false;
var type=Read<uint>(image+0x5FDB224);var component=RegisteredComponent(services.registry,type);
if(!CopyGameRouteOptions(component,image,out var main,out _)||!GameRouteOptionsAllowed(main,image,options))return false;
if(options.starClassMask is not null&&!CopyGalaxyRouteStarClassState(main,image,out _))return false;
if(options.mapFilter is{} map&&!CopyGalaxyRouteCheckboxState(main,image,map,out _))return false;
bool CurrentController()=>Current() is{valid:true} currentFrame&¤tFrame.entity==frame.entity&&Read<uint>(image+0x5FDB224)==type&&
RegisteredComponent(services.registry,type)==component&&CopyGameRouteOptions(component,image,out var current,out _)&¤t==main&&GameRouteOptionsAllowed(current,image,options);
// Invoke the four actual current UI setters on their owning thread,
// with the recovered integer/bool/XMM1 ABIs. Never write game fields.
if(options.virtualCargoMass is{} cargo)
{if(!CurrentController())return false;((delegate* unmanaged<nuint,float,void>)(image+0x3A6AD80))(main,cargo);}
if(options.fsdBoostLevel is{} level)
{if(!CurrentController())return false;((delegate* unmanaged<nuint,float,void>)(image+0x3A6AE10))(main,level);}
if(options.useJetConeBoost is{} jet)
{if(!CurrentController())return false;((delegate* unmanaged<nuint,byte,void>)(image+0x3A6AFA0))(main,jet?(byte)1:(byte)0);}
// Boost's setter enables its mode flag. Apply the explicit mode last,
// just as the original radio selection does, then verify every override.
if(options.mode is{} mode)
{if(!CurrentController())return false;((delegate* unmanaged<nuint,int,void>)(image+0x3A6AEB0))(main,mode);}
if(!CurrentController()||!CopyGameRouteOptions(component,image,out _,out var actual))return false;
return (options.mode is null||actual.mode==options.mode)&&
(options.virtualCargoMass is null||actual.cargoMass==options.virtualCargoMass)&&
(options.useJetConeBoost is null||actual.jetCone==options.useJetConeBoost)&&
(options.fsdBoostLevel is null||actual.boostLevel==options.fsdBoostLevel&&actual.boostEnabled)&&
(options.starClassMask is not{} mask||ApplyGalaxyStarClassRouteFilter(frame,mask))&&
(options.mapFilter is not{} mapFilter||ApplyGalaxyRouteMapFilter(frame,mapFilter));
}
}
using System.Text.Json;
namespace XFEEDToolBox.Plugin;
internal static unsafe partial class Navigation
{
internal sealed record GameRouteOptions(int? mode,bool? useJetConeBoost,int? fsdBoostLevel,float? virtualCargoMass,uint? starClassMask=null,GalaxyRouteMapFilterRequest? mapFilter=null);
internal readonly record struct GameRouteOptionsSnapshot(int mode,bool jetCone,int boostLevel,float cargoMass,bool boostEnabled);
static GameRouteOptions? ParseGameRouteOptions(JsonElement request)
{
if(!request.TryGetProperty("routeOptions",out var input)||input.ValueKind==JsonValueKind.Null)return null;
if(input.ValueKind!=JsonValueKind.Object)throw new InvalidDataException("Invalid original route options");
int? Integer(string name,int maximum)
{
if(!input.TryGetProperty(name,out var value)||value.ValueKind==JsonValueKind.Null)return null;
if(value.ValueKind!=JsonValueKind.Number||!value.TryGetInt32(out var number)||number<0||number>maximum)
throw new InvalidDataException("Invalid original route option: "+name);
return number;
}
var mode=Integer("mode",2);var boost=Integer("fsdBoostLevel",3);var starMask=Integer("starClassMask",0xFFFF);bool? jet=null;float? cargo=null;
if(input.TryGetProperty("useJetConeBoost",out var j)&&j.ValueKind!=JsonValueKind.Null)
{if(j.ValueKind is not(JsonValueKind.True or JsonValueKind.False))throw new InvalidDataException("Invalid jet cone option");jet=j.GetBoolean();}
if(input.TryGetProperty("virtualCargoMass",out var c)&&c.ValueKind!=JsonValueKind.Null)
{if(c.ValueKind!=JsonValueKind.Number||!c.TryGetSingle(out var mass)||!float.IsFinite(mass)||mass is <0 or >2048)throw new InvalidDataException("Invalid route cargo mass");cargo=mass;}
if(boost is not null&&mode!=2)throw new InvalidDataException("FSD boost level requires original boost mode");
var mapFilter=ParseGalaxyRouteMapFilter(input);
return mode is null&&boost is null&&jet is null&&cargo is null&&starMask is null&&mapFilter is null?null:new(mode,jet,boost,cargo,(uint?)starMask,mapFilter);
}
// Original Settings+1F0 ready branch: active profile stride248, uint array54.
// No original getter, fallback, loader or setter is called by this reader.
static bool CopyGameRouteJetCone(nuint settings,nuint expectedImage,out bool jet)
{
jet=false;
try
{
if(settings<0x10000)return false;var table=Pointer(settings);
if(table<0x10000||Pointer(table+0x1F0)!=expectedImage+0x2601B60||Pointer(table+0x200)!=expectedImage+0x2646760||Read<int>(settings+0x5F8)!=2)return false;
var count=Read<uint>(settings+0x530);var index=Read<uint>(settings+0x560);var rows=Pointer(settings+0x538);
if(count is <1 or >128||index>=count||rows<0x10000)return false;
var at=rows+(nuint)index*0x248+0x54+29*4;var value=Read<uint>(at);
if(value>1||Pointer(settings)!=table||Read<int>(settings+0x5F8)!=2||Read<uint>(settings+0x530)!=count||
Read<uint>(settings+0x560)!=index||Pointer(settings+0x538)!=rows||Read<uint>(at)!=value)return false;
jet=value!=0;return true;
}
catch{return false;}
}
static bool CopyGameRouteOptions(nuint component,nuint expectedImage,out nuint controller,out GameRouteOptionsSnapshot snapshot)
{
controller=0;snapshot=default;
try
{
if(component<0x10058||Pointer(component)!=expectedImage+0x551FE98||
Pointer(expectedImage+0x551FE98)!=expectedImage+0x3A4C030)return false;
var main=component+0x30;var table=expectedImage+0x551FFC0;
if(Pointer(main)!=table||Pointer(table+0xF0)!=expectedImage+0x3A6AEB0||Pointer(table+0xF8)!=expectedImage+0x3A6AFA0||
Pointer(table+0x100)!=expectedImage+0x3A6AE10||Pointer(table+0x108)!=expectedImage+0x3A6AD80)return false;
var settings=Pointer(main+0x1B8);if(!CopyGameRouteJetCone(settings,expectedImage,out var jet))return false;
var mode=Read<int>(main+0x5AC);var cargo=Read<float>(main+0x5B0);var active=Read<byte>(main+0x5B4);var boost=Read<int>(main+0x5B8);
if(mode is <0 or >2||!float.IsFinite(cargo)||cargo is <0 or >2048||active>1||boost is <0 or >3)return false;
if(Pointer(component)!=expectedImage+0x551FE98||Pointer(main)!=table||Pointer(main+0x1B8)!=settings||
Read<int>(main+0x5AC)!=mode||Read<float>(main+0x5B0)!=cargo||Read<byte>(main+0x5B4)!=active||Read<int>(main+0x5B8)!=boost)return false;
controller=main;snapshot=new(mode,jet,boost,cargo,active!=0);return true;
}
catch{controller=0;snapshot=default;return false;}
}
static bool GameRouteOptionsAllowed(nuint main,nuint expectedImage,GameRouteOptions options)
{
// All four original setters reject view1; only the mode setter also
// rejects view3. Preserve that distinction instead of blanket gating.
if(Read<int>(main+0x220)!=-1)return true;
var navigation=Pointer(main+0x228);if(navigation==0)return true;
var view=CopyGalaxyNavRouteViewMode(navigation,expectedImage);
return view is not null and not 1&&(options.mode is null||view!=3);
}
static bool ApplyGameRouteOptions(Snapshot frame,GameRouteOptions options)
{
if(!Enabled()||!inGame||GetCurrentThreadId()!=owningThread||!frame.valid||!FindGalaxyServices(frame,out var services))return false;
var type=Read<uint>(image+0x5FDB224);var component=RegisteredComponent(services.registry,type);
if(!CopyGameRouteOptions(component,image,out var main,out _)||!GameRouteOptionsAllowed(main,image,options))return false;
if(options.starClassMask is not null&&!CopyGalaxyRouteStarClassState(main,image,out _))return false;
if(options.mapFilter is{} map&&!CopyGalaxyRouteCheckboxState(main,image,map,out _))return false;
bool CurrentController()=>Current() is{valid:true} currentFrame&¤tFrame.entity==frame.entity&&Read<uint>(image+0x5FDB224)==type&&
RegisteredComponent(services.registry,type)==component&&CopyGameRouteOptions(component,image,out var current,out _)&¤t==main&&GameRouteOptionsAllowed(current,image,options);
// Invoke the four actual current UI setters on their owning thread,
// with the recovered integer/bool/XMM1 ABIs. Never write game fields.
if(options.virtualCargoMass is{} cargo)
{if(!CurrentController())return false;((delegate* unmanaged<nuint,float,void>)(image+0x3A6AD80))(main,cargo);}
if(options.fsdBoostLevel is{} level)
{if(!CurrentController())return false;((delegate* unmanaged<nuint,float,void>)(image+0x3A6AE10))(main,level);}
if(options.useJetConeBoost is{} jet)
{if(!CurrentController())return false;((delegate* unmanaged<nuint,byte,void>)(image+0x3A6AFA0))(main,jet?(byte)1:(byte)0);}
// Boost's setter enables its mode flag. Apply the explicit mode last,
// just as the original radio selection does, then verify every override.
if(options.mode is{} mode)
{if(!CurrentController())return false;((delegate* unmanaged<nuint,int,void>)(image+0x3A6AEB0))(main,mode);}
if(!CurrentController()||!CopyGameRouteOptions(component,image,out _,out var actual))return false;
return (options.mode is null||actual.mode==options.mode)&&
(options.virtualCargoMass is null||actual.cargoMass==options.virtualCargoMass)&&
(options.useJetConeBoost is null||actual.jetCone==options.useJetConeBoost)&&
(options.fsdBoostLevel is null||actual.boostLevel==options.fsdBoostLevel&&actual.boostEnabled)&&
(options.starClassMask is not{} mask||ApplyGalaxyStarClassRouteFilter(frame,mask))&&
(options.mapFilter is not{} mapFilter||ApplyGalaxyRouteMapFilter(frame,mapFilter));
}
}