using System.Diagnostics;
using System.Text.Json;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Plugins;
using XFEEDToolBox.Core.Galaxy;
try
{
if(args.Length==4&&args[0]=="accept-trade-pause"&&args[3]=="--open-verified-pause")
{await TradePauseRecoveryAcceptance.RunAsync(int.Parse(args[1]),args[2],openVerifiedPause:true);return;}
if(args.Length==3&&args[0]=="accept-trade-pause")
{await TradePauseRecoveryAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0] is "start-and-verify-sco" or "start-and-verify-sco-cycles")
{
var directory=Path.GetFullPath(args[2]);if(Directory.Exists(directory))throw new IOException("Use a fresh complete SCO evidence directory.");Directory.CreateDirectory(directory);
await GameSessionAcceptance.StartAsync(args[1],Path.Combine(directory,"entry.json"));
using var currentGame=Process.GetProcessesByName("EliteDangerous64").Single(game=>string.Equals(game.MainModule?.FileName,Path.GetFullPath(args[1]),StringComparison.OrdinalIgnoreCase));
var testGameStarted=currentGame.StartTime.ToUniversalTime().Ticks;
try
{
await PrepareScoLeg.RunAsync(currentGame.Id,Path.Combine(directory,"prepare"),longContinuityLeg:true);
if(args[0]=="start-and-verify-sco-cycles")
await SCOThermalCycleAcceptance.RunAsync(currentGame.Id,Path.Combine(directory,"thermal-cycles"));
else
await SCOContinuityAcceptance.RunAsync(currentGame.Id,Path.Combine(directory,"continuity"));
}
finally
{
// A normal acceptance result is not a reason to close the user's game.
// Each fixture releases SCO and its own expiring input leases itself.
currentGame.Refresh();
if(!currentGame.HasExited&¤tGame.StartTime.ToUniversalTime().Ticks==testGameStarted
&&string.Equals(currentGame.MainModule?.FileName,Path.GetFullPath(args[1]),StringComparison.OrdinalIgnoreCase))
{
await File.WriteAllTextAsync(Path.Combine(directory,"test-session-retained.json"),JsonSerializer.Serialize(new
{processId=currentGame.Id,startedUtcTicks=testGameStarted,at=DateTimeOffset.UtcNow,gameClosed=false,
reason="Acceptance finished; only the fixture's SCO and expiring controls are released"},ToolboxSettings.JsonOptions));
}
}
return;
}
if(args.Length==4&&args[0]=="recover-current-session"&&args[3]=="--update-stationary-ship")
{await RecoverCurrentSession.RunAsync(args[1],args[2],updateStationaryShip:true);return;}
if(args.Length==4&&args[0]=="recover-current-session"&&args[3]=="--update-docked-ship")
{await RecoverCurrentSession.RunAsync(args[1],args[2],updateDockedShip:true);return;}
if(args.Length==3&&args[0]=="recover-current-session")
{await RecoverCurrentSession.RunAsync(args[1],args[2]);return;}
if(args.Length==4&&args[0]=="accept-flight-context-admission")
{await FlightContextAdmissionAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==5&&args[0]=="accept-persistent-docking-throttle")
{await PersistentDockingThrottleAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4]);return;}
if(args.Length==6&&args[0]=="accept-persistent-docking-throttle"&&args[5]=="--production-reset")
{await PersistentDockingThrottleAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4],productionReset:true);return;}
if(args.Length==2&&args[0]=="accept-sco-control"&&args[1]=="--self-test")
{SCOControlFlightAcceptance.SelfTest();return;}
if(args.Length==3&&args[0]=="accept-sco-control"&&args[1]=="--replay-preflight")
{SCOControlFlightAcceptance.ReplayPreflight(args[2]);return;}
if(args.Length==6&&args[0]=="accept-sco-control"&&args[1]=="--finish-return")
{await SCOControlFlightAcceptance.FinishReturnAsync(int.Parse(args[2]),args[3],args[4],args[5]);return;}
if(args.Length==4&&args[0]=="accept-sco-control")
{await SCOControlFlightAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==3&&args[0]=="verify-sco-continuity")
{await SCOContinuityAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="verify-sco-thermal-cycles")
{await SCOThermalCycleAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==4&&args[0]=="prepare-sco-leg"&&args[3]=="--long-continuity")
{await PrepareScoLeg.RunAsync(int.Parse(args[1]),args[2],longContinuityLeg:true);return;}
if(args.Length==3&&args[0]=="prepare-sco-leg")
{await PrepareScoLeg.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==4&&args[0]=="observe-sco-lifecycle")
{await CruiseLifecycleAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==5&&args[0]=="observe-sco-lifecycle")
{await CruiseLifecycleAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4]);return;}
if(args.Length==4&&args[0]=="finish-sco-lifecycle-return")
{await CruiseLifecycleAcceptance.FinishReturnAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==5&&args[0]=="finish-sco-lifecycle-return")
{await CruiseLifecycleAcceptance.FinishReturnAsync(int.Parse(args[1]),args[2],args[3],args[4]);return;}
if(args.Length==5&&args[0]=="finish-sco-lifecycle-current-return")
{await CruiseLifecycleAcceptance.FinishReturnAsync(int.Parse(args[1]),args[2],args[3],args[4],reuseCurrentSolo:true);return;}
if(args.Length==4&&args[0]=="finish-sco-lifecycle-maintenance")
{await CruiseLifecycleAcceptance.FinishMaintenanceAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="deactivate-cruise-control")
{await CruiseControlAcceptance.DeactivateAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="probe-cruise-control")
{await CruiseControlAcceptance.LoadAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==3&&args[0] is "read-cruise-control" or "watch-cruise-control")
{await CruiseControlAcceptance.ReadAsync(int.Parse(args[1]),args[2],args[0]=="watch-cruise-control");return;}
if(args.Length==4&&args[0]=="deactivate-cruise-observation")
{await CruiseObservationAcceptance.DeactivateAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="probe-cruise-observation")
{await CruiseObservationAcceptance.LoadAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==3&&args[0] is "read-cruise-observation" or "watch-cruise-observation")
{await CruiseObservationAcceptance.ReadAsync(int.Parse(args[1]),args[2],args[0]=="watch-cruise-observation");return;}
if(args.Length==3&&args[0]=="probe-docking-reset-bridge")
{await SessionRecoveryAcceptance.ProbeBridgeAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==4&&args[0] is "accept-original-docking-reset" or "accept-original-docking-ai")
{await OriginalDockingResetAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],requireNonzero:args[0]=="accept-original-docking-reset");return;}
if(args.Length==6&&args[0] is "accept-original-docking-reset-at" or "accept-original-docking-ai-at")
{await OriginalDockingResetAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],requireNonzero:args[0]=="accept-original-docking-reset-at",targetMarketId:long.Parse(args[4]),targetName:args[5]);return;}
if(args.Length==4&&args[0] is "observe-session-extension" or "deactivate-session-recovery")
{await SessionRecoveryAcceptance.InspectAsync(int.Parse(args[1]),args[2],args[3],args[0]=="deactivate-session-recovery");return;}
if(args.Length==4&&args[0] is "probe-session-recovery" or "leave-with-session-recovery")
{await SessionRecoveryAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[0]=="leave-with-session-recovery");return;}
if(args.Length==7&&args[0]=="docking-fixture-wait")
{await DockingFixtureWait.RunAsync(int.Parse(args[1]),long.Parse(args[2]),int.Parse(args[3]),long.Parse(args[4]),args[5],args[6]);return;}
if(args.Length==3&&args[0]=="docking-fixture")
{await DockingFixtureAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="prepare-cg-visual")
{await ManualVisualHandoff.PrepareAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="run-cg-visual")
{await ManualVisualHandoff.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="auto-cg-trade")
{await CgTradeLoop.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="service-cg-docked")
{await DockedMaintenance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="reconcile-rebuy")
{await RebuyReconciliation.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="inspect-docking-input-sources")
{
if(File.Exists(args[2]))throw new IOException("Use a new read-only docking input report.");
var result=await XFEEDToolBox.Core.Native.NativeDockingInputDiagnostics.ReadAsync(int.Parse(args[1]));
await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(result,ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(result,ToolboxSettings.JsonOptions));return;
}
if(args.Length==3&&args[0]=="start-session")
{
try{await GameSessionAcceptance.StartAsync(args[1],args[2]);}
catch(Exception error)
{
if(!File.Exists(args[2]))await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(new{passed=false,stage="session-entry-not-confirmed",failure=error.ToString(),screenUse=false,flightInputsSent=0,maintenanceHoldReleased=false},ToolboxSettings.JsonOptions));
throw;
}
return;
}
if(args.Length==5&&args[0]=="recover-disconnected-session")
{await GameSessionAcceptance.RecoverDisconnectedAsync(int.Parse(args[1]),args[2],args[3],args[4]);return;}
if(args.Length==4&&args[0]=="enter-session")
{await GameSessionAcceptance.StartAsync(args[2],args[3],int.Parse(args[1]));return;}
if(args.Length==3&&args[0]=="leave-session")
{await GameSessionAcceptance.LeaveAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="close-docked-for-protection")
{
if(File.Exists(args[2]))throw new IOException("Use a new confirmed protection-install report.");
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new XFEEDToolBox.Core.Data.JournalMonitor(settings.JournalDirectory);await journal.StartAsync();var state=journal.State;
if(!state.InGame||!state.Docked||state.CurrentShipId!=5||state.CargoCount!=0||!TravelPreparation.StatusConfirmed(state,DateTimeOffset.UtcNow)
||state.Status is null||(state.Status.Flags&1)==0||NavigationSafety.HasExternalThreat(state))throw new InvalidOperationException("Verified docked empty Corsair required before loading protection update.");
var driver=new XFEEDToolBox.Services.WindowsGameSessionDriver(()=>journal.State,()=>settings.PluginGameExecutable);
var target=await driver.FindTargetAsync(CancellationToken.None);if(target.ProcessId!=int.Parse(args[1]))throw new InvalidOperationException("Selected game instance changed.");
await driver.ValidateExitAsync(target,CancellationToken.None);await driver.TerminateAsync(target,CancellationToken.None);
for(var i=0;i<30&&!await driver.HasExitedAsync(target,CancellationToken.None);i++)await Task.Delay(100);
var exited=await driver.HasExitedAsync(target,CancellationToken.None);
await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(new{pid=target.ProcessId,target.StartedUtcTicks,exited,forced=true,state.Docked,state.CargoCount,state.CurrentShipId,flightInputsSent=0,tradeOrdersSent=0},ToolboxSettings.JsonOptions));
if(!exited)throw new IOException("Selected game has not exited; do not update the loaded protection module.");return;
}
if(args.Length==2&&args[0]=="inspect-session")
{Console.WriteLine(await GameSessionAcceptance.StatusAsync(int.Parse(args[1])));return;}
if(args.Length==2&&args[0]=="inspect-nav-safety")
{
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new XFEEDToolBox.Core.Data.JournalMonitor(settings.JournalDirectory);await journal.StartAsync();var state=journal.State;
var native=await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op="health.read"});
Console.WriteLine(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,state.InGame,state.SystemName,state.Navigation.CurrentSystemAddress,state.CurrentShipId,state.CargoCount,
state.InDanger,state.StatusAt,state.StatusReadAt,statusFlags=state.Status?.Flags,journalExternalThreat=state.Navigation.ExternalThreat,
unhealthyHull=NavigationSafety.UnhealthyHull(state),externalThreat=NavigationSafety.HasExternalThreat(state),
shipHull=state.Ships.FirstOrDefault(x=>x.ShipId==state.CurrentShipId)?.HullHealth,native,flightInputsSent=0},ToolboxSettings.JsonOptions));return;
}
if(args.Length==2&&args[0]=="inspect-trade-checkpoint")
{
var value=XFEEDToolBox.Core.Execution.TradeJourneyCheckpoints.Load(args[1]);
Console.WriteLine(JsonSerializer.Serialize(new{value.Commander,value.ShipId,value.StartedAt,bought=value.Bought,sold=value.Sold,gameAccess=false,ordersSubmitted=0},ToolboxSettings.JsonOptions));return;
}
if(args.Length==3&&args[0]=="one-ton-cg-journey")
{await TradeJourneyAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="local-catalog")
{
if(File.Exists(args[2]))throw new IOException("Use a new report path.");
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new XFEEDToolBox.Core.Data.JournalMonitor(settings.JournalDirectory);await journal.StartAsync();
using var source=new XFEEDToolBox.Core.Native.NativeCurrentSystemSource(int.Parse(args[1]),XFEEDToolBox.Core.Native.NativeSystemLocationReader.GameSha256,()=>journal.State);
var catalogue=await source.ReadLocationsAsync();
await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(catalogue,ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{catalogue.SystemName,count=catalogue.Locations.Count,gameCommandsInvoked=false}));return;
}
if(args.Length==2&&args[0]=="describe-local")
{Console.WriteLine(JsonSerializer.Serialize(await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op="local.read"})));return;}
if(args.Length==2&&args[0]=="commerce-inventory")
{Console.WriteLine(JsonSerializer.Serialize(await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op="commerce.inventory"})));return;}
if(args.Length==3&&args[0]=="commerce-docked-status")
{Console.WriteLine(JsonSerializer.Serialize(await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op="commerce.docked.status",marketId=uint.Parse(args[2])})));return;}
if(args.Length==4&&args[0]=="cg-read"&&args[2] is "all" or "mine")
{await NativeCommunityGoals.ReadAsync(int.Parse(args[1]),args[2]=="mine",args[3]);return;}
if(args.Length==3&&args[0]=="join-trade-cg")
{await NativeCommunityGoals.JoinTradeAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="cg-cargo-journey")
{
var pid=int.Parse(args[1]);using var guardian=await PersistentProtectionProcess.StartOrReuseAsync(pid,Directory.GetParent(Path.GetFullPath(args[2]))!.FullName,CancellationToken.None);
var previous=PersistentProtectionProcess.Current;PersistentProtectionProcess.Current=guardian;
try{await CgCargoJourney.RunAsync(pid,args[2]);}finally{PersistentProtectionProcess.Current=previous;}return;
}
if(args.Length==3&&args[0]=="accept-main-inputs")
{
if(File.Exists(args[2]))throw new IOException("Use a new evidence path.");
var pid=int.Parse(args[1]);var pipe=new NavigationPipeClient(pid);var hello=await pipe.CallAsync(new{op="describe"});
var channel=new NativeGameInputChannel(pid,hello.GetProperty("gameSha256").GetString()!,Path.GetFullPath("XFEEDToolBox/PluginPackage"));
using var timeout=new CancellationTokenSource(TimeSpan.FromSeconds(15));await channel.ConnectAsync(hello.GetProperty("owningThread").GetInt32(),timeout.Token);
var samples=new List<GameInputState>();
while(samples.Count<20)
{
var input=await channel.ReadAsync(timeout.Token);var health=await pipe.CallAsync(new{op="health.read"},timeout.Token);
if(!health.GetProperty("localPlayerShipVerified").GetBoolean()||input.EntityId.ToString()!=health.GetProperty("entityId").GetString())throw new InvalidDataException("Input observer differs from the independently verified main ship.");
samples.Add(input);await Task.Delay(30,timeout.Token);
}
await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(new{passed=true,pid,samples,flightInputsSent=0,osInputsSent=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{passed=true,samples=samples.Count,flightInputsSent=0}));return;
}
if(args.Length==2&&args[0]=="service-inventory")
{Console.WriteLine(JsonSerializer.Serialize(await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op="station.services.inventory"})));return;}
if(args.Length==4&&args[0]=="read-docked-market")
{
if(File.Exists(args[3]))throw new IOException("Use a new report path.");
var pipe=new NavigationPipeClient(int.Parse(args[1]));var market=uint.Parse(args[2]);
var queued=await pipe.CallAsync(new{op="commerce.market.read",marketId=market});
var generation=queued.GetProperty("generation").GetUInt64();using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(35));
while(true)
{
var result=await pipe.CallAsync(new{op="commerce.market.status",generation,maximum=50},stop.Token);
if(result.GetProperty("state").GetInt32()>=3)
{
var commodities=new List<JsonElement>();
if(result.GetProperty("state").GetInt32()==3)
for(var offset=0;offset<result.GetProperty("commodityCount").GetInt32();offset+=50)
{
var page=offset==0?result:await pipe.CallAsync(new{op="commerce.market.status",generation,offset,maximum=50},stop.Token);
foreach(var item in page.GetProperty("commodities").EnumerateArray())commodities.Add(item.Clone());
}
await File.WriteAllTextAsync(args[3],JsonSerializer.Serialize(new{result,commodities,screenUse=false,mutationsSent=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{state=result.GetProperty("state").GetInt32(),market,commodities=commodities.Count,screenUse=false,mutationsSent=0}));
if(result.GetProperty("state").GetInt32()!=3)throw new InvalidOperationException("Original read-only market request failed.");return;
}
await Task.Delay(100,stop.Token);
}
}
if(args.Length==5&&args[0]=="accept-native-market-order")
{await NativeMarketOrderAcceptance.RunAsync(int.Parse(args[1]),args[2],int.Parse(args[3]),args[4]);return;}
if(args.Length==4&&args[0]=="accept-native-refuel")
{await NativeStationServiceAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==5&&args[0]=="accept-native-repair")
{await NativeRepairAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4]);return;}
if(args.Length==3&&args[0]=="accept-native-docked-services")
{await NativeDockedServicesAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==4&&args[0]=="accept-native-normal-stop")
{await NativeThrottleZeroAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="inspect-native-services")
{
if(File.Exists(args[3]))throw new IOException("Use a new report path.");
var pipe=new NavigationPipeClient(int.Parse(args[1]));
var inventory=await pipe.CallAsync(new{op="commerce.inventory"});
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new XFEEDToolBox.Core.Data.JournalMonitor(settings.JournalDirectory);await journal.StartAsync();
if(!journal.State.InGame||!journal.State.Docked||journal.State.MarketId is not {} market)throw new InvalidOperationException("Actual current docking required.");
var queued=await pipe.CallAsync(new{op="station.services.inspect",marketId=(uint)market,maintenanceAddress=args[2]});
var generation=queued.GetProperty("generation").GetUInt64();using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(35));
while(true)
{
var result=await pipe.CallAsync(new{op="commerce.market.status",generation},stop.Token);
if(result.GetProperty("state").GetInt32()>=3)
{
await File.WriteAllTextAsync(args[3],JsonSerializer.Serialize(new{result,inventory,stationServices=journal.State.StationServices,screenUse=false,mutationsSent=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{state=result.GetProperty("state").GetInt32(),market,screenUse=false,mutationsSent=0}));
if(result.GetProperty("state").GetInt32()!=3)throw new InvalidOperationException("Original service-price read failed.");return;
}
await Task.Delay(100,stop.Token);
}
}
await using var flightProtection=args.Length>1&&args[0] is "accept-direct" or "accept-orbit" or "probe-sc-response" or "accept-hyperspace" or "calibrate-steering" or "calibrate-fsd" or "game-input-set" or "accept-local-steering"
?await FlightTestProtectionScope.ArmAsync(int.Parse(args[1])):null;
if(args.Length==4&&args[0]=="select-local")
{await LocalTargetSelection.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-frame")
{await LocalFlightFrameAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-steering")
{await LocalSteeringAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-cruise-entry")
{await LocalCruiseEntryAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-cruise-exit")
{await LocalCruiseExitAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-assist")
{await LocalAssistAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-native-navigation")
{await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-allow-obscuration")
{await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],true,true,true);return;}
if(args.Length==4&&args[0]=="accept-local-software-cruise")
{await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],true,false);return;}
if(args.Length==4&&args[0]=="accept-local-docking")
{await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],false);return;}
if(args.Length==4&&args[0]=="accept-local-docking-keep-scene")
{await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],requireCruise:false,keepHandoffScene:true);return;}
if(args.Length==4&&args[0]=="accept-local-launch")
{await LocalLaunchAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-launch-cancel")
{await LocalLaunchCancellationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-departure")
{await LocalDepartureAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-game-route")
{await InterstellarTravelAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==3&&args[0]=="current-system-read")
{
var pid=int.Parse(args[1]);var pipe=new NavigationPipeClient(pid);var hello=await pipe.CallAsync(new{op="describe"});
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new XFEEDToolBox.Core.Data.JournalMonitor(settings.JournalDirectory);await journal.StartAsync();
using var source=new XFEEDToolBox.Core.Native.NativeCurrentSystemSource(pid,hello.GetProperty("gameSha256").GetString()!,()=>journal.State);
var result=await source.ReadLocationsAsync();
await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(new{result,flightInputsSent=0,gameCallsInvoked=0,screenUse=false},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{result.SystemName,result.SystemAddress,locations=result.Locations.Count,distances=result.Locations.Count(x=>x.DistanceMetres is not null),
selected=result.Locations.FirstOrDefault(x=>x.Selected)?.Name,flightInputsSent=0,gameCallsInvoked=0,screenUse=false}));return;
}
if(args.Length==4&&args[0]=="observe-health")
{await ShipHealthObservation.RunAsync(int.Parse(args[1]),args[2],int.Parse(args[3]));return;}
if(args.Length==3&&args[0]=="observe-health-service")
{
using var output=new StreamWriter(new FileStream(args[2]+".log",FileMode.CreateNew,FileAccess.Write,FileShare.Read)){AutoFlush=true};
Console.SetOut(output);Console.SetError(output);
await ShipHealthObservation.RunAsync(int.Parse(args[1]),args[2],0);return;
}
if(args.Length is 3 or 5&&args[0] is "game-inputs" or "game-input-set")
{
if(args[0]=="game-inputs"&&args.Length!=3||args[0]=="game-input-set"&&args.Length!=5)throw new ArgumentException("Explicit input name and Boolean value required.");
var pid=int.Parse(args[1]);var nav=new NavigationPipeClient(pid);var hello=await nav.CallAsync(new{op="describe"});
var channel=new NativeGameInputChannel(pid,hello.GetProperty("gameSha256").GetString()!,Path.GetFullPath(args[2]));
await channel.ConnectAsync(hello.GetProperty("owningThread").GetInt32(),CancellationToken.None);
var before=await channel.ReadAsync(CancellationToken.None);var raw=await nav.CallAsync(new{op="read"});
if(before.EntityId.ToString()!=raw.GetProperty("entityId").GetString())throw new InvalidDataException("Input and navigation entities differ.");
long? generation=null;
if(args[0]=="game-input-set")
{
generation=await channel.SetAsync(before,args[3],bool.Parse(args[4]),CancellationToken.None);
await Task.Delay(100);
}
Console.WriteLine(JsonSerializer.Serialize(new{operation=args[0],pid,before,generation,after=await channel.ReadAsync(CancellationToken.None),screenUse=false,osInputSent=false},ToolboxSettings.JsonOptions));return;
}
if(args.Length==3&&args[0]=="accept-read")
{await NativeReadAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==4&&args[0]=="preview-reference")
{await ReferencePreview.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="probe-sc-response")
{await SupercruiseResponseProbe.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==3&&args[0] is "galaxy-find" or "galaxy-suggest")
{
var catalog=new NativeGalaxyCatalog(int.Parse(args[1]));
object result=args[0]=="galaxy-find"?await catalog.FindAsync(args[2]):await catalog.SuggestAsync(args[2]);
Console.WriteLine(JsonSerializer.Serialize(new{result,flightInputsSent=0,screenUse=false},ToolboxSettings.JsonOptions));return;
}
if(args.Length==6&&args[0]=="galaxy-region")
{
double D(int i)=>double.Parse(args[i],System.Globalization.CultureInfo.InvariantCulture);
var rows=await new NativeGalaxyCatalog(int.Parse(args[1])).RegionAsync(new(D(2),D(3),D(4)),D(5));
Console.WriteLine(JsonSerializer.Serialize(new{systems=rows,count=rows.Count,flightInputsSent=0,screenUse=false},ToolboxSettings.JsonOptions));return;
}
if(args.Length==2&&args[0] is "galaxy-overview" or "skybox-capture" or "skybox-refresh" or "skybox-status")
{
var operation=args[0] switch{"galaxy-overview"=>"galaxy.overview","skybox-capture"=>"galaxy.skybox.capture","skybox-refresh"=>"galaxy.skybox.refresh",_=>"galaxy.skybox.status"};
Console.WriteLine(await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op=operation}));return;
}
if(args.Length is 5 or 7&&args[0]=="accept-direct")
{
Environment.ExitCode=await DirectNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4],args.Length==7?args[5]:null,args.Length==7?double.Parse(args[6],System.Globalization.CultureInfo.InvariantCulture):null)?0:1;
return;
}
if(args.Length==4&&args[0]=="accept-orbit")
{
Environment.ExitCode=await StarOrbitAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3])?0:1;
return;
}
if(args.Length==4&&args[0]=="select-target")
{
var pid=int.Parse(args[1]);
using var profile=JsonDocument.Parse(await File.ReadAllTextAsync(args[2]));
using var game=Process.GetProcessById(pid);
if(!string.Equals(game.MainModule?.FileName,profile.RootElement.GetProperty("image").GetString(),StringComparison.OrdinalIgnoreCase))throw new InvalidDataException("Selected game process changed.");
var pipe=new NavigationPipeClient(pid);var owner=Guid.NewGuid().ToString("N");var target=ulong.Parse(args[3]);
var hello=await pipe.CallAsync(new{op="describe"});
if(hello.GetProperty("processId").GetInt32()!=pid||hello.GetProperty("gameSha256").GetString()!=profile.RootElement.GetProperty("sha256").GetString())throw new InvalidDataException("Selected native game build changed.");
try
{
var result=await pipe.CallAsync(new{op="target.select",owner,targetSystemAddress=target});var wait=Stopwatch.StartNew();
while(!result.GetProperty("selected").GetBoolean())
{
if(!result.GetProperty("pending").GetBoolean()||wait.Elapsed.TotalSeconds>1)throw new InvalidOperationException("Original target selection was not acknowledged.");
await Task.Delay(25);result=await pipe.CallAsync(new{op="target.status",owner,targetSystemAddress=target});
}
Console.WriteLine(JsonSerializer.Serialize(new{operation="select-target",pid,result,screenUse=false,flightInputsSent=0}));
}
finally{Console.WriteLine(await pipe.CallAsync(new{op="release",owner}));}
return;
}
if(args.Length is 5 or 6&&args[0]=="accept-hyperspace")
{
Environment.ExitCode=await HyperspaceAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4],args.Length==6?args[5]:null)?0:1;
return;
}
if(args.Length==4&&args[0]=="calibrate-steering")
{
Environment.ExitCode=await SteeringCalibration.RunAsync(int.Parse(args[1]),args[2],args[3])?0:1;
return;
}
if(args.Length==5&&args[0]=="calibrate-fsd")
{
Environment.ExitCode=await FsdCalibration.RunAsync(int.Parse(args[1]),args[2],args[3],args[4])?0:1;
return;
}
if(args.Length==3&&args[0] is "update-plugin" or "uninstall-plugin" or "install-plugin")
{
var game=Path.GetFullPath(args[1]);
foreach(var process in Process.GetProcessesByName("EliteDangerous64"))
{
using(process)if(string.Equals(process.MainModule?.FileName,game,StringComparison.OrdinalIgnoreCase))throw new InvalidOperationException("更新插件前必须退出所选游戏进程。");
}
var manager=new GamePluginManager(Path.GetFullPath(args[2]));
if(args[0]=="install-plugin")
{Console.WriteLine(JsonSerializer.Serialize(await manager.InstallAsync(game),ToolboxSettings.JsonOptions));return;}
var previous=await manager.InspectAsync(game);
if(!previous.Installed)throw new InvalidOperationException("验收更新只替换有本工具安装记录的插件。");
var removed=await manager.UninstallAsync(game);
if(args[0]=="uninstall-plugin")
{Console.WriteLine(JsonSerializer.Serialize(removed,ToolboxSettings.JsonOptions));return;}
var installed=await manager.InstallAsync(game);
Console.WriteLine(JsonSerializer.Serialize(installed,ToolboxSettings.JsonOptions));return;
}
if(args.Length==2&&args[0] is "observe" or "describe-native" or "describe-fsd" or "describe-geometry")
{
var pipe=new NavigationPipeClient(int.Parse(args[1]));
if(args[0]=="describe-fsd"){Console.WriteLine(await pipe.CallAsync(new{op="fsd.describe"}));return;}
if(args[0]=="describe-geometry"){Console.WriteLine(await pipe.CallAsync(new{op="geometry.describe"}));return;}
Console.WriteLine(await pipe.CallAsync(new{op="describe"}));
if(args[0]=="describe-native")return;
Console.WriteLine(await pipe.CallAsync(new{op="read"}));return;
}
if(args.Length==2&&args[0] is "start-observation" or "plugin-status")
{
var pid=int.Parse(args[1]);
var reply=args[0]=="start-observation"?await GamePluginManager.StartNavigationObservationAsync(pid):await GamePluginManager.ProbeAsync(pid);
Console.WriteLine(JsonSerializer.Serialize(reply,ToolboxSettings.JsonOptions));return;
}
throw new ArgumentException("Use install-plugin/update-plugin/uninstall-plugin <game.exe> <package directory>, or plugin-status/start-observation/describe-native/observe <pid>.");
}
catch(Exception error) when(error is IOException or InvalidOperationException or ArgumentException or OperationCanceledException or UnauthorizedAccessException or NotSupportedException or TimeoutException or System.ComponentModel.Win32Exception)
{
Console.Error.WriteLine(error.Message);
Environment.ExitCode=1;
}
using System.Diagnostics;
using System.Text.Json;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Plugins;
using XFEEDToolBox.Core.Galaxy;
try
{
if(args.Length==4&&args[0]=="accept-trade-pause"&&args[3]=="--open-verified-pause")
{await TradePauseRecoveryAcceptance.RunAsync(int.Parse(args[1]),args[2],openVerifiedPause:true);return;}
if(args.Length==3&&args[0]=="accept-trade-pause")
{await TradePauseRecoveryAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0] is "start-and-verify-sco" or "start-and-verify-sco-cycles")
{
var directory=Path.GetFullPath(args[2]);if(Directory.Exists(directory))throw new IOException("Use a fresh complete SCO evidence directory.");Directory.CreateDirectory(directory);
await GameSessionAcceptance.StartAsync(args[1],Path.Combine(directory,"entry.json"));
using var currentGame=Process.GetProcessesByName("EliteDangerous64").Single(game=>string.Equals(game.MainModule?.FileName,Path.GetFullPath(args[1]),StringComparison.OrdinalIgnoreCase));
var testGameStarted=currentGame.StartTime.ToUniversalTime().Ticks;
try
{
await PrepareScoLeg.RunAsync(currentGame.Id,Path.Combine(directory,"prepare"),longContinuityLeg:true);
if(args[0]=="start-and-verify-sco-cycles")
await SCOThermalCycleAcceptance.RunAsync(currentGame.Id,Path.Combine(directory,"thermal-cycles"));
else
await SCOContinuityAcceptance.RunAsync(currentGame.Id,Path.Combine(directory,"continuity"));
}
finally
{
// A normal acceptance result is not a reason to close the user's game.
// Each fixture releases SCO and its own expiring input leases itself.
currentGame.Refresh();
if(!currentGame.HasExited&¤tGame.StartTime.ToUniversalTime().Ticks==testGameStarted
&&string.Equals(currentGame.MainModule?.FileName,Path.GetFullPath(args[1]),StringComparison.OrdinalIgnoreCase))
{
await File.WriteAllTextAsync(Path.Combine(directory,"test-session-retained.json"),JsonSerializer.Serialize(new
{processId=currentGame.Id,startedUtcTicks=testGameStarted,at=DateTimeOffset.UtcNow,gameClosed=false,
reason="Acceptance finished; only the fixture's SCO and expiring controls are released"},ToolboxSettings.JsonOptions));
}
}
return;
}
if(args.Length==4&&args[0]=="recover-current-session"&&args[3]=="--update-stationary-ship")
{await RecoverCurrentSession.RunAsync(args[1],args[2],updateStationaryShip:true);return;}
if(args.Length==4&&args[0]=="recover-current-session"&&args[3]=="--update-docked-ship")
{await RecoverCurrentSession.RunAsync(args[1],args[2],updateDockedShip:true);return;}
if(args.Length==3&&args[0]=="recover-current-session")
{await RecoverCurrentSession.RunAsync(args[1],args[2]);return;}
if(args.Length==4&&args[0]=="accept-flight-context-admission")
{await FlightContextAdmissionAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==5&&args[0]=="accept-persistent-docking-throttle")
{await PersistentDockingThrottleAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4]);return;}
if(args.Length==6&&args[0]=="accept-persistent-docking-throttle"&&args[5]=="--production-reset")
{await PersistentDockingThrottleAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4],productionReset:true);return;}
if(args.Length==2&&args[0]=="accept-sco-control"&&args[1]=="--self-test")
{SCOControlFlightAcceptance.SelfTest();return;}
if(args.Length==3&&args[0]=="accept-sco-control"&&args[1]=="--replay-preflight")
{SCOControlFlightAcceptance.ReplayPreflight(args[2]);return;}
if(args.Length==6&&args[0]=="accept-sco-control"&&args[1]=="--finish-return")
{await SCOControlFlightAcceptance.FinishReturnAsync(int.Parse(args[2]),args[3],args[4],args[5]);return;}
if(args.Length==4&&args[0]=="accept-sco-control")
{await SCOControlFlightAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==3&&args[0]=="verify-sco-continuity")
{await SCOContinuityAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="verify-sco-thermal-cycles")
{await SCOThermalCycleAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==4&&args[0]=="prepare-sco-leg"&&args[3]=="--long-continuity")
{await PrepareScoLeg.RunAsync(int.Parse(args[1]),args[2],longContinuityLeg:true);return;}
if(args.Length==3&&args[0]=="prepare-sco-leg")
{await PrepareScoLeg.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==4&&args[0]=="observe-sco-lifecycle")
{await CruiseLifecycleAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==5&&args[0]=="observe-sco-lifecycle")
{await CruiseLifecycleAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4]);return;}
if(args.Length==4&&args[0]=="finish-sco-lifecycle-return")
{await CruiseLifecycleAcceptance.FinishReturnAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==5&&args[0]=="finish-sco-lifecycle-return")
{await CruiseLifecycleAcceptance.FinishReturnAsync(int.Parse(args[1]),args[2],args[3],args[4]);return;}
if(args.Length==5&&args[0]=="finish-sco-lifecycle-current-return")
{await CruiseLifecycleAcceptance.FinishReturnAsync(int.Parse(args[1]),args[2],args[3],args[4],reuseCurrentSolo:true);return;}
if(args.Length==4&&args[0]=="finish-sco-lifecycle-maintenance")
{await CruiseLifecycleAcceptance.FinishMaintenanceAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="deactivate-cruise-control")
{await CruiseControlAcceptance.DeactivateAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="probe-cruise-control")
{await CruiseControlAcceptance.LoadAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==3&&args[0] is "read-cruise-control" or "watch-cruise-control")
{await CruiseControlAcceptance.ReadAsync(int.Parse(args[1]),args[2],args[0]=="watch-cruise-control");return;}
if(args.Length==4&&args[0]=="deactivate-cruise-observation")
{await CruiseObservationAcceptance.DeactivateAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="probe-cruise-observation")
{await CruiseObservationAcceptance.LoadAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==3&&args[0] is "read-cruise-observation" or "watch-cruise-observation")
{await CruiseObservationAcceptance.ReadAsync(int.Parse(args[1]),args[2],args[0]=="watch-cruise-observation");return;}
if(args.Length==3&&args[0]=="probe-docking-reset-bridge")
{await SessionRecoveryAcceptance.ProbeBridgeAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==4&&args[0] is "accept-original-docking-reset" or "accept-original-docking-ai")
{await OriginalDockingResetAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],requireNonzero:args[0]=="accept-original-docking-reset");return;}
if(args.Length==6&&args[0] is "accept-original-docking-reset-at" or "accept-original-docking-ai-at")
{await OriginalDockingResetAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],requireNonzero:args[0]=="accept-original-docking-reset-at",targetMarketId:long.Parse(args[4]),targetName:args[5]);return;}
if(args.Length==4&&args[0] is "observe-session-extension" or "deactivate-session-recovery")
{await SessionRecoveryAcceptance.InspectAsync(int.Parse(args[1]),args[2],args[3],args[0]=="deactivate-session-recovery");return;}
if(args.Length==4&&args[0] is "probe-session-recovery" or "leave-with-session-recovery")
{await SessionRecoveryAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[0]=="leave-with-session-recovery");return;}
if(args.Length==7&&args[0]=="docking-fixture-wait")
{await DockingFixtureWait.RunAsync(int.Parse(args[1]),long.Parse(args[2]),int.Parse(args[3]),long.Parse(args[4]),args[5],args[6]);return;}
if(args.Length==3&&args[0]=="docking-fixture")
{await DockingFixtureAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="prepare-cg-visual")
{await ManualVisualHandoff.PrepareAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="run-cg-visual")
{await ManualVisualHandoff.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="auto-cg-trade")
{await CgTradeLoop.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="service-cg-docked")
{await DockedMaintenance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="reconcile-rebuy")
{await RebuyReconciliation.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="inspect-docking-input-sources")
{
if(File.Exists(args[2]))throw new IOException("Use a new read-only docking input report.");
var result=await XFEEDToolBox.Core.Native.NativeDockingInputDiagnostics.ReadAsync(int.Parse(args[1]));
await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(result,ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(result,ToolboxSettings.JsonOptions));return;
}
if(args.Length==3&&args[0]=="start-session")
{
try{await GameSessionAcceptance.StartAsync(args[1],args[2]);}
catch(Exception error)
{
if(!File.Exists(args[2]))await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(new{passed=false,stage="session-entry-not-confirmed",failure=error.ToString(),screenUse=false,flightInputsSent=0,maintenanceHoldReleased=false},ToolboxSettings.JsonOptions));
throw;
}
return;
}
if(args.Length==5&&args[0]=="recover-disconnected-session")
{await GameSessionAcceptance.RecoverDisconnectedAsync(int.Parse(args[1]),args[2],args[3],args[4]);return;}
if(args.Length==4&&args[0]=="enter-session")
{await GameSessionAcceptance.StartAsync(args[2],args[3],int.Parse(args[1]));return;}
if(args.Length==3&&args[0]=="leave-session")
{await GameSessionAcceptance.LeaveAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="close-docked-for-protection")
{
if(File.Exists(args[2]))throw new IOException("Use a new confirmed protection-install report.");
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new XFEEDToolBox.Core.Data.JournalMonitor(settings.JournalDirectory);await journal.StartAsync();var state=journal.State;
if(!state.InGame||!state.Docked||state.CurrentShipId!=5||state.CargoCount!=0||!TravelPreparation.StatusConfirmed(state,DateTimeOffset.UtcNow)
||state.Status is null||(state.Status.Flags&1)==0||NavigationSafety.HasExternalThreat(state))throw new InvalidOperationException("Verified docked empty Corsair required before loading protection update.");
var driver=new XFEEDToolBox.Services.WindowsGameSessionDriver(()=>journal.State,()=>settings.PluginGameExecutable);
var target=await driver.FindTargetAsync(CancellationToken.None);if(target.ProcessId!=int.Parse(args[1]))throw new InvalidOperationException("Selected game instance changed.");
await driver.ValidateExitAsync(target,CancellationToken.None);await driver.TerminateAsync(target,CancellationToken.None);
for(var i=0;i<30&&!await driver.HasExitedAsync(target,CancellationToken.None);i++)await Task.Delay(100);
var exited=await driver.HasExitedAsync(target,CancellationToken.None);
await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(new{pid=target.ProcessId,target.StartedUtcTicks,exited,forced=true,state.Docked,state.CargoCount,state.CurrentShipId,flightInputsSent=0,tradeOrdersSent=0},ToolboxSettings.JsonOptions));
if(!exited)throw new IOException("Selected game has not exited; do not update the loaded protection module.");return;
}
if(args.Length==2&&args[0]=="inspect-session")
{Console.WriteLine(await GameSessionAcceptance.StatusAsync(int.Parse(args[1])));return;}
if(args.Length==2&&args[0]=="inspect-nav-safety")
{
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new XFEEDToolBox.Core.Data.JournalMonitor(settings.JournalDirectory);await journal.StartAsync();var state=journal.State;
var native=await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op="health.read"});
Console.WriteLine(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,state.InGame,state.SystemName,state.Navigation.CurrentSystemAddress,state.CurrentShipId,state.CargoCount,
state.InDanger,state.StatusAt,state.StatusReadAt,statusFlags=state.Status?.Flags,journalExternalThreat=state.Navigation.ExternalThreat,
unhealthyHull=NavigationSafety.UnhealthyHull(state),externalThreat=NavigationSafety.HasExternalThreat(state),
shipHull=state.Ships.FirstOrDefault(x=>x.ShipId==state.CurrentShipId)?.HullHealth,native,flightInputsSent=0},ToolboxSettings.JsonOptions));return;
}
if(args.Length==2&&args[0]=="inspect-trade-checkpoint")
{
var value=XFEEDToolBox.Core.Execution.TradeJourneyCheckpoints.Load(args[1]);
Console.WriteLine(JsonSerializer.Serialize(new{value.Commander,value.ShipId,value.StartedAt,bought=value.Bought,sold=value.Sold,gameAccess=false,ordersSubmitted=0},ToolboxSettings.JsonOptions));return;
}
if(args.Length==3&&args[0]=="one-ton-cg-journey")
{await TradeJourneyAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="local-catalog")
{
if(File.Exists(args[2]))throw new IOException("Use a new report path.");
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new XFEEDToolBox.Core.Data.JournalMonitor(settings.JournalDirectory);await journal.StartAsync();
using var source=new XFEEDToolBox.Core.Native.NativeCurrentSystemSource(int.Parse(args[1]),XFEEDToolBox.Core.Native.NativeSystemLocationReader.GameSha256,()=>journal.State);
var catalogue=await source.ReadLocationsAsync();
await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(catalogue,ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{catalogue.SystemName,count=catalogue.Locations.Count,gameCommandsInvoked=false}));return;
}
if(args.Length==2&&args[0]=="describe-local")
{Console.WriteLine(JsonSerializer.Serialize(await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op="local.read"})));return;}
if(args.Length==2&&args[0]=="commerce-inventory")
{Console.WriteLine(JsonSerializer.Serialize(await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op="commerce.inventory"})));return;}
if(args.Length==3&&args[0]=="commerce-docked-status")
{Console.WriteLine(JsonSerializer.Serialize(await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op="commerce.docked.status",marketId=uint.Parse(args[2])})));return;}
if(args.Length==4&&args[0]=="cg-read"&&args[2] is "all" or "mine")
{await NativeCommunityGoals.ReadAsync(int.Parse(args[1]),args[2]=="mine",args[3]);return;}
if(args.Length==3&&args[0]=="join-trade-cg")
{await NativeCommunityGoals.JoinTradeAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==3&&args[0]=="cg-cargo-journey")
{
var pid=int.Parse(args[1]);using var guardian=await PersistentProtectionProcess.StartOrReuseAsync(pid,Directory.GetParent(Path.GetFullPath(args[2]))!.FullName,CancellationToken.None);
var previous=PersistentProtectionProcess.Current;PersistentProtectionProcess.Current=guardian;
try{await CgCargoJourney.RunAsync(pid,args[2]);}finally{PersistentProtectionProcess.Current=previous;}return;
}
if(args.Length==3&&args[0]=="accept-main-inputs")
{
if(File.Exists(args[2]))throw new IOException("Use a new evidence path.");
var pid=int.Parse(args[1]);var pipe=new NavigationPipeClient(pid);var hello=await pipe.CallAsync(new{op="describe"});
var channel=new NativeGameInputChannel(pid,hello.GetProperty("gameSha256").GetString()!,Path.GetFullPath("XFEEDToolBox/PluginPackage"));
using var timeout=new CancellationTokenSource(TimeSpan.FromSeconds(15));await channel.ConnectAsync(hello.GetProperty("owningThread").GetInt32(),timeout.Token);
var samples=new List<GameInputState>();
while(samples.Count<20)
{
var input=await channel.ReadAsync(timeout.Token);var health=await pipe.CallAsync(new{op="health.read"},timeout.Token);
if(!health.GetProperty("localPlayerShipVerified").GetBoolean()||input.EntityId.ToString()!=health.GetProperty("entityId").GetString())throw new InvalidDataException("Input observer differs from the independently verified main ship.");
samples.Add(input);await Task.Delay(30,timeout.Token);
}
await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(new{passed=true,pid,samples,flightInputsSent=0,osInputsSent=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{passed=true,samples=samples.Count,flightInputsSent=0}));return;
}
if(args.Length==2&&args[0]=="service-inventory")
{Console.WriteLine(JsonSerializer.Serialize(await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op="station.services.inventory"})));return;}
if(args.Length==4&&args[0]=="read-docked-market")
{
if(File.Exists(args[3]))throw new IOException("Use a new report path.");
var pipe=new NavigationPipeClient(int.Parse(args[1]));var market=uint.Parse(args[2]);
var queued=await pipe.CallAsync(new{op="commerce.market.read",marketId=market});
var generation=queued.GetProperty("generation").GetUInt64();using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(35));
while(true)
{
var result=await pipe.CallAsync(new{op="commerce.market.status",generation,maximum=50},stop.Token);
if(result.GetProperty("state").GetInt32()>=3)
{
var commodities=new List<JsonElement>();
if(result.GetProperty("state").GetInt32()==3)
for(var offset=0;offset<result.GetProperty("commodityCount").GetInt32();offset+=50)
{
var page=offset==0?result:await pipe.CallAsync(new{op="commerce.market.status",generation,offset,maximum=50},stop.Token);
foreach(var item in page.GetProperty("commodities").EnumerateArray())commodities.Add(item.Clone());
}
await File.WriteAllTextAsync(args[3],JsonSerializer.Serialize(new{result,commodities,screenUse=false,mutationsSent=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{state=result.GetProperty("state").GetInt32(),market,commodities=commodities.Count,screenUse=false,mutationsSent=0}));
if(result.GetProperty("state").GetInt32()!=3)throw new InvalidOperationException("Original read-only market request failed.");return;
}
await Task.Delay(100,stop.Token);
}
}
if(args.Length==5&&args[0]=="accept-native-market-order")
{await NativeMarketOrderAcceptance.RunAsync(int.Parse(args[1]),args[2],int.Parse(args[3]),args[4]);return;}
if(args.Length==4&&args[0]=="accept-native-refuel")
{await NativeStationServiceAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==5&&args[0]=="accept-native-repair")
{await NativeRepairAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4]);return;}
if(args.Length==3&&args[0]=="accept-native-docked-services")
{await NativeDockedServicesAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==4&&args[0]=="accept-native-normal-stop")
{await NativeThrottleZeroAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="inspect-native-services")
{
if(File.Exists(args[3]))throw new IOException("Use a new report path.");
var pipe=new NavigationPipeClient(int.Parse(args[1]));
var inventory=await pipe.CallAsync(new{op="commerce.inventory"});
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new XFEEDToolBox.Core.Data.JournalMonitor(settings.JournalDirectory);await journal.StartAsync();
if(!journal.State.InGame||!journal.State.Docked||journal.State.MarketId is not {} market)throw new InvalidOperationException("Actual current docking required.");
var queued=await pipe.CallAsync(new{op="station.services.inspect",marketId=(uint)market,maintenanceAddress=args[2]});
var generation=queued.GetProperty("generation").GetUInt64();using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(35));
while(true)
{
var result=await pipe.CallAsync(new{op="commerce.market.status",generation},stop.Token);
if(result.GetProperty("state").GetInt32()>=3)
{
await File.WriteAllTextAsync(args[3],JsonSerializer.Serialize(new{result,inventory,stationServices=journal.State.StationServices,screenUse=false,mutationsSent=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{state=result.GetProperty("state").GetInt32(),market,screenUse=false,mutationsSent=0}));
if(result.GetProperty("state").GetInt32()!=3)throw new InvalidOperationException("Original service-price read failed.");return;
}
await Task.Delay(100,stop.Token);
}
}
await using var flightProtection=args.Length>1&&args[0] is "accept-direct" or "accept-orbit" or "probe-sc-response" or "accept-hyperspace" or "calibrate-steering" or "calibrate-fsd" or "game-input-set" or "accept-local-steering"
?await FlightTestProtectionScope.ArmAsync(int.Parse(args[1])):null;
if(args.Length==4&&args[0]=="select-local")
{await LocalTargetSelection.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-frame")
{await LocalFlightFrameAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-steering")
{await LocalSteeringAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-cruise-entry")
{await LocalCruiseEntryAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-cruise-exit")
{await LocalCruiseExitAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-assist")
{await LocalAssistAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-native-navigation")
{await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-allow-obscuration")
{await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],true,true,true);return;}
if(args.Length==4&&args[0]=="accept-local-software-cruise")
{await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],true,false);return;}
if(args.Length==4&&args[0]=="accept-local-docking")
{await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],false);return;}
if(args.Length==4&&args[0]=="accept-local-docking-keep-scene")
{await LocalNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],requireCruise:false,keepHandoffScene:true);return;}
if(args.Length==4&&args[0]=="accept-local-launch")
{await LocalLaunchAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-launch-cancel")
{await LocalLaunchCancellationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-local-departure")
{await LocalDepartureAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="accept-game-route")
{await InterstellarTravelAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==3&&args[0]=="current-system-read")
{
var pid=int.Parse(args[1]);var pipe=new NavigationPipeClient(pid);var hello=await pipe.CallAsync(new{op="describe"});
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new XFEEDToolBox.Core.Data.JournalMonitor(settings.JournalDirectory);await journal.StartAsync();
using var source=new XFEEDToolBox.Core.Native.NativeCurrentSystemSource(pid,hello.GetProperty("gameSha256").GetString()!,()=>journal.State);
var result=await source.ReadLocationsAsync();
await File.WriteAllTextAsync(args[2],JsonSerializer.Serialize(new{result,flightInputsSent=0,gameCallsInvoked=0,screenUse=false},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{result.SystemName,result.SystemAddress,locations=result.Locations.Count,distances=result.Locations.Count(x=>x.DistanceMetres is not null),
selected=result.Locations.FirstOrDefault(x=>x.Selected)?.Name,flightInputsSent=0,gameCallsInvoked=0,screenUse=false}));return;
}
if(args.Length==4&&args[0]=="observe-health")
{await ShipHealthObservation.RunAsync(int.Parse(args[1]),args[2],int.Parse(args[3]));return;}
if(args.Length==3&&args[0]=="observe-health-service")
{
using var output=new StreamWriter(new FileStream(args[2]+".log",FileMode.CreateNew,FileAccess.Write,FileShare.Read)){AutoFlush=true};
Console.SetOut(output);Console.SetError(output);
await ShipHealthObservation.RunAsync(int.Parse(args[1]),args[2],0);return;
}
if(args.Length is 3 or 5&&args[0] is "game-inputs" or "game-input-set")
{
if(args[0]=="game-inputs"&&args.Length!=3||args[0]=="game-input-set"&&args.Length!=5)throw new ArgumentException("Explicit input name and Boolean value required.");
var pid=int.Parse(args[1]);var nav=new NavigationPipeClient(pid);var hello=await nav.CallAsync(new{op="describe"});
var channel=new NativeGameInputChannel(pid,hello.GetProperty("gameSha256").GetString()!,Path.GetFullPath(args[2]));
await channel.ConnectAsync(hello.GetProperty("owningThread").GetInt32(),CancellationToken.None);
var before=await channel.ReadAsync(CancellationToken.None);var raw=await nav.CallAsync(new{op="read"});
if(before.EntityId.ToString()!=raw.GetProperty("entityId").GetString())throw new InvalidDataException("Input and navigation entities differ.");
long? generation=null;
if(args[0]=="game-input-set")
{
generation=await channel.SetAsync(before,args[3],bool.Parse(args[4]),CancellationToken.None);
await Task.Delay(100);
}
Console.WriteLine(JsonSerializer.Serialize(new{operation=args[0],pid,before,generation,after=await channel.ReadAsync(CancellationToken.None),screenUse=false,osInputSent=false},ToolboxSettings.JsonOptions));return;
}
if(args.Length==3&&args[0]=="accept-read")
{await NativeReadAcceptance.RunAsync(int.Parse(args[1]),args[2]);return;}
if(args.Length==4&&args[0]=="preview-reference")
{await ReferencePreview.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==4&&args[0]=="probe-sc-response")
{await SupercruiseResponseProbe.RunAsync(int.Parse(args[1]),args[2],args[3]);return;}
if(args.Length==3&&args[0] is "galaxy-find" or "galaxy-suggest")
{
var catalog=new NativeGalaxyCatalog(int.Parse(args[1]));
object result=args[0]=="galaxy-find"?await catalog.FindAsync(args[2]):await catalog.SuggestAsync(args[2]);
Console.WriteLine(JsonSerializer.Serialize(new{result,flightInputsSent=0,screenUse=false},ToolboxSettings.JsonOptions));return;
}
if(args.Length==6&&args[0]=="galaxy-region")
{
double D(int i)=>double.Parse(args[i],System.Globalization.CultureInfo.InvariantCulture);
var rows=await new NativeGalaxyCatalog(int.Parse(args[1])).RegionAsync(new(D(2),D(3),D(4)),D(5));
Console.WriteLine(JsonSerializer.Serialize(new{systems=rows,count=rows.Count,flightInputsSent=0,screenUse=false},ToolboxSettings.JsonOptions));return;
}
if(args.Length==2&&args[0] is "galaxy-overview" or "skybox-capture" or "skybox-refresh" or "skybox-status")
{
var operation=args[0] switch{"galaxy-overview"=>"galaxy.overview","skybox-capture"=>"galaxy.skybox.capture","skybox-refresh"=>"galaxy.skybox.refresh",_=>"galaxy.skybox.status"};
Console.WriteLine(await new NavigationPipeClient(int.Parse(args[1])).CallAsync(new{op=operation}));return;
}
if(args.Length is 5 or 7&&args[0]=="accept-direct")
{
Environment.ExitCode=await DirectNavigationAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4],args.Length==7?args[5]:null,args.Length==7?double.Parse(args[6],System.Globalization.CultureInfo.InvariantCulture):null)?0:1;
return;
}
if(args.Length==4&&args[0]=="accept-orbit")
{
Environment.ExitCode=await StarOrbitAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3])?0:1;
return;
}
if(args.Length==4&&args[0]=="select-target")
{
var pid=int.Parse(args[1]);
using var profile=JsonDocument.Parse(await File.ReadAllTextAsync(args[2]));
using var game=Process.GetProcessById(pid);
if(!string.Equals(game.MainModule?.FileName,profile.RootElement.GetProperty("image").GetString(),StringComparison.OrdinalIgnoreCase))throw new InvalidDataException("Selected game process changed.");
var pipe=new NavigationPipeClient(pid);var owner=Guid.NewGuid().ToString("N");var target=ulong.Parse(args[3]);
var hello=await pipe.CallAsync(new{op="describe"});
if(hello.GetProperty("processId").GetInt32()!=pid||hello.GetProperty("gameSha256").GetString()!=profile.RootElement.GetProperty("sha256").GetString())throw new InvalidDataException("Selected native game build changed.");
try
{
var result=await pipe.CallAsync(new{op="target.select",owner,targetSystemAddress=target});var wait=Stopwatch.StartNew();
while(!result.GetProperty("selected").GetBoolean())
{
if(!result.GetProperty("pending").GetBoolean()||wait.Elapsed.TotalSeconds>1)throw new InvalidOperationException("Original target selection was not acknowledged.");
await Task.Delay(25);result=await pipe.CallAsync(new{op="target.status",owner,targetSystemAddress=target});
}
Console.WriteLine(JsonSerializer.Serialize(new{operation="select-target",pid,result,screenUse=false,flightInputsSent=0}));
}
finally{Console.WriteLine(await pipe.CallAsync(new{op="release",owner}));}
return;
}
if(args.Length is 5 or 6&&args[0]=="accept-hyperspace")
{
Environment.ExitCode=await HyperspaceAcceptance.RunAsync(int.Parse(args[1]),args[2],args[3],args[4],args.Length==6?args[5]:null)?0:1;
return;
}
if(args.Length==4&&args[0]=="calibrate-steering")
{
Environment.ExitCode=await SteeringCalibration.RunAsync(int.Parse(args[1]),args[2],args[3])?0:1;
return;
}
if(args.Length==5&&args[0]=="calibrate-fsd")
{
Environment.ExitCode=await FsdCalibration.RunAsync(int.Parse(args[1]),args[2],args[3],args[4])?0:1;
return;
}
if(args.Length==3&&args[0] is "update-plugin" or "uninstall-plugin" or "install-plugin")
{
var game=Path.GetFullPath(args[1]);
foreach(var process in Process.GetProcessesByName("EliteDangerous64"))
{
using(process)if(string.Equals(process.MainModule?.FileName,game,StringComparison.OrdinalIgnoreCase))throw new InvalidOperationException("更新插件前必须退出所选游戏进程。");
}
var manager=new GamePluginManager(Path.GetFullPath(args[2]));
if(args[0]=="install-plugin")
{Console.WriteLine(JsonSerializer.Serialize(await manager.InstallAsync(game),ToolboxSettings.JsonOptions));return;}
var previous=await manager.InspectAsync(game);
if(!previous.Installed)throw new InvalidOperationException("验收更新只替换有本工具安装记录的插件。");
var removed=await manager.UninstallAsync(game);
if(args[0]=="uninstall-plugin")
{Console.WriteLine(JsonSerializer.Serialize(removed,ToolboxSettings.JsonOptions));return;}
var installed=await manager.InstallAsync(game);
Console.WriteLine(JsonSerializer.Serialize(installed,ToolboxSettings.JsonOptions));return;
}
if(args.Length==2&&args[0] is "observe" or "describe-native" or "describe-fsd" or "describe-geometry")
{
var pipe=new NavigationPipeClient(int.Parse(args[1]));
if(args[0]=="describe-fsd"){Console.WriteLine(await pipe.CallAsync(new{op="fsd.describe"}));return;}
if(args[0]=="describe-geometry"){Console.WriteLine(await pipe.CallAsync(new{op="geometry.describe"}));return;}
Console.WriteLine(await pipe.CallAsync(new{op="describe"}));
if(args[0]=="describe-native")return;
Console.WriteLine(await pipe.CallAsync(new{op="read"}));return;
}
if(args.Length==2&&args[0] is "start-observation" or "plugin-status")
{
var pid=int.Parse(args[1]);
var reply=args[0]=="start-observation"?await GamePluginManager.StartNavigationObservationAsync(pid):await GamePluginManager.ProbeAsync(pid);
Console.WriteLine(JsonSerializer.Serialize(reply,ToolboxSettings.JsonOptions));return;
}
throw new ArgumentException("Use install-plugin/update-plugin/uninstall-plugin <game.exe> <package directory>, or plugin-status/start-observation/describe-native/observe <pid>.");
}
catch(Exception error) when(error is IOException or InvalidOperationException or ArgumentException or OperationCanceledException or UnauthorizedAccessException or NotSupportedException or TimeoutException or System.ComponentModel.Win32Exception)
{
Console.Error.WriteLine(error.Message);
Environment.ExitCode=1;
}