-
Notifications
You must be signed in to change notification settings - Fork 85
/
Patcher.cs
145 lines (132 loc) · 5.72 KB
/
Patcher.cs
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
namespace TrafficManager.Lifecycle {
using CSUtil.Commons;
using HarmonyLib;
using System;
using CitiesHarmony.API;
using System.Runtime.CompilerServices;
using System.Reflection;
using TrafficManager.Util;
using System.Linq;
using Patch;
using ColossalFramework.Plugins;
using TrafficManager.UI.Helpers;
public static class Patcher {
private const string ERROR_MESSAGE =
"****** ERRRROOORRRRRR!!!!!!!!!! **************\n" +
"**********************************************\n" +
" HARMONY MOD DEPENDENCY IS NOT INSTALLED!\n\n" +
SOLUTION + "\n" +
"**********************************************\n" +
"**********************************************\n";
private const string SOLUTION =
"Solution:\n" +
" - exit to desktop.\n" +
" - unsubscribe harmony mod.\n" +
" - make sure harmony mod is deleted from the content folder\n" +
" - resubscribe to harmony mod.\n" +
" - run the game again.";
internal static void AssertCitiesHarmonyInstalled() {
if (!HarmonyHelper.IsHarmonyInstalled) {
Prompt.Error("Error: Missing Harmony", SOLUTION);
throw new Exception(ERROR_MESSAGE);
}
}
public static void Install() {
bool fail = false;
#if DEBUG
Harmony.DEBUG = false; // set to true to get harmony debug info.
#endif
AssertCitiesHarmonyInstalled();
fail = !PatchAll(API.Harmony.HARMONY_ID, forbiddens: new [] {typeof(CustomPathFindPatchAttribute), typeof(PreloadPatchAttribute)});
if (fail) {
Log.Info("patcher failed");
Prompt.Error(
"TM:PE failed to load",
"Traffic Manager: President Edition failed to load. You can " +
"continue playing but it's NOT recommended. Traffic Manager will " +
"not work as expected.");
} else {
Log.Info("TMPE patches installed successfully");
}
}
public static void InstallPathFinding() {
bool fail = false;
#if DEBUG
Harmony.DEBUG = false; // set to true to get harmony debug info.
#endif
AssertCitiesHarmonyInstalled();
fail = !PatchAll(API.Harmony.HARMONY_ID_PATHFINDING, required: typeof(CustomPathFindPatchAttribute));
if (fail) {
Log.Info("TMPE Path-finding patcher failed");
Prompt.Error(
"TM:PE failed to patch Path-finding",
"Traffic Manager: President Edition failed to load necessary patches. You can " +
"continue playing but it's NOT recommended. Traffic Manager will " +
"not work as expected.");
} else {
Log.Info("TMPE Path-finding patches installed successfully");
}
}
public static void InstallPreload() {
bool fail = false;
#if DEBUG
Harmony.DEBUG = false; // set to true to get harmony debug info.
#endif
AssertCitiesHarmonyInstalled();
// reinstall:
Uninstall(API.Harmony.HARMONY_ID_PRELOAD);
fail = !PatchAll(API.Harmony.HARMONY_ID_PRELOAD, required: typeof(PreloadPatchAttribute));
if (fail) {
Log.Info("TMPE patcher failed at preload");
Prompt.Error(
"TM:PE failed to patch at preload",
"Traffic Manager: President Edition failed to load necessary patches. You can " +
"continue playing but it's NOT recommended. Traffic Manager will " +
"not work as expected.");
} else {
Log.Info("preload patches installed successfully");
}
}
/// <summary>
/// applies all attribute driven harmony patches.
/// continues on error.
/// </summary>
/// <returns>false if exception happens, true otherwise</returns>
[MethodImpl(MethodImplOptions.NoInlining)]
private static bool PatchAll(string harmonyId, Type required = null, params Type[] forbiddens) {
try {
bool success = true;
var harmony = new Harmony(harmonyId);
var assembly = Assembly.GetExecutingAssembly();
foreach (var type in AccessTools.GetTypesFromAssembly(assembly)) {
try {
if (required is not null && !type.IsDefined(required, true))
continue;
bool isForbidden = forbiddens?.Any(forbidden => type.IsDefined(forbidden, true)) ?? false;
if (isForbidden)
continue;
var methods = harmony.CreateClassProcessor(type).Patch();
if (methods != null && methods.Any()) {
var strMethods = methods.Select(_method => _method.Name).ToArray();
}
} catch (Exception ex) {
ex.LogException();
success = false;
}
}
return success;
} catch (Exception ex) {
ex.LogException();
return false;
}
}
public static void Uninstall(string harmonyId) {
try {
new Harmony(harmonyId).UnpatchAll(harmonyId);
Log.Info($"TMPE patches in [{harmonyId}] uninstalled.");
} catch(Exception ex) {
ex.LogException(true);
}
}
}
}