Skip to content

Commit 8adb612

Browse files
authored
Patch FFXIV_ACT_Plugin bundled Machina library dynamically (#1)
* Patch plugin-bundled Machina * Update NotDeucalionInjector * Merge marzent/machina#{432adaff,e679a652} * CN 7.0 opcodes.jsonc * IPluginLog * .net8 * OpcodeManager.Instance._opcodes may be public
1 parent 2bb91fd commit 8adb612

File tree

20 files changed

+567
-115
lines changed

20 files changed

+567
-115
lines changed

.gitmodules

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
[submodule "machina"]
2-
path = machina
3-
url = https://github.com/marzent/machina
41
[submodule "OverlayPlugin.Core/resources/act-overlays"]
52
path = OverlayPlugin.Core/resources/act-overlays
63
url = https://github.com/cking/act-overlays

FetchDependencies/Costura.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ internal static class Costura
77
{
88
public static bool CheckForPlugin(string name)
99
{
10-
return name.Contains("act");
10+
return name.Contains("act") || name.Contains("machina");
1111
}
1212

1313
public static string Fix(string name)
@@ -16,6 +16,8 @@ public static string Fix(string name)
1616
return "FFXIV_ACT_" + name.Substring(18, name.Length - 33).ToTitleCase() + ".dll";
1717
if (name.Contains("machina.ffxiv"))
1818
return "Machina.FFXIV.dll";
19+
if (name.Contains("machina"))
20+
return "Machina.dll";
1921
return name.Substring(8, name.Length - 23).ToTitleCase() + ".dll";
2022
}
2123

FetchDependencies/FetchDependencies.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.IO.Compression;
2+
using Dalamud.Plugin.Services;
23

34
namespace FetchDependencies;
45

@@ -13,13 +14,15 @@ public class FetchDependencies
1314
private string DependenciesDir { get; }
1415
private bool IsChinese { get; }
1516
private HttpClient HttpClient { get; }
17+
private IPluginLog PluginLog { get; }
1618

17-
public FetchDependencies(Version version, string assemblyDir, bool isChinese, HttpClient httpClient)
19+
public FetchDependencies(Version version, string assemblyDir, bool isChinese, HttpClient httpClient, IPluginLog pluginLog)
1820
{
1921
PluginVersion = version;
2022
DependenciesDir = assemblyDir;
2123
IsChinese = isChinese;
2224
HttpClient = httpClient;
25+
PluginLog = pluginLog;
2326
}
2427

2528
public void GetFfxivPlugin()
@@ -53,10 +56,11 @@ public void GetFfxivPlugin()
5356
File.Delete(deucalionDll);
5457
}
5558

56-
var patcher = new Patcher(PluginVersion, DependenciesDir);
59+
var patcher = new Patcher(PluginVersion, DependenciesDir, PluginLog);
5760
patcher.MainPlugin();
5861
patcher.LogFilePlugin();
5962
patcher.MemoryPlugin();
63+
patcher.MachinaFFXIV();
6064
}
6165

6266
private bool NeedsUpdate(string dllPath)

FetchDependencies/FetchDependencies.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@
2626
<DalamudLibPath>$(HOME)/Library/Application Support/XIV on Mac/dalamud/Hooks/dev/</DalamudLibPath>
2727
</PropertyGroup>
2828

29+
<ItemGroup>
30+
<Reference Include="Dalamud">
31+
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
32+
<Private>false</Private>
33+
</Reference>
34+
</ItemGroup>
35+
2936
<ItemGroup>
3037
<Reference Include="Mono.Cecil">
3138
<HintPath>$(DalamudLibPath)Mono.Cecil.dll</HintPath>

FetchDependencies/Patcher.cs

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using Dalamud.Plugin.Services;
12
using Mono.Cecil;
23
using Mono.Cecil.Cil;
34

@@ -7,11 +8,13 @@ internal class Patcher
78
{
89
private Version PluginVersion { get; }
910
private string WorkPath { get; }
11+
private IPluginLog PluginLog { get; }
1012

11-
public Patcher(Version version, string workPath)
13+
public Patcher(Version version, string workPath, IPluginLog pluginLog)
1214
{
1315
PluginVersion = version;
1416
WorkPath = workPath;
17+
PluginLog = pluginLog;
1518
}
1619

1720
public void MainPlugin()
@@ -196,4 +199,112 @@ void UseMarshallCopyBuffer(MethodDefinition method)
196199

197200
memory.WriteOut();
198201
}
202+
203+
public void MachinaFFXIV()
204+
{
205+
var machinaDalamud = new TargetAssembly(Path.Combine(WorkPath, "Machina.FFXIV.Dalamud.dll"));
206+
207+
var machina = new TargetAssembly(Path.Combine(WorkPath, "Machina.FFXIV.dll"));
208+
machina.RemoveStrongNaming();
209+
210+
// For the Machina library, we want to replace all usages of `DeucalionClient` with our reimplemented
211+
// `NotDeucalionClient`. That class is used only in `Machina.FFXIV.FFXIVNetworkMonitor` class. So we need
212+
// to do the following modifications:
213+
//
214+
// 1. Replace its private member `_deucalionClient`'s type.
215+
// 2. In its every method:
216+
// - Replace all its `ldfld` and `stfld` instructions of the current class's `_deucalionClient` member.
217+
// - Replace all its `ldfld` and `stfld` instructions of `DeucalionClient`'s members with `NotDeucalionClient`'s
218+
// corresponding members. Thay have the same names and types, so we can just replace the type references.
219+
// - Replace all its `newobj` and `call*` instructions of `DeucalionClient`'s methods with `NotDeucalionClient`'s
220+
// corresponding methods.
221+
// - To prevent the Deucalion library from being really injected, replace all its `call*` instructions that to
222+
// class `DeucalionInjector` with `NotDeucalionInjector`.
223+
// 3. Safely remove the `DeucalionClient` and `DeucalionInjector` classes from the Machina library.
224+
225+
var oldClientFullName = "Machina.FFXIV.Deucalion.DeucalionClient";
226+
var newClientFullName = "Machina.FFXIV.Dalamud.NotDeucalionClient";
227+
var oldClientDefiniton = machina.Assembly.MainModule.Types.First(type => type.FullName == oldClientFullName);
228+
var newClientDefiniton = machinaDalamud.Assembly.MainModule.Types.First(type => type.FullName == newClientFullName);
229+
230+
var oldInjectorFullName = "Machina.FFXIV.Deucalion.DeucalionInjector";
231+
var newInjectorFullName = "Machina.FFXIV.Dalamud.NotDeucalionInjector";
232+
var oldInjectorDefiniton = machina.Assembly.MainModule.Types.First(type => type.FullName == oldInjectorFullName);
233+
var newInjectorDefiniton = machinaDalamud.Assembly.MainModule.Types.First(type => type.FullName == newInjectorFullName);
234+
235+
// For method replacements, also replace for the nested types of `DeucalionClient`.
236+
// (i.e. `MessageReceivedHandler` / `MessageSentHandler` delegates)
237+
var typeReplacements = new Dictionary<string, TypeDefinition>
238+
{
239+
{oldClientFullName, newClientDefiniton},
240+
{oldInjectorFullName, newInjectorDefiniton},
241+
};
242+
foreach (var nestedType in oldClientDefiniton.NestedTypes)
243+
{
244+
var target = newClientDefiniton.NestedTypes.FirstOrDefault(
245+
type => type.Name == nestedType.Name);
246+
if (target != null)
247+
typeReplacements[nestedType.FullName] = target;
248+
}
249+
250+
// Get the `FFXIVNetworkMonitor` class.
251+
var ffxivNetworkMonitor = machina.Assembly.MainModule.Types.First(type =>
252+
type.FullName == "Machina.FFXIV.FFXIVNetworkMonitor");
253+
254+
{
255+
// Step 1 - Replace its private member `_deucalionClient`'s type.
256+
var deucalionClientField = ffxivNetworkMonitor.Fields.First(field => field.Name == "_deucalionClient");
257+
deucalionClientField.FieldType = machina.Assembly.MainModule.ImportReference(newClientDefiniton);
258+
}
259+
{
260+
// Step 2
261+
foreach (var method in ffxivNetworkMonitor.Methods)
262+
{
263+
foreach (var instruction in method.Body.Instructions)
264+
{
265+
if (instruction.OpCode == OpCodes.Newobj ||
266+
instruction.OpCode == OpCodes.Call ||
267+
instruction.OpCode == OpCodes.Calli ||
268+
instruction.OpCode == OpCodes.Callvirt)
269+
{
270+
var methodReference = (MethodReference)instruction.Operand;
271+
foreach (var (oldType, newType) in typeReplacements)
272+
{
273+
if (methodReference.DeclaringType.FullName == oldType)
274+
{
275+
var newMethodDefinition = newType.Methods.FirstOrDefault(m =>
276+
m.Name == methodReference.Name && m.Parameters.Count == methodReference.Parameters.Count);
277+
if (newMethodDefinition != null)
278+
instruction.Operand = machina.Assembly.MainModule.ImportReference(newMethodDefinition);
279+
else
280+
{
281+
PluginLog.Error($"[Patcher] Could not find method {methodReference.Name} in {newType.FullName}");
282+
}
283+
}
284+
}
285+
}
286+
else if (instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Stfld)
287+
{
288+
var fieldReference = (FieldReference)instruction.Operand;
289+
if (fieldReference.DeclaringType.FullName == ffxivNetworkMonitor.FullName &&
290+
fieldReference.FieldType.FullName == oldClientFullName)
291+
{
292+
fieldReference.FieldType = machina.Assembly.MainModule.ImportReference(newClientDefiniton);
293+
}
294+
else if (fieldReference.DeclaringType.FullName == oldClientFullName)
295+
{
296+
var newFieldDefiniton = newClientDefiniton.Fields.First(f => f.Name == fieldReference.Name);
297+
instruction.Operand = machina.Assembly.MainModule.ImportReference(newFieldDefiniton);
298+
}
299+
}
300+
}
301+
}
302+
}
303+
304+
// Step 3 - Remove the unused classes.
305+
machina.Assembly.MainModule.Types.Remove(oldClientDefiniton);
306+
machina.Assembly.MainModule.Types.Remove(oldInjectorDefiniton);
307+
308+
machina.WriteOut();
309+
}
199310
}

IINACT.sln

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OverlayPlugin.Core", "Overl
1313
EndProject
1414
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FetchDependencies", "FetchDependencies\FetchDependencies.csproj", "{4EDF0EAF-0BC8-40CB-A2D1-F4A207770002}"
1515
EndProject
16-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Machina", "machina\Machina\Machina.csproj", "{38F9CA44-D795-4F78-8DFC-5D555CDEC4F5}"
17-
EndProject
18-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Machina.FFXIV", "machina\Machina.FFXIV\Machina.FFXIV.csproj", "{1AAD30B3-55ED-4C10-94BF-41889B6BD44A}"
19-
EndProject
2016
Global
2117
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2218
Debug|Any CPU = Debug|Any CPU

IINACT/IINACT.csproj

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,9 @@
7070

7171
<ItemGroup>
7272
<ProjectReference Include="..\FetchDependencies\FetchDependencies.csproj" />
73-
<ProjectReference Include="..\machina\Machina.FFXIV\Machina.FFXIV.csproj" />
74-
<ProjectReference Include="..\machina\Machina\Machina.csproj" />
7573
<ProjectReference Include="..\NotACT\NotACT.csproj" />
7674
<ProjectReference Include="..\OverlayPlugin.Core\OverlayPlugin.Core.csproj" />
75+
<ProjectReference Include="..\Machina.FFXIV.Dalamud\Machina.FFXIV.Dalamud.csproj" />
7776
</ItemGroup>
7877

7978
<ItemGroup>
@@ -109,6 +108,14 @@
109108
<HintPath>..\external_dependencies\SDK\FFXIV_ACT_Plugin.Resource.dll</HintPath>
110109
<Private>False</Private>
111110
</Reference>
111+
<Reference Include="Machina">
112+
<HintPath>..\external_dependencies\SDK\Machina.dll</HintPath>
113+
<Private>False</Private>
114+
</Reference>
115+
<Reference Include="Machina.FFXIV">
116+
<HintPath>..\external_dependencies\SDK\Machina.FFXIV.dll</HintPath>
117+
<Private>False</Private>
118+
</Reference>
112119
</ItemGroup>
113120

114121
<ItemGroup>

IINACT/Plugin.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public Plugin(IDalamudPluginInterface pluginInterface,
8080

8181
var fetchDeps =
8282
new FetchDependencies.FetchDependencies(Version, PluginInterface.AssemblyLocation.Directory!.FullName,
83-
DataManager.Language.ToString() == "ChineseSimplified", HttpClient);
83+
DataManager.Language.ToString() == "ChineseSimplified", HttpClient, Log);
8484

8585
fetchDeps.GetFfxivPlugin();
8686

IINACT/packages.lock.json

Lines changed: 67 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,68 @@
1-
{
2-
"version": 1,
3-
"dependencies": {
4-
"net8.0-windows7.0": {
5-
"DalamudPackager": {
6-
"type": "Direct",
7-
"requested": "[2.1.13, )",
8-
"resolved": "2.1.13",
9-
"contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ=="
10-
},
11-
"System.Speech": {
12-
"type": "Direct",
13-
"requested": "[8.0.0, )",
14-
"resolved": "8.0.0",
15-
"contentHash": "CNuiA6vb95Oe5PRjClZEBiaju31vwB8OIeCgeSBXyZL6+MS4RVVB2X/C11z0xCkooHE3Vy91nM2z76emIzR+sg=="
16-
},
17-
"Microsoft.CSharp": {
18-
"type": "Transitive",
19-
"resolved": "4.7.0",
20-
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
21-
},
22-
"NetCoreServer": {
23-
"type": "Transitive",
24-
"resolved": "8.0.7",
25-
"contentHash": "BhNiJ6EVxKS1eKqlHINy7BruLDc+xCH59oZxu7d2Hve8CzFki69PumVlf6vyAQvIVfxbZiHw/FI3UHNnMajuVg=="
26-
},
27-
"System.Data.DataSetExtensions": {
28-
"type": "Transitive",
29-
"resolved": "4.5.0",
30-
"contentHash": "221clPs1445HkTBZPL+K9sDBdJRB8UN8rgjO3ztB0CQ26z//fmJXtlsr6whGatscsKGBrhJl5bwJuKSA8mwFOw=="
31-
},
32-
"Advanced Combat Tracker": {
33-
"type": "Project"
34-
},
35-
"fetchdependencies": {
36-
"type": "Project"
37-
},
38-
"machina": {
39-
"type": "Project"
40-
},
41-
"machina.ffxiv": {
42-
"type": "Project",
43-
"dependencies": {
44-
"Machina": "[2.3.1.3, )"
45-
}
46-
},
47-
"overlayplugin.common": {
48-
"type": "Project",
49-
"dependencies": {
50-
"Advanced Combat Tracker": "[6.9.0, )",
51-
"Microsoft.CSharp": "[4.7.0, )",
52-
"System.Data.DataSetExtensions": "[4.5.0, )"
53-
}
54-
},
55-
"overlayplugin.core": {
56-
"type": "Project",
57-
"dependencies": {
58-
"Machina": "[2.3.1.3, )",
59-
"Machina.FFXIV": "[2.3.8.5, )",
60-
"Microsoft.CSharp": "[4.7.0, )",
61-
"NetCoreServer": "[8.0.7, )",
62-
"OverlayPlugin.Common": "[1.0.0, )",
63-
"System.Data.DataSetExtensions": "[4.5.0, )"
64-
}
65-
}
66-
},
67-
"net8.0-windows7.0/win-x64": {
68-
"System.Speech": {
69-
"type": "Direct",
70-
"requested": "[8.0.0, )",
71-
"resolved": "8.0.0",
72-
"contentHash": "CNuiA6vb95Oe5PRjClZEBiaju31vwB8OIeCgeSBXyZL6+MS4RVVB2X/C11z0xCkooHE3Vy91nM2z76emIzR+sg=="
73-
}
74-
}
75-
}
1+
{
2+
"version": 1,
3+
"dependencies": {
4+
"net8.0-windows7.0": {
5+
"DalamudPackager": {
6+
"type": "Direct",
7+
"requested": "[2.1.13, )",
8+
"resolved": "2.1.13",
9+
"contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ=="
10+
},
11+
"System.Speech": {
12+
"type": "Direct",
13+
"requested": "[8.0.0, )",
14+
"resolved": "8.0.0",
15+
"contentHash": "CNuiA6vb95Oe5PRjClZEBiaju31vwB8OIeCgeSBXyZL6+MS4RVVB2X/C11z0xCkooHE3Vy91nM2z76emIzR+sg=="
16+
},
17+
"Microsoft.CSharp": {
18+
"type": "Transitive",
19+
"resolved": "4.7.0",
20+
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
21+
},
22+
"NetCoreServer": {
23+
"type": "Transitive",
24+
"resolved": "8.0.7",
25+
"contentHash": "BhNiJ6EVxKS1eKqlHINy7BruLDc+xCH59oZxu7d2Hve8CzFki69PumVlf6vyAQvIVfxbZiHw/FI3UHNnMajuVg=="
26+
},
27+
"System.Data.DataSetExtensions": {
28+
"type": "Transitive",
29+
"resolved": "4.5.0",
30+
"contentHash": "221clPs1445HkTBZPL+K9sDBdJRB8UN8rgjO3ztB0CQ26z//fmJXtlsr6whGatscsKGBrhJl5bwJuKSA8mwFOw=="
31+
},
32+
"Advanced Combat Tracker": {
33+
"type": "Project"
34+
},
35+
"fetchdependencies": {
36+
"type": "Project"
37+
},
38+
"machina.ffxiv.dalamud": {
39+
"type": "Project"
40+
},
41+
"overlayplugin.common": {
42+
"type": "Project",
43+
"dependencies": {
44+
"Advanced Combat Tracker": "[6.9.0, )",
45+
"Microsoft.CSharp": "[4.7.0, )",
46+
"System.Data.DataSetExtensions": "[4.5.0, )"
47+
}
48+
},
49+
"overlayplugin.core": {
50+
"type": "Project",
51+
"dependencies": {
52+
"Microsoft.CSharp": "[4.7.0, )",
53+
"NetCoreServer": "[8.0.7, )",
54+
"OverlayPlugin.Common": "[1.0.0, )",
55+
"System.Data.DataSetExtensions": "[4.5.0, )"
56+
}
57+
}
58+
},
59+
"net8.0-windows7.0/win-x64": {
60+
"System.Speech": {
61+
"type": "Direct",
62+
"requested": "[8.0.0, )",
63+
"resolved": "8.0.0",
64+
"contentHash": "CNuiA6vb95Oe5PRjClZEBiaju31vwB8OIeCgeSBXyZL6+MS4RVVB2X/C11z0xCkooHE3Vy91nM2z76emIzR+sg=="
65+
}
66+
}
67+
}
7668
}

0 commit comments

Comments
 (0)