#if PLUGIN_SESSION_RECOVERY || PLUGIN_CRUISE_OBSERVATION || PLUGIN_CRUISE_CONTROL using System.Text; namespace XFEEDToolBox.Plugin; internal static unsafe partial class Navigation { internal sealed class RecoveryResetBinding { internal Snapshot baseline; internal JsonElement target; internal nuint node,source,registry,computer,events; internal float before,sourceBefore; } internal static void ConfigureRecoveryReadOnlyImage(){image=Platform.ImageBase;imageEnd=Platform.ImageEnd;} private static Snapshot RecoveryPlayer(ulong expectedEntity) { var pool=image+FsdPool; if(expectedEntity==0||Read(pool+0x68)!=0xEC0||Read(pool+0x90)>128)throw new InvalidOperationException("Reset player pool differs"); var visited=new HashSet();var block=Pointer(pool+0x30);Snapshot result=default;var found=0; while(block!=0&&visited.Add(block)&&visited.Count<=128) { var count=Read(block+0x38);if(count is 0 or >128)throw new InvalidOperationException("Reset player pool bound"); for(nuint i=0;i<(nuint)count;i++) { var fsd=block+0x80+i*0xEC0; if(Pointer(fsd)!=image+0x5316CC8||Pointer(fsd+0x58)!=image+0x5316DF8)continue; var owner=Pointer(fsd+0x20);var table=Pointer(owner);if(!InImage(table))continue; var reference=Leaf(owner,Pointer(table+0x20));if(reference==0)continue;var life=Pointer(reference); if(Read(life+0xC0)!=8||Read(life+0xB0)!=expectedEntity)continue; if(++found!=1)throw new InvalidOperationException("Reset player is ambiguous"); var physics=Pointer(fsd+0x2A8)-0x58;var input=Pointer(physics+0x3B0)-0x60; result=new(){entity=expectedEntity,life=life,fsd=fsd,physics=physics,inputRoot=input}; if(!BoundFsd(result,fsd))throw new InvalidOperationException("Reset controlled actor changed"); } block=Pointer(block+0x20); } if(block!=0||found!=1)throw new InvalidOperationException("Reset player is unavailable");return result; } internal static RecoveryResetBinding BindRecoveryReset(ulong entity,ulong world,JsonElement target,double hull,double? shield,nuint node) { var frame=RecoveryPlayer(entity);frame.referenceWorld=world;frame.healthValid=true;frame.hullFraction=hull; frame.shieldKnown=shield is not null;frame.shieldFraction=shield??0; var computer=LocalDockingComputer(frame); if(computer==0||Pointer(node)!=image+0x512E330||Pointer(node+0x40)!=Pointer(computer+0x230) ||Pointer(node+0x78)!=Pointer(computer+0x260)||Pointer(frame.fsd+0x6F8)!=Pointer(computer+0x230))throw new InvalidOperationException("Reset node does not belong to current main ship"); return new(){baseline=frame,target=target.Clone(),node=node,source=Pointer(node+0x78),registry=Pointer(node+0x40),computer=computer,events=Pointer(node+0x68)}; } private static bool RecoverySubscriber(RecoveryResetBinding proof) { var events=proof.events;var node=proof.node; if(events<0x10000||Pointer(node+0x68)!=events||Pointer(events)!=image+0x512EC98||Pointer(Pointer(events)+0x18)!=image+0x90C170 ||Pointer(node+0x20)!=image+0x512E3A0||Pointer(Pointer(node+0x20))!=image+0x8F5320)return false; var set=events+0x9E0;var buckets=Pointer(set);var capacity=Read(set+8);var used=Read(set+0x18); if(buckets<0x10000||capacity is 0 or >8192||used is 0 or >4096)return false; var seen=new HashSet();var matches=0; for(nuint i=0;i<(nuint)capacity;i++) { var head=buckets+i*8;var item=Pointer(head); while(item!=head) { if(item<0x10000||!seen.Add(item)||seen.Count>4096)return false; if(Pointer(item+8)==node+0x20)matches++;item=Pointer(item); } } return matches==1&&(ulong)seen.Count==used&&Pointer(set)==buckets&&Read(set+8)==capacity&&Read(set+0x18)==used; } private static bool RecoveryResetHealthEligible(Snapshot previous,Snapshot current) =>previous.healthValid&¤t.healthValid&&previous.entity==current.entity&&previous.life==current.life &&double.IsFinite(current.hullFraction)&¤t.hullFraction is >0 and <=1 &&!HealthDamageEmergency(previous,current); private static Snapshot ReadRecoveryResetFrame(RecoveryResetBinding proof,nuint node) { if(node!=proof.node)throw new InvalidOperationException("Reset mapper identity differs"); var current=RecoveryPlayer(proof.baseline.entity); if(current.life!=proof.baseline.life||current.fsd!=proof.baseline.fsd||current.physics!=proof.baseline.physics||current.inputRoot!=proof.baseline.inputRoot ||!Sample(current.physics,out var frame)||frame.entity!=proof.baseline.entity||frame.life!=current.life||frame.fsd!=current.fsd||frame.inputRoot!=current.inputRoot ||frame.referenceWorld!=proof.baseline.referenceWorld||!RecoveryResetHealthEligible(proof.baseline,frame) ||frame.rawFsdState is not(1 or 2)||!BoundFsd(frame,frame.fsd))throw new InvalidOperationException("Reset actual main ship or health changed"); if(!SampleLocalFlight(frame,out var local,out var stage))throw new InvalidOperationException("Reset original flight telemetry unavailable: "+stage); var target=proof.target; if(local.target.key!=target.GetProperty("key").GetInt32()||local.target.body.ToString()!=target.GetProperty("bodyAddress").GetString() ||local.target.kind!=target.GetProperty("kind").GetUInt32()||local.target.market!=target.GetProperty("marketId").GetUInt32() ||LocalIdentityName(local.target)!=target.GetProperty("name").GetString()||!local.selected||local.inSupercruise||local.distance>7500 ||!local.dockingVerified||!local.dockingGranted||local.nativeDockingActive||local.nativeLaunchActive||local.cruiseAssistEngaged) throw new InvalidOperationException("Reset actual target, ATC or native duty changed"); var computer=LocalDockingComputer(frame);var dock=LocalDocking(frame);var controller=Pointer(frame.inputRoot+0x2F8); if(computer!=proof.computer||!LocalDockingAvailable(computer)||Pointer(computer+0x240)!=controller||Pointer(node+0x40)!=Pointer(computer+0x230) ||Pointer(node+0x40)!=Pointer(frame.fsd+0x6F8)||Pointer(node+0x78)!=proof.source||Pointer(computer+0x260)!=proof.source ||!FixtureBindingName(Pointer(node+0xB0),"ForwardReverseThrust_Set")||!FixtureBindingName(Pointer(node+0xB8),"SetSpeedZero") ||!FixtureBindingName(Pointer(node+0x90),"Bindings_Flight_Active")||dock==0||Read(dock+0x614)!=2||Read(dock+0x4C0)is not(3 or 4)||Read(dock+0x4E9)==255) throw new InvalidOperationException("Reset original input ownership or ATC differs"); var registry=Pointer(node+0x40); if(Pointer(registry)!=image+0x512FD90||Pointer(Pointer(registry)+0x40)!=image+0x8FEDC0 ||Pointer(Pointer(node+0xB0)+0x10)!=registry+0xB8||Pointer(Pointer(node+0xB8)+0x10)!=registry+0xB8||Pointer(proof.source+0x10)!=registry+0xB8) throw new InvalidOperationException("Reset exact registry aliases differ"); var station=Pointer(dock+0x4E0); if(station==0||Read(station+0xC0)!=8||Read(station+0xB0)!=local.target.market||!RecoverySubscriber(proof) ||!Platform.ReadBytes(image+0x8F5320,18).AsSpan().SequenceEqual(new byte[]{0xC6,0x81,0xC0,0,0,0,0,0xC7,0x81,0xB0,0,0,0,0,0,0,0,0xC3})) throw new InvalidOperationException("Reset original callback or subscription differs"); return frame; } internal static void CallRecoveryReset(RecoveryResetBinding proof,nuint node,ulong deadline,Func stillAuthorized) { var frame=ReadRecoveryResetFrame(proof,node);var controller=Pointer(frame.inputRoot+0x2F8); proof.before=Read(node+0xD0); proof.sourceBefore=Read(proof.source+0x18); var actualBefore=Read(frame.inputRoot+0x70);var controllerBefore=Read(controller+0x680);var checkedAt=UtcMilliseconds(); if(!float.IsFinite(proof.before)||Math.Abs(proof.before)>1.001f||!float.IsFinite(proof.sourceBefore)||Math.Abs(proof.sourceBefore)>1.001f||UtcMilliseconds()>=deadline||!stillAuthorized() ||!float.IsFinite(controllerBefore)||Math.Abs(controllerBefore)>.001f||!float.IsFinite(actualBefore)||Math.Abs(actualBefore)>.001f) throw new InvalidOperationException($"Reset pre-call refused: accumulator={proof.before:R}, source={proof.sourceBefore:R}, actual={actualBefore:R}, controller={controllerBefore:R}, observedAt={checkedAt}, originalDeadline={deadline}"); // This original registered callback alone clears its own accumulator. // The following original mapper invocation computes the real output. Call(image+0x8F5320,(ulong)(node+0x20)); } } #endif