using System.Buffers.Binary;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Rendering;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public class GalaxyDataTests
{
private static byte[] Density()
{
var data=new byte[48+16*16*4];"XFEGDM2\0"u8.CopyTo(data);
BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(8),16);BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(12),16);
foreach(var (at,value) in new[]{(16,49985d),(24,24105d),(32,100000d),(40,100000d)})BinaryPrimitives.WriteDoubleLittleEndian(data.AsSpan(at),value);
BinaryPrimitives.WriteSingleLittleEndian(data.AsSpan(48),.75f);return data;
}
[Test] public void GalaxyRasterPreservesOriginalFullGalaxyFrameAndRejectsCorruptPixels()
{
var data=Density();var map=GalaxyDensityMap.Parse(data);
Check.Equal(49985d,map.OriginX);Check.Equal(24105d,map.OriginZ);Check.Equal(100000d,map.ExtentX);Check.That(map.FloatValues);
Check.Equal(.75f,BinaryPrimitives.ReadSingleLittleEndian(map.Values));
Check.Throws<InvalidDataException>(()=>GalaxyDensityMap.Parse(data[..^1]));
BinaryPrimitives.WriteSingleLittleEndian(data.AsSpan(48),float.NaN);
Check.Throws<InvalidDataException>(()=>GalaxyDensityMap.Parse(data));
BinaryPrimitives.WriteSingleLittleEndian(data.AsSpan(48),1.01f);
Check.Throws<InvalidDataException>(()=>GalaxyDensityMap.Parse(data));
}
[Test] public void OriginalDdsCubeRetainsSixFacesAndRejectsDepthOrPartialArrays()
{
var data=new byte[148+16*16*4*6];"DDS "u8.CopyTo(data);"DX10"u8.CopyTo(data.AsSpan(84));
foreach(var (at,value) in new[]{(4,124),(12,16),(16,16),(28,1),(76,32),(128,28),(132,3),(136,4),(140,1)})
BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(at),value);
for(var i=0;i<6;i++)data[148+i*16*16*4]=(byte)i;
var cube=GameSkyboxCube.Parse(data);Check.Equal(64,cube.RowPitch);Check.Equal(6,cube.Faces.Length);
for(var i=0;i<6;i++)Check.Equal((byte)i,cube.Faces[i][0]);
Check.Throws<InvalidDataException>(()=>GameSkyboxCube.Parse(data[..^1]));
BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(128),40);
Check.Throws<InvalidDataException>(()=>GameSkyboxCube.Parse(data));
BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(128),28);BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(140),2);
Check.Throws<InvalidDataException>(()=>GameSkyboxCube.Parse(data));
}
[Test] public void FuelDashesNeverAssumeRefuellingAndSupplyMarkersRequireActualScoop()
{
var ship=new ShipRecord{ShipId=7,ConfigurationAt=DateTimeOffset.UtcNow,Modules=[
new(){Slot="FrameShiftDrive",Item="int_hyperdrive_size6_class5",Enabled=true},
new(){Slot="Slot01_Size6",Item="int_fuelscoop_size6_class5",Enabled=true}]};
var player=new PlayerState{CurrentShipId=7,Ships=[ship],Navigation=new(){CurrentSystemAddress=1,MainFuel=9,
Route=[new(1,"Here",new(0,0,0),"G"),new(2,"Supply",new(10,0,0),"K"),new(3,"Neutron",new(20,0,0),"N"),new(4,"End",new(30,0,0),"M")]}};
var overlay=NavigationFuelOverlay.Build(player,.5);
Check.That(overlay.DashedLegs.SequenceEqual(new long[]{3,4}));Check.That(overlay.SupplySites.SequenceEqual(new long[]{1,2,4}));Check.Equal(2L,overlay.RecommendedSupply);
var noScoop=NavigationFuelOverlay.Build(player with{Ships=[ship with{Modules=ship.Modules.Take(1).ToArray()}]},.5);
Check.Equal(0,noScoop.SupplySites.Count);Check.That(noScoop.RecommendedSupply is null);
Check.Equal(0,NavigationFuelOverlay.Build(player with{Navigation=player.Navigation with{Route=[]}},.5).DashedLegs.Count);
}
[Test] public void SkyInspectionSeparatesSparseStarsFromLongSceneEdges()
{
const int size=128;var pixels=new byte[size*size*4];
for(var i=0;i<pixels.Length;i+=4){pixels[i]=pixels[i+1]=pixels[i+2]=12;pixels[i+3]=255;}
for(var y=12;y<110;y+=23)for(var x=13;x<110;x+=29){var p=(y*size+x)*4;pixels[p]=pixels[p+1]=pixels[p+2]=240;}
var stars=SkyboxPixels.Inspect(pixels,size,size);Check.That(stars.Mean>.001&&stars.LongestEdge<8&&stars.LargestEdgeComponent<32);
for(var y=40;y<90;y++)for(var x=10;x<118;x++){var p=(y*size+x)*4;pixels[p]=pixels[p+1]=pixels[p+2]=160;}
var scene=SkyboxPixels.Inspect(pixels,size,size);Check.That(scene.LongestEdge>80&&scene.LargestEdgeComponent>192);
Check.Equal(0d,SkyboxPixels.Inspect(new byte[size*size*4],size,size).Mean);
}
}
using System.Buffers.Binary;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Rendering;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public class GalaxyDataTests
{
private static byte[] Density()
{
var data=new byte[48+16*16*4];"XFEGDM2\0"u8.CopyTo(data);
BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(8),16);BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(12),16);
foreach(var (at,value) in new[]{(16,49985d),(24,24105d),(32,100000d),(40,100000d)})BinaryPrimitives.WriteDoubleLittleEndian(data.AsSpan(at),value);
BinaryPrimitives.WriteSingleLittleEndian(data.AsSpan(48),.75f);return data;
}
[Test] public void GalaxyRasterPreservesOriginalFullGalaxyFrameAndRejectsCorruptPixels()
{
var data=Density();var map=GalaxyDensityMap.Parse(data);
Check.Equal(49985d,map.OriginX);Check.Equal(24105d,map.OriginZ);Check.Equal(100000d,map.ExtentX);Check.That(map.FloatValues);
Check.Equal(.75f,BinaryPrimitives.ReadSingleLittleEndian(map.Values));
Check.Throws<InvalidDataException>(()=>GalaxyDensityMap.Parse(data[..^1]));
BinaryPrimitives.WriteSingleLittleEndian(data.AsSpan(48),float.NaN);
Check.Throws<InvalidDataException>(()=>GalaxyDensityMap.Parse(data));
BinaryPrimitives.WriteSingleLittleEndian(data.AsSpan(48),1.01f);
Check.Throws<InvalidDataException>(()=>GalaxyDensityMap.Parse(data));
}
[Test] public void OriginalDdsCubeRetainsSixFacesAndRejectsDepthOrPartialArrays()
{
var data=new byte[148+16*16*4*6];"DDS "u8.CopyTo(data);"DX10"u8.CopyTo(data.AsSpan(84));
foreach(var (at,value) in new[]{(4,124),(12,16),(16,16),(28,1),(76,32),(128,28),(132,3),(136,4),(140,1)})
BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(at),value);
for(var i=0;i<6;i++)data[148+i*16*16*4]=(byte)i;
var cube=GameSkyboxCube.Parse(data);Check.Equal(64,cube.RowPitch);Check.Equal(6,cube.Faces.Length);
for(var i=0;i<6;i++)Check.Equal((byte)i,cube.Faces[i][0]);
Check.Throws<InvalidDataException>(()=>GameSkyboxCube.Parse(data[..^1]));
BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(128),40);
Check.Throws<InvalidDataException>(()=>GameSkyboxCube.Parse(data));
BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(128),28);BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(140),2);
Check.Throws<InvalidDataException>(()=>GameSkyboxCube.Parse(data));
}
[Test] public void FuelDashesNeverAssumeRefuellingAndSupplyMarkersRequireActualScoop()
{
var ship=new ShipRecord{ShipId=7,ConfigurationAt=DateTimeOffset.UtcNow,Modules=[
new(){Slot="FrameShiftDrive",Item="int_hyperdrive_size6_class5",Enabled=true},
new(){Slot="Slot01_Size6",Item="int_fuelscoop_size6_class5",Enabled=true}]};
var player=new PlayerState{CurrentShipId=7,Ships=[ship],Navigation=new(){CurrentSystemAddress=1,MainFuel=9,
Route=[new(1,"Here",new(0,0,0),"G"),new(2,"Supply",new(10,0,0),"K"),new(3,"Neutron",new(20,0,0),"N"),new(4,"End",new(30,0,0),"M")]}};
var overlay=NavigationFuelOverlay.Build(player,.5);
Check.That(overlay.DashedLegs.SequenceEqual(new long[]{3,4}));Check.That(overlay.SupplySites.SequenceEqual(new long[]{1,2,4}));Check.Equal(2L,overlay.RecommendedSupply);
var noScoop=NavigationFuelOverlay.Build(player with{Ships=[ship with{Modules=ship.Modules.Take(1).ToArray()}]},.5);
Check.Equal(0,noScoop.SupplySites.Count);Check.That(noScoop.RecommendedSupply is null);
Check.Equal(0,NavigationFuelOverlay.Build(player with{Navigation=player.Navigation with{Route=[]}},.5).DashedLegs.Count);
}
[Test] public void SkyInspectionSeparatesSparseStarsFromLongSceneEdges()
{
const int size=128;var pixels=new byte[size*size*4];
for(var i=0;i<pixels.Length;i+=4){pixels[i]=pixels[i+1]=pixels[i+2]=12;pixels[i+3]=255;}
for(var y=12;y<110;y+=23)for(var x=13;x<110;x+=29){var p=(y*size+x)*4;pixels[p]=pixels[p+1]=pixels[p+2]=240;}
var stars=SkyboxPixels.Inspect(pixels,size,size);Check.That(stars.Mean>.001&&stars.LongestEdge<8&&stars.LargestEdgeComponent<32);
for(var y=40;y<90;y++)for(var x=10;x<118;x++){var p=(y*size+x)*4;pixels[p]=pixels[p+1]=pixels[p+2]=160;}
var scene=SkyboxPixels.Inspect(pixels,size,size);Check.That(scene.LongestEdge>80&&scene.LargestEdgeComponent>192);
Check.Equal(0d,SkyboxPixels.Inspect(new byte[size*size*4],size,size).Mean);
}
}