namespace XFEEDToolBox.Plugin;
internal static unsafe partial class Navigation
{
// Exact string comparisons in original1D5F600. The prefix loops include
// each literal's NUL; they are exact matches, not StartsWith matches.
static readonly HashSet<string> GalaxySocialGameplayStates=new(StringComparer.Ordinal)
{
"InGame","Pause","Respawn","BuyArx_Buffer","BuyArx_Pause","BuyArx_PC","BuyArx_PS4","BuyArx_XB1",
"Cancel CQC join","CancelMultiCrewJoin","CancelScenarioLoad","CheckMultiplayerPrivilegeForFriends",
"Confirm Quit To CQC","Confirm Quit To MultiCrew","Confirm Quit To Scenario","Confirmed Quit Game",
"Confirmed Quit To Game","Confirmed Quit To Main Menu","Confirmed Restart Checkpoint","Confirmed Restart Scenario",
"CustomerSupport_Pause","Debug_InEditor","DiedInHumanoidConflictZone","ExitOptions","Friends Management Screen",
"FriendsUnavailable","HelpAndInfo_PauseMenu","Increment checkpoint","IssueTracker_Pause","Leaving Pause Menu",
"OpenCustomerSupport_PC","OpenCustomerSupport_PS4","OpenCustomerSupport_XB1","OpenIssueTracker_PC",
"OpenIssueTracker_PS4","OpenIssueTracker_XB1","Pause Audio Options","Pause Control Binding Options",
"Pause Controller Layout","Pause Graphic Options","Pause Options","PrePauseMenu","ScenarioSuccess",
"ScenarioRestartOptions","Skip Scenario","Skip Scenario: Flow stage","Skip to next Checkpoint","StartRespawn"
};
static bool? GalaxyFriendMarkerCurrent(int status,int? mode,bool? gameplay)
{
// Exact 3AFF080 conjunction, independent from the display option.
if(status is not(5 or 6)||gameplay==false||mode==0)return false;
if(mode==1)return status==5?gameplay:false;
if(mode==2)return status==6?gameplay:false;
return null;
}
static bool CopyGalaxySocialModes(nuint flow,nuint expectedImage,uint avatarTypeId,out int? mode,out bool? gameplay)
{
mode=null;gameplay=null;
try
{
// 1D00EC0, 1D507A0: same flow interface used by SessionMenu.
if(flow<0x10058||Pointer(flow)!=expectedImage+0x5239F60||Pointer(flow-0x48)==0)return false;
var csm=Pointer(flow+0x310);
if(csm<0x10000||Pointer(csm)!=expectedImage+0x513BE00)return false;
var current=Pointer(csm+0x100);if(current<0x10000)return false;
var name=Pointer(current);if(!SessionName(name,out var state))return false;
gameplay=GalaxySocialGameplayStates.Contains(state);
// 1D53050 lazy-initialises this reference. Only copy it when
// already present; never call that getter or initialise config.
var config=Pointer(flow+0x568);
if(config==0)return Stable();
// Original integer value vtable4DDED20, whose type getter150DD0
// returns5F059C0. 1D53050 reads the signed value at+10.
if(Pointer(config)!=expectedImage+0x4DDED20)return Stable();
var cached=Read<int>(config+0x10);if(cached is <0 or >2)return Stable();
if(cached!=2){mode=cached;return Stable()&&StableConfig();}
// Original2175F70 (5F3492C iface+40) is exactly state12..15.
// If false, 1D53050 may suppress mode2 for an avatar value<100.
var multiplayer=Pointer(flow+0x90);
if(multiplayer<0x10000||Pointer(multiplayer)!=expectedImage+0x5269B28)return Stable();
var multiplayerState=Read<sbyte>(multiplayer+0x140);
if(multiplayerState is >=12 and <=15)
{
mode=2;return Stable()&&StableConfig()&&StableMultiplayer();
}
var user=Pointer(flow+0xD8);
if(user<0x10000||Pointer(user)!=expectedImage+0x52A0258)return Stable();
// Original978AF0 yields user+138's weak life, 636A80 tests>4.
var life=Pointer(user+0x138);nuint components=0,avatar=0;int lifeState=0;uint value=0;
if(life!=0)
{
lifeState=Read<int>(life+0xC0);
if(lifeState>4)
{
components=Pointer(life+0xC8);if(components<0x10030)return Stable();
avatar=RegisteredComponent(components-0x30,avatarTypeId);
if(avatar!=0)
{
if(Pointer(avatar)!=expectedImage+0x52A3188)return Stable();
// Original26055E0. Preserve its unsigned comparison.
value=Read<uint>(avatar+0x138);
}
}
}
mode=avatar!=0&&value<100?0:2;
if(!Stable()||!StableConfig()||!StableMultiplayer()||Pointer(flow+0xD8)!=user||
Pointer(user)!=expectedImage+0x52A0258||Pointer(user+0x138)!=life||
life!=0&&(Read<int>(life+0xC0)!=lifeState||lifeState>4&&Pointer(life+0xC8)!=components)||
avatar!=0&&(Pointer(avatar)!=expectedImage+0x52A3188||Read<uint>(avatar+0x138)!=value))
{mode=null;gameplay=null;return false;}
return true;
bool Stable()=>Pointer(flow)==expectedImage+0x5239F60&&Pointer(flow+0x310)==csm&&
Pointer(csm)==expectedImage+0x513BE00&&Pointer(csm+0x100)==current&&Pointer(current)==name&&
SessionName(name,out var after)&&after==state;
bool StableConfig()=>Pointer(flow+0x568)==config&&Pointer(config)==expectedImage+0x4DDED20&&Read<int>(config+0x10)==cached;
bool StableMultiplayer()=>Pointer(flow+0x90)==multiplayer&&Pointer(multiplayer)==expectedImage+0x5269B28&&
Read<sbyte>(multiplayer+0x140)==multiplayerState;
}
catch{mode=null;gameplay=null;return false;}
}
}
namespace XFEEDToolBox.Plugin;
internal static unsafe partial class Navigation
{
// Exact string comparisons in original1D5F600. The prefix loops include
// each literal's NUL; they are exact matches, not StartsWith matches.
static readonly HashSet<string> GalaxySocialGameplayStates=new(StringComparer.Ordinal)
{
"InGame","Pause","Respawn","BuyArx_Buffer","BuyArx_Pause","BuyArx_PC","BuyArx_PS4","BuyArx_XB1",
"Cancel CQC join","CancelMultiCrewJoin","CancelScenarioLoad","CheckMultiplayerPrivilegeForFriends",
"Confirm Quit To CQC","Confirm Quit To MultiCrew","Confirm Quit To Scenario","Confirmed Quit Game",
"Confirmed Quit To Game","Confirmed Quit To Main Menu","Confirmed Restart Checkpoint","Confirmed Restart Scenario",
"CustomerSupport_Pause","Debug_InEditor","DiedInHumanoidConflictZone","ExitOptions","Friends Management Screen",
"FriendsUnavailable","HelpAndInfo_PauseMenu","Increment checkpoint","IssueTracker_Pause","Leaving Pause Menu",
"OpenCustomerSupport_PC","OpenCustomerSupport_PS4","OpenCustomerSupport_XB1","OpenIssueTracker_PC",
"OpenIssueTracker_PS4","OpenIssueTracker_XB1","Pause Audio Options","Pause Control Binding Options",
"Pause Controller Layout","Pause Graphic Options","Pause Options","PrePauseMenu","ScenarioSuccess",
"ScenarioRestartOptions","Skip Scenario","Skip Scenario: Flow stage","Skip to next Checkpoint","StartRespawn"
};
static bool? GalaxyFriendMarkerCurrent(int status,int? mode,bool? gameplay)
{
// Exact 3AFF080 conjunction, independent from the display option.
if(status is not(5 or 6)||gameplay==false||mode==0)return false;
if(mode==1)return status==5?gameplay:false;
if(mode==2)return status==6?gameplay:false;
return null;
}
static bool CopyGalaxySocialModes(nuint flow,nuint expectedImage,uint avatarTypeId,out int? mode,out bool? gameplay)
{
mode=null;gameplay=null;
try
{
// 1D00EC0, 1D507A0: same flow interface used by SessionMenu.
if(flow<0x10058||Pointer(flow)!=expectedImage+0x5239F60||Pointer(flow-0x48)==0)return false;
var csm=Pointer(flow+0x310);
if(csm<0x10000||Pointer(csm)!=expectedImage+0x513BE00)return false;
var current=Pointer(csm+0x100);if(current<0x10000)return false;
var name=Pointer(current);if(!SessionName(name,out var state))return false;
gameplay=GalaxySocialGameplayStates.Contains(state);
// 1D53050 lazy-initialises this reference. Only copy it when
// already present; never call that getter or initialise config.
var config=Pointer(flow+0x568);
if(config==0)return Stable();
// Original integer value vtable4DDED20, whose type getter150DD0
// returns5F059C0. 1D53050 reads the signed value at+10.
if(Pointer(config)!=expectedImage+0x4DDED20)return Stable();
var cached=Read<int>(config+0x10);if(cached is <0 or >2)return Stable();
if(cached!=2){mode=cached;return Stable()&&StableConfig();}
// Original2175F70 (5F3492C iface+40) is exactly state12..15.
// If false, 1D53050 may suppress mode2 for an avatar value<100.
var multiplayer=Pointer(flow+0x90);
if(multiplayer<0x10000||Pointer(multiplayer)!=expectedImage+0x5269B28)return Stable();
var multiplayerState=Read<sbyte>(multiplayer+0x140);
if(multiplayerState is >=12 and <=15)
{
mode=2;return Stable()&&StableConfig()&&StableMultiplayer();
}
var user=Pointer(flow+0xD8);
if(user<0x10000||Pointer(user)!=expectedImage+0x52A0258)return Stable();
// Original978AF0 yields user+138's weak life, 636A80 tests>4.
var life=Pointer(user+0x138);nuint components=0,avatar=0;int lifeState=0;uint value=0;
if(life!=0)
{
lifeState=Read<int>(life+0xC0);
if(lifeState>4)
{
components=Pointer(life+0xC8);if(components<0x10030)return Stable();
avatar=RegisteredComponent(components-0x30,avatarTypeId);
if(avatar!=0)
{
if(Pointer(avatar)!=expectedImage+0x52A3188)return Stable();
// Original26055E0. Preserve its unsigned comparison.
value=Read<uint>(avatar+0x138);
}
}
}
mode=avatar!=0&&value<100?0:2;
if(!Stable()||!StableConfig()||!StableMultiplayer()||Pointer(flow+0xD8)!=user||
Pointer(user)!=expectedImage+0x52A0258||Pointer(user+0x138)!=life||
life!=0&&(Read<int>(life+0xC0)!=lifeState||lifeState>4&&Pointer(life+0xC8)!=components)||
avatar!=0&&(Pointer(avatar)!=expectedImage+0x52A3188||Read<uint>(avatar+0x138)!=value))
{mode=null;gameplay=null;return false;}
return true;
bool Stable()=>Pointer(flow)==expectedImage+0x5239F60&&Pointer(flow+0x310)==csm&&
Pointer(csm)==expectedImage+0x513BE00&&Pointer(csm+0x100)==current&&Pointer(current)==name&&
SessionName(name,out var after)&&after==state;
bool StableConfig()=>Pointer(flow+0x568)==config&&Pointer(config)==expectedImage+0x4DDED20&&Read<int>(config+0x10)==cached;
bool StableMultiplayer()=>Pointer(flow+0x90)==multiplayer&&Pointer(multiplayer)==expectedImage+0x5269B28&&
Read<sbyte>(multiplayer+0x140)==multiplayerState;
}
catch{mode=null;gameplay=null;return false;}
}
}