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(); 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(); if(result.GetProperty("state").GetInt32()==3) for(var offset=0;offset=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 , or plugin-status/start-observation/describe-native/observe ."); } 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; }