forked from Dradonhunter11/CorePatcher
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPatchLoader.cs
More file actions
347 lines (298 loc) · 14.1 KB
/
PatchLoader.cs
File metadata and controls
347 lines (298 loc) · 14.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using CorePatcher.Attributes;
using CorePatcher.Configs;
using log4net;
using Mono.Cecil;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using Terraria;
using Terraria.ModLoader;
using Terraria.ModLoader.UI;
using Terraria.Utilities;
using FieldAttributes = Mono.Cecil.FieldAttributes;
namespace CorePatcher
{
public class PatchLoader : Loader<ModCorePatch>
{
public static int Count => _patchList.Count;
private static readonly List<ModCorePatch> _patchList = new List<ModCorePatch>();
private static readonly List<Action> _prePatchList = new List<Action>();
private static readonly List<Action> _postPatchList = new List<Action>();
public static void RegisterPrePatchOperation(Action prePatch)
{
_prePatchList.Add(prePatch);
}
public static void RegisterPostPatchOperation(Action postPatch)
{
_postPatchList.Add(postPatch);
}
public static void AddDeps(AssemblyDefinition asmInfo)
{
PatchDepsEditing.AddDependency(asmInfo);
}
public void Register(ModCorePatch patch)
{
_patchList.Add(patch);
}
internal static void PrePatch()
{
foreach (var action in _prePatchList)
{
action();
}
}
internal static void PostPatch()
{
foreach (var action in _postPatchList)
{
action();
}
}
internal static void Apply()
{
if (DetectPatchedAssembly())
{
return;
}
var currentFile = Path.Combine(Environment.CurrentDirectory, "tModLoader.dll");
var newFile = Path.Combine(Environment.CurrentDirectory, "tmodloader2.dll");
if (File.Exists(newFile))
{
File.Delete(newFile);
}
File.Copy(currentFile, newFile);
currentFile = newFile;
using var terrariaAssembly = AssemblyDefinition.ReadAssembly(currentFile, new ReaderParameters(ReadingMode.Immediate)
{
ReadWrite = true,
InMemory = true
});
foreach (var modCorePatch in _patchList)
{
var attribute = modCorePatch.GetType().GetCustomAttribute(typeof(PatchType), true);
if (attribute != null)
{
var typeName = ((PatchType)attribute).GetTypeName();
var methods = modCorePatch.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
foreach (var methodInfo in methods)
{
var @params = methodInfo.GetParameters();
if (@params.Length == 2)
{
if (@params[0].ParameterType == typeof(TypeDefinition) &&
@params[1].ParameterType == typeof(AssemblyDefinition))
{
methodInfo.Invoke(modCorePatch,
new object[]
{
terrariaAssembly.MainModule.Types.First(p => p.FullName == typeName),
terrariaAssembly
});
}
}
}
}
}
// Write the patched assembly
terrariaAssembly.Write(Path.ChangeExtension(Path.Combine(Environment.CurrentDirectory, "tModLoader.dll"), ".patched.dll"));
if (File.Exists(newFile))
{
File.Delete(newFile);
}
CopyRuntimeConfig();
Restart();
}
public static bool DetectPatchedAssembly()
{
FieldInfo CorePatchedFieldInfo =
typeof(Main).GetField("CorePatched", BindingFlags.Public | BindingFlags.Static);
return CorePatchedFieldInfo != null;
}
/// <summary>
/// TODO: Find a way to execute this after the game has exited, maybe a batch script?
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void DeleteOnceDone(object sender, EventArgs e)
{
if (DetectPatchedAssembly())
{
string runtimeConfigPath = Path.Combine(Environment.CurrentDirectory, "tModLoader.runtimeconfig.json");
string runtimeConfigDev = Path.Combine(Environment.CurrentDirectory, "tModLoader.runtimeconfig.dev.json");
if (File.Exists(runtimeConfigPath))
{
File.Delete(Path.Combine(Environment.CurrentDirectory, "tModLoader.patched.runtimeconfig.json"));
File.Delete(Path.Combine(Environment.CurrentDirectory, "tModLoader.patched.runtimeconfig.dev.json"));
}
}
}
private static void CopyRuntimeConfig()
{
string runtimeConfigPath = Path.Combine(Environment.CurrentDirectory, "tModLoader.runtimeconfig.json");
string runtimeConfigDev = Path.Combine(Environment.CurrentDirectory, "tModLoader.runtimeconfig.dev.json");
if (File.Exists(runtimeConfigPath))
{
File.Copy(runtimeConfigPath, Path.Combine(Environment.CurrentDirectory, "tModLoader.patched.runtimeconfig.json"), true);
File.Copy(runtimeConfigDev, Path.Combine(Environment.CurrentDirectory, "tModLoader.patched.runtimeconfig.dev.json"), true);
PatchDepsEditing.PatchTargetRuntime();
}
}
private static void Restart()
{
if (!ModContent.GetInstance<CorePatcherConfig>().ReloadUponPatching)
{
return;
}
Process process = new Process();
process.StartInfo = new ProcessStartInfo("dotnet", "\"tModLoader.patched.dll\"")
{
WorkingDirectory = Environment.CurrentDirectory
};
process.Start();
Thread.Sleep(1000);
if (Environment.OSVersion.Platform == PlatformID.MacOSX)
{
MethodInfo quitGame = typeof(Main).GetMethod("QuitGame", BindingFlags.NonPublic | BindingFlags.Instance);
if (quitGame != null)
{
quitGame.Invoke(Main.instance, new object[] { });
}
Environment.Exit(0);
}
else
{
Environment.Exit(0);
}
}
}
[PatchType("Terraria.ModLoader.UI.Interface")]
internal class InterfacePatch : ModCorePatch
{
private static void ModifyModLoaderMenus(TypeDefinition type, AssemblyDefinition terraria)
{
FieldDefinition definition =
new FieldDefinition("CorePatched", FieldAttributes.Public | FieldAttributes.Static, type.Module.TypeSystem.Boolean);
var main = terraria.MainModule.Types.First(i => i.FullName == "Terraria.Main").Fields;
main.Add(definition);
EditStaticFieldString(definition);
if (!ModContent.GetInstance<CorePatcherConfig>().DevMode) return;
FieldReference infoMessage = terraria.MainModule.Types.First(i => i.FullName == "Terraria.ModLoader.UI.Interface").Fields.First(i => i.Name == "infoMessage");
FieldReference menuMode = terraria.MainModule.Types.First(i => i.FullName == "Terraria.Main").Fields.First(i => i.Name == "menuMode");
FieldReference corePatcher = terraria.MainModule.Types.First(i => i.FullName == "Terraria.Main").Fields.FirstOrDefault(i => i.Name == "CorePatched");
MethodReference show = terraria.MainModule.Types.First(i => i.FullName == "Terraria.ModLoader.UI.UIInfoMessage").Methods.First(i => i.Name == "Show");
var method = type.Methods.First(i => i.Name == "ModLoaderMenus");
var instructions = method.Body.GetILProcessor().Body.Instructions;
ILContext context = new ILContext(method);
ILCursor cursor = new ILCursor(context);
Instruction target = cursor.Instrs[cursor.Index + 3];
Instruction target2 = cursor.Instrs[2];
instructions.Insert(3, Instruction.Create(OpCodes.Brtrue, target));
instructions.Insert(3, Instruction.Create(OpCodes.Ldsfld, corePatcher));
Instruction instruction = Instruction.Create(OpCodes.Br, (Instruction)target2.Operand);
cursor.Index += 5;
cursor.EmitLdcI4(1);
cursor.EmitStsfld(corePatcher);
cursor.Emit(OpCodes.Ldsfld, infoMessage);
cursor.EmitLdstr(BuildMessage());
cursor.EmitLdsfld(menuMode);
cursor.EmitLdnull();
cursor.EmitLdstr("");
cursor.EmitLdnull();
cursor.EmitLdnull();
cursor.EmitCallvirt(show);
instructions.Insert(cursor.Index, instruction);
}
private static string BuildMessage()
{
StringBuilder builder = new StringBuilder();
builder.AppendLine("Welcome to tModLoader - Core patcher dev mode!");
builder.AppendLine("If you see this, this mean you have the dev mode option enabled in the config for Core patcher.");
builder.AppendLine("Here are a couple tips to help you in your journey through core modding!");
builder.AppendLine();
builder.AppendLine("=== View your patches ===");
builder.AppendLine("1. Open ILSpy, DNSpy or your favorite program to view C# assembly IL/Code");
builder.AppendLine("2. Go in your tML installation folder");
builder.AppendLine("3. Drag and drop the tModLoader.patched.dll into ILSpy");
builder.AppendLine("4. Go to the method/class where you have done your patches.");
builder.AppendLine();
builder.AppendLine("=== Debugging your mod with tModLoader - core patcher (Require VS) ===");
builder.AppendLine("0. Stay on this screen");
builder.AppendLine("1. In VS with your mod project opened go in the Debugging tabs and click on \"Attach to process\" (or press CTRL+ALT+P)");
builder.AppendLine("2. In the process list, find a dotnet.exe process with tmodloader as the title.");
builder.AppendLine("3. Click on attach and it's done!");
builder.AppendLine();
builder.AppendLine("Thanks for using core patcher!");
return builder.ToString();
}
private static void DelegateToInject()
{
Interface.infoMessage.Show("This is a test message", Main.menuMode);
}
private static void EditStaticFieldString(FieldDefinition definition)
{
MethodDefinition staticConstructor = definition.DeclaringType.Methods.FirstOrDefault(m => m.Name == ".cctor");
if (staticConstructor != null)
{
ILProcessor processor = staticConstructor.Body.GetILProcessor();
IList<Instruction> instructions = new List<Instruction>();
instructions.Add(processor.Create(OpCodes.Ldc_I4, 0));
instructions.Add(processor.Create(OpCodes.Stsfld, definition));
foreach (Instruction instruction in instructions)
{
processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 2, instruction);
}
}
}
/*
}
[PatchType("Terraria.Main")]
internal class MainMenuPatch : ModCorePatch
{
/// <summary>
/// Patch the string version at the bottom left of the screen
/// Mainly just to be able to tell if the patching was a success
/// </summary>
/// <param name="type"></param>
/// <param name="terraria"></param>
private static void ModifyVersionStringDefault(TypeDefinition type, AssemblyDefinition terraria)
{
MethodDefinition staticConstructor = type.Methods.FirstOrDefault(m => m.Name == ".cctor");
if (staticConstructor == null)
{
staticConstructor = new MethodDefinition(".cctor", Mono.Cecil.MethodAttributes.Static | Mono.Cecil.MethodAttributes.Private | Mono.Cecil.MethodAttributes.HideBySig | Mono.Cecil.MethodAttributes.SpecialName | Mono.Cecil.MethodAttributes.RTSpecialName, terraria.MainModule.TypeSystem.Void);
type.Methods.Add(staticConstructor);
}
EditStaticFieldString(type.Fields.FirstOrDefault(p => p.Name == "versionNumber"), "v1.4.4.9 - Core patcher");
EditStaticFieldString(type.Fields.FirstOrDefault(p => p.Name == "versionNumber2"), "v1.4.4.9 - Core patcher");
}
private static void EditStaticFieldString(FieldDefinition definition, string value)
{
MethodDefinition staticConstructor = definition.DeclaringType.Methods.FirstOrDefault(m => m.Name == ".cctor");
if (staticConstructor != null)
{
ILProcessor processor = staticConstructor.Body.GetILProcessor();
IList<Instruction> instructions = new List<Instruction>();
instructions.Add(processor.Create(OpCodes.Ldstr, value));
instructions.Add(processor.Create(OpCodes.Stsfld, definition));
foreach (Instruction instruction in instructions)
{
processor.Body.Instructions.Insert(processor.Body.Instructions.Count - 2, instruction);
}
}
}
/*
private static void AddDetectionField(TypeDefinition type, AssemblyDefinition terraria)
{
FieldDefinition definition =
new FieldDefinition("CorePatched", FieldAttributes.Public | FieldAttributes.Static, type.Module.TypeSystem.Boolean);
type.Fields.Add(definition);
}*/
}
}