diff --git a/ChatTwo/ChatTwo.csproj b/ChatTwo/ChatTwo.csproj
index 0a65174..2abaa96 100755
--- a/ChatTwo/ChatTwo.csproj
+++ b/ChatTwo/ChatTwo.csproj
@@ -58,7 +58,7 @@
-
+
diff --git a/ChatTwo/GameFunctions/Chat.cs b/ChatTwo/GameFunctions/Chat.cs
index a862b69..28f7475 100755
--- a/ChatTwo/GameFunctions/Chat.cs
+++ b/ChatTwo/GameFunctions/Chat.cs
@@ -56,6 +56,12 @@ internal sealed unsafe class Chat : IDisposable {
[Signature("E8 ?? ?? ?? ?? 4C 8B C0 FF C3")]
private readonly delegate* unmanaged _getLinkshellName = null!;
+ [Signature("40 56 41 54 41 55 41 57 48 83 EC 28 48 8B 01", Fallibility = Fallibility.Fallible)]
+ private readonly delegate* unmanaged _rotateLinkshellHistory;
+
+ [Signature("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 20 48 8B 01 44 8B F2", Fallibility = Fallibility.Fallible)]
+ private readonly delegate* unmanaged _rotateCrossLinkshellHistory;
+
[Signature("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B F2 48 8D B9")]
private readonly delegate* unmanaged _getColourInfo = null!;
@@ -109,6 +115,12 @@ internal sealed unsafe class Chat : IDisposable {
[Signature("4C 8D B6 ?? ?? ?? ?? 41 8B 1E 45 85 E4 74 7A 33 FF 8B EF 66 0F 1F 44 00", Offset = 3)]
private readonly int? _linkshellCycleOffset;
+ [Signature("BA ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B F0 48 85 C0 0F 84 ?? ?? ?? ?? 48 8B 10 33", Offset = 1)]
+ private readonly uint? _linkshellInfoProxyIdx;
+
+ [Signature("BA ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 89 6C 24 ?? 4C 8B E0 48 89 74 24", Offset = 1)]
+ private readonly uint? _crossLinkshellInfoProxyIdx;
+
#pragma warning restore 0649
// Pointers
@@ -155,7 +167,11 @@ internal sealed unsafe class Chat : IDisposable {
}
internal string? GetLinkshellName(uint idx) {
- var infoProxy = this.Plugin.Functions.GetInfoProxyByIndex(2);
+ if (this._linkshellInfoProxyIdx is not { } proxyIdx) {
+ return null;
+ }
+
+ var infoProxy = this.Plugin.Functions.GetInfoProxyByIndex(proxyIdx);
if (infoProxy == IntPtr.Zero) {
return null;
}
@@ -170,7 +186,11 @@ internal sealed unsafe class Chat : IDisposable {
}
internal string? GetCrossLinkshellName(uint idx) {
- var infoProxy = this.Plugin.Functions.GetInfoProxyByIndex(26);
+ if (this._crossLinkshellInfoProxyIdx is not { } proxyIdx) {
+ return null;
+ }
+
+ var infoProxy = this.Plugin.Functions.GetInfoProxyByIndex(proxyIdx);
if (infoProxy == IntPtr.Zero) {
return null;
}
@@ -186,12 +206,17 @@ internal sealed unsafe class Chat : IDisposable {
*(int*) (uiModule + this._linkshellCycleOffset.Value) = -1;
}
- return RotateLinkshellHistoryInternal(201, mode);
+ return RotateLinkshellHistoryInternal(this._rotateLinkshellHistory, mode);
}
- internal ulong RotateCrossLinkshellHistory(RotateMode mode) => RotateLinkshellHistoryInternal(203, mode);
+ internal ulong RotateCrossLinkshellHistory(RotateMode mode) => RotateLinkshellHistoryInternal(this._rotateCrossLinkshellHistory, mode);
+
+ private static ulong RotateLinkshellHistoryInternal(delegate* unmanaged func, RotateMode mode) {
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+ if (func == null) {
+ return 0;
+ }
- private static ulong RotateLinkshellHistoryInternal(int vfunc, RotateMode mode) {
var idx = mode switch {
RotateMode.Forward => 1,
RotateMode.Reverse => -1,
@@ -199,8 +224,7 @@ internal sealed unsafe class Chat : IDisposable {
};
var uiModule = Framework.Instance()->GetUiModule();
- var cycleFunc = (delegate* unmanaged) uiModule->vfunc[vfunc];
- return cycleFunc(uiModule, idx);
+ return func(uiModule, idx);
}
// This function looks up a channel's user-defined colour.
diff --git a/ChatTwo/GameFunctions/GameFunctions.cs b/ChatTwo/GameFunctions/GameFunctions.cs
index 4387d58..3e2db34 100755
--- a/ChatTwo/GameFunctions/GameFunctions.cs
+++ b/ChatTwo/GameFunctions/GameFunctions.cs
@@ -57,6 +57,9 @@ internal unsafe class GameFunctions : IDisposable {
#pragma warning restore 0649
+ [Signature("FF 90 ?? ?? ?? ?? 48 8B C8 BA ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B F0 48 85 C0 0F 84 ?? ?? ?? ?? 48 8B 10 33 ED", Offset = 2)]
+ private readonly int? _infoModuleVfunc;
+
private Plugin Plugin { get; }
internal Party Party { get; }
internal Chat Chat { get; }
@@ -81,14 +84,18 @@ internal unsafe class GameFunctions : IDisposable {
Marshal.FreeHGlobal(this._placeholderNamePtr);
}
- private static IntPtr GetInfoModule() {
+ private IntPtr GetInfoModule() {
+ if (this._infoModuleVfunc is not { } vfunc) {
+ return IntPtr.Zero;
+ }
+
var uiModule = Framework.Instance()->GetUiModule();
- var getInfoModule = (delegate* unmanaged) uiModule->vfunc[33];
+ var getInfoModule = (delegate* unmanaged) uiModule->vfunc[vfunc / 8];
return getInfoModule(uiModule);
}
internal IntPtr GetInfoProxyByIndex(uint idx) {
- var infoModule = GetInfoModule();
+ var infoModule = this.GetInfoModule();
return infoModule == IntPtr.Zero ? IntPtr.Zero : this._getInfoProxyByIndex(infoModule, idx);
}
diff --git a/ChatTwo/packages.lock.json b/ChatTwo/packages.lock.json
old mode 100644
new mode 100755
index 64f62db..2ae5c01
--- a/ChatTwo/packages.lock.json
+++ b/ChatTwo/packages.lock.json
@@ -33,9 +33,9 @@
},
"XivCommon": {
"type": "Direct",
- "requested": "[6.0.0, )",
- "resolved": "6.0.0",
- "contentHash": "8HwrC7mnkcaP+JxRoIIu9MqFElQRVL8HOr8EQ6Te+11zzvKoXTZZrZ7VWETF2CHfB+7c1v5wONZKqZJ2hETpjg=="
+ "requested": "[6.0.1, )",
+ "resolved": "6.0.1",
+ "contentHash": "X58/iHscbwzF9JziooBKYE4S0XDYuGYI7Eg5Er1LhdQSjGwMLes0whRdKtH591wIhefFiS5G14W6EEL3Qt76wg=="
},
"Microsoft.NETCore.Platforms": {
"type": "Transitive",