using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Nodes;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
// Data replay only. Never selects targets, leases controls, or installs a DLL.
internal static class ReferencePreview
{
public static async Task RunAsync(int pid,string geometryPath,string reportPath)
{
if(File.Exists(reportPath))throw new IOException("Use a new reference preview report path.");
using var source=JsonDocument.Parse(await File.ReadAllTextAsync(geometryPath));var data=source.RootElement;
if(data.GetProperty("pid").GetInt32()!=pid||DateTimeOffset.UtcNow-data.GetProperty("observedAt").GetDateTimeOffset()>TimeSpan.FromSeconds(30))throw new InvalidDataException("Reference replay requires a fresh snapshot of the selected process.");
using var game=Process.GetProcessById(pid);var start=game.StartTime;
var pipe=new NavigationPipeClient(pid);var hello=await pipe.CallAsync(new{op="describe"});var raw=await pipe.CallAsync(new{op="read"});
if(hello.GetProperty("gameSha256").GetString()!=data.GetProperty("gameSha256").GetString()||raw.GetProperty("entityId").GetString()!=data.GetProperty("entityId").GetString())throw new InvalidDataException("Reference replay process/build/entity changed.");
Vector3d V(JsonElement array)=>new(array[0].GetDouble(),array[1].GetDouble(),array[2].GetDouble());
if((V(raw.GetProperty("position"))-V(data.GetProperty("rawPosition"))).Length>1)throw new InvalidDataException("Player moved between reference and telemetry observations.");
var directory=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),"Saved Games","Frontier Developments","Elite Dangerous");
await using var journal=new JournalMonitor(directory);await journal.StartAsync();var player=journal.State;
if(player.Navigation.CurrentSystemAddress.ToString()!=data.GetProperty("system").GetString())throw new InvalidDataException("Journal and replay reference disagree.");
var position=V(data.GetProperty("shipMatrix")[3]);var bodies=data.GetProperty("bodies").EnumerateArray().ToArray();
var primary=bodies.Where(b=>b.GetProperty("kind").GetInt32()==1).MinBy(b=>(position-V(b.GetProperty("matrix")[3])).Length);
var origin=V(primary.GetProperty("matrix")[3]);
object Array(Vector3d value)=>new[]{value.X,value.Y,value.Z};
var geometry=new{fresh=true,referenceFrameVerified=true,referenceBodyKind=data.GetProperty("referenceBodyKind").GetInt32(),
worldAddress=data.GetProperty("referenceWorldAddress").GetString(),bodyAddress=primary.GetProperty("address").GetString(),bodyKind=1,starType=primary.GetProperty("starType").GetInt32(),
exclusionRadius=primary.GetProperty("exclusionRadius").GetDouble(),position=Array(position-origin),
right=data.GetProperty("shipMatrix")[0],up=data.GetProperty("shipMatrix")[1],forward=data.GetProperty("shipMatrix")[2],
nearbyBodies=bodies.Select(b=>new{bodyAddress=b.GetProperty("address").GetString(),bodyKind=b.GetProperty("kind").GetInt32(),starType=b.GetProperty("starType"),
exclusionRadius=b.GetProperty("exclusionRadius").GetDouble(),centre=Array(V(b.GetProperty("matrix")[3])-origin)}).ToArray()};
var node=JsonNode.Parse(raw.GetRawText())!;node["referenceWorldAddress"]=data.GetProperty("referenceWorldAddress").GetString();
node["geometry"]=JsonSerializer.SerializeToNode(geometry,ToolboxSettings.JsonOptions);
using var replay=JsonDocument.Parse(node.ToJsonString());var now=DateTimeOffset.UtcNow;
var target=long.Parse(raw.GetProperty("fsd").GetProperty("targetAddress").GetString()!);
var next=player.Navigation.Route.FirstOrDefault(w=>w.SystemAddress==target)??throw new InvalidDataException("Actual target is not on the Journal route.");
var observation=new NativeFrameProjector(data.GetProperty("gameSha256").GetString()!).Project(replay.RootElement,player,next,.5,now);
var frame=observation.Frame;var decision=new FlightNavigator().Decide(frame,next,observation.Thermal,now);
if(game.HasExited||game.StartTime!=start)throw new InvalidOperationException("Selected process changed.");
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{processId=pid,loadedPlugin=hello.GetProperty("version").GetString(),
source="VM_READ orbital matrices replayed through the managed projector; no replacement native DLL loaded",system=player.SystemName,next=next.Name,
referenceKind=data.GetProperty("referenceBodyKind").GetInt32(),frame.Position,frame.InSupercruise,frame.SpeedMetresPerSecond,frame.OutwardDepartureRequired,
frame.FuelMain,frame.FuelNeededToNextRefuel,frame.HeatPercent,frame.NativeFullChargePeak,bodies=bodies.Length,
blockingBodies=frame.Obstacles.Append(frame.CurrentStar!).Where(b=>FlightGeometry.Obscures(b,frame.Position,frame.TargetPosition)).Select(b=>b.BodyId).ToArray(),
decision,referenceReplay=true,flightCommandsInvoked=false,gameRestartInvoked=false,screenUse=false,gameNativeFunctionsInvoked=false},new JsonSerializerOptions{WriteIndented=true}));
Console.WriteLine(await File.ReadAllTextAsync(reportPath));
}
}
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Nodes;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
// Data replay only. Never selects targets, leases controls, or installs a DLL.
internal static class ReferencePreview
{
public static async Task RunAsync(int pid,string geometryPath,string reportPath)
{
if(File.Exists(reportPath))throw new IOException("Use a new reference preview report path.");
using var source=JsonDocument.Parse(await File.ReadAllTextAsync(geometryPath));var data=source.RootElement;
if(data.GetProperty("pid").GetInt32()!=pid||DateTimeOffset.UtcNow-data.GetProperty("observedAt").GetDateTimeOffset()>TimeSpan.FromSeconds(30))throw new InvalidDataException("Reference replay requires a fresh snapshot of the selected process.");
using var game=Process.GetProcessById(pid);var start=game.StartTime;
var pipe=new NavigationPipeClient(pid);var hello=await pipe.CallAsync(new{op="describe"});var raw=await pipe.CallAsync(new{op="read"});
if(hello.GetProperty("gameSha256").GetString()!=data.GetProperty("gameSha256").GetString()||raw.GetProperty("entityId").GetString()!=data.GetProperty("entityId").GetString())throw new InvalidDataException("Reference replay process/build/entity changed.");
Vector3d V(JsonElement array)=>new(array[0].GetDouble(),array[1].GetDouble(),array[2].GetDouble());
if((V(raw.GetProperty("position"))-V(data.GetProperty("rawPosition"))).Length>1)throw new InvalidDataException("Player moved between reference and telemetry observations.");
var directory=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),"Saved Games","Frontier Developments","Elite Dangerous");
await using var journal=new JournalMonitor(directory);await journal.StartAsync();var player=journal.State;
if(player.Navigation.CurrentSystemAddress.ToString()!=data.GetProperty("system").GetString())throw new InvalidDataException("Journal and replay reference disagree.");
var position=V(data.GetProperty("shipMatrix")[3]);var bodies=data.GetProperty("bodies").EnumerateArray().ToArray();
var primary=bodies.Where(b=>b.GetProperty("kind").GetInt32()==1).MinBy(b=>(position-V(b.GetProperty("matrix")[3])).Length);
var origin=V(primary.GetProperty("matrix")[3]);
object Array(Vector3d value)=>new[]{value.X,value.Y,value.Z};
var geometry=new{fresh=true,referenceFrameVerified=true,referenceBodyKind=data.GetProperty("referenceBodyKind").GetInt32(),
worldAddress=data.GetProperty("referenceWorldAddress").GetString(),bodyAddress=primary.GetProperty("address").GetString(),bodyKind=1,starType=primary.GetProperty("starType").GetInt32(),
exclusionRadius=primary.GetProperty("exclusionRadius").GetDouble(),position=Array(position-origin),
right=data.GetProperty("shipMatrix")[0],up=data.GetProperty("shipMatrix")[1],forward=data.GetProperty("shipMatrix")[2],
nearbyBodies=bodies.Select(b=>new{bodyAddress=b.GetProperty("address").GetString(),bodyKind=b.GetProperty("kind").GetInt32(),starType=b.GetProperty("starType"),
exclusionRadius=b.GetProperty("exclusionRadius").GetDouble(),centre=Array(V(b.GetProperty("matrix")[3])-origin)}).ToArray()};
var node=JsonNode.Parse(raw.GetRawText())!;node["referenceWorldAddress"]=data.GetProperty("referenceWorldAddress").GetString();
node["geometry"]=JsonSerializer.SerializeToNode(geometry,ToolboxSettings.JsonOptions);
using var replay=JsonDocument.Parse(node.ToJsonString());var now=DateTimeOffset.UtcNow;
var target=long.Parse(raw.GetProperty("fsd").GetProperty("targetAddress").GetString()!);
var next=player.Navigation.Route.FirstOrDefault(w=>w.SystemAddress==target)??throw new InvalidDataException("Actual target is not on the Journal route.");
var observation=new NativeFrameProjector(data.GetProperty("gameSha256").GetString()!).Project(replay.RootElement,player,next,.5,now);
var frame=observation.Frame;var decision=new FlightNavigator().Decide(frame,next,observation.Thermal,now);
if(game.HasExited||game.StartTime!=start)throw new InvalidOperationException("Selected process changed.");
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{processId=pid,loadedPlugin=hello.GetProperty("version").GetString(),
source="VM_READ orbital matrices replayed through the managed projector; no replacement native DLL loaded",system=player.SystemName,next=next.Name,
referenceKind=data.GetProperty("referenceBodyKind").GetInt32(),frame.Position,frame.InSupercruise,frame.SpeedMetresPerSecond,frame.OutwardDepartureRequired,
frame.FuelMain,frame.FuelNeededToNextRefuel,frame.HeatPercent,frame.NativeFullChargePeak,bodies=bodies.Length,
blockingBodies=frame.Obstacles.Append(frame.CurrentStar!).Where(b=>FlightGeometry.Obscures(b,frame.Position,frame.TargetPosition)).Select(b=>b.BodyId).ToArray(),
decision,referenceReplay=true,flightCommandsInvoked=false,gameRestartInvoked=false,screenUse=false,gameNativeFunctionsInvoked=false},new JsonSerializerOptions{WriteIndented=true}));
Console.WriteLine(await File.ReadAllTextAsync(reportPath));
}
}