-
-
Notifications
You must be signed in to change notification settings - Fork 317
Expand file tree
/
Copy pathLibCpp2IlMain.cs
More file actions
249 lines (194 loc) · 10.7 KB
/
Copy pathLibCpp2IlMain.cs
File metadata and controls
249 lines (194 loc) · 10.7 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
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using AssetRipper.Primitives;
using LibCpp2IL.Logging;
using LibCpp2IL.Metadata;
using LibCpp2IL.Reflection;
namespace LibCpp2IL;
public static class LibCpp2IlMain
{
public delegate byte[]? MetadataFixupFunc(byte[] originalBytes, UnityVersion unityVersion);
private static readonly Regex UnityVersionRegex = new Regex(@"^[0-9]+\.[0-9]+\.[0-9]+[abcfxp][0-9]+$", RegexOptions.Compiled);
public class LibCpp2IlSettings
{
public bool AllowManualMetadataAndCodeRegInput;
public bool DisableMethodPointerMapping;
public bool DisableGlobalResolving;
public MetadataFixupFunc? MetadataFixupFunc; //If set, this will be called if metadata fails to parse, to allow you to attempt to fix it.
}
public static readonly LibCpp2IlSettings Settings = new();
/// <summary>
/// Initialize the metadata and binary from a pair of byte arrays, returning a context.
/// </summary>
public static LibCpp2IlContext InitializeAsContext(byte[] binaryBytes, byte[] metadataBytes, UnityVersion unityVersion)
=> LibCpp2IlContextBuilder.Build(binaryBytes, metadataBytes, unityVersion);
/// <summary>
/// Initialize the metadata and binary from file paths, returning a context.
/// </summary>
public static LibCpp2IlContext LoadFromFileAsContext(string pePath, string metadataPath, UnityVersion unityVersion)
=> LibCpp2IlContextBuilder.BuildFromFiles(pePath, metadataPath, unityVersion);
/// <summary>
/// Attempts to determine the Unity version from the given binary path and game data path
/// </summary>
/// <param name="unityPlayerPath">The path to the unity player executable - either the executable itself or [lib]unityplayer[.dll]</param>
/// <param name="gameDataPath">The path to the GameName_Data folder, from which assets files can be read.</param>
/// <returns>A valid unity version if one can be read, else 0.0.0a0</returns>
public static UnityVersion DetermineUnityVersion(string? unityPlayerPath, string? gameDataPath)
{
//We prefer pulling from assets because it gives a full version number (i.e. including what is usually an f1 at the end)
//But we can fall back to the unity player if we have to (and are on windows)
if (!string.IsNullOrEmpty(gameDataPath))
{
LibLogger.VerboseNewline($"DetermineUnityVersion: Have game data path {gameDataPath}, trying to pull version from globalgamemanagers or data.unity3d");
//Globalgamemanagers
var globalgamemanagersPath = Path.Combine(gameDataPath, "globalgamemanagers");
if (File.Exists(globalgamemanagersPath))
{
LibLogger.VerboseNewline($"DetermineUnityVersion: globalgamemanagers exists, pulling version from it");
var ggmBytes = File.ReadAllBytes(globalgamemanagersPath);
return GetVersionFromGlobalGameManagers(ggmBytes);
}
//Data.unity3d
var dataPath = Path.Combine(gameDataPath, "data.unity3d");
if (File.Exists(dataPath))
{
LibLogger.VerboseNewline($"DetermineUnityVersion: data.unity3d exists, pulling version from it");
using var dataStream = File.OpenRead(dataPath);
return GetVersionFromDataUnity3D(dataStream);
}
LibLogger.VerboseNewline($"DetermineUnityVersion: No globalgamemanagers or data.unity3d found in game data path.");
}
if (Environment.OSVersion.Platform == PlatformID.Win32NT && !string.IsNullOrEmpty(unityPlayerPath))
{
LibLogger.VerboseNewline($"DetermineUnityVersion: Running on windows so have FileVersionInfo, trying to pull version from unity player {unityPlayerPath}");
var unityVer = FileVersionInfo.GetVersionInfo(unityPlayerPath);
if (unityVer.FileMajorPart > 0)
return new UnityVersion((ushort)unityVer.FileMajorPart, (ushort)unityVer.FileMinorPart, (ushort)unityVer.FileBuildPart);
LibLogger.VerboseNewline($"DetermineUnityVersion: FileVersionInfo gave useless result.");
}
LibLogger.VerboseNewline($"DetermineUnityVersion: All methods to determine unity version failed!");
return default;
}
/// <summary>
/// Attempts to determine the Unity version from the given globalgamemanagers file
/// </summary>
/// <param name="ggmBytes">The bytes making up the globalgamemanagers asset file</param>
/// <returns>A valid unity version if one can be read, else 0.0.0a0</returns>
public static UnityVersion GetVersionFromGlobalGameManagers(byte[] ggmBytes)
{
var verString = new StringBuilder();
var idx = 0x14;
while (ggmBytes[idx] != 0)
{
verString.Append(Convert.ToChar(ggmBytes[idx]));
idx++;
}
string unityVer = verString.ToString();
if (!UnityVersionRegex.IsMatch(unityVer))
{
idx = 0x30;
verString = new StringBuilder();
while (ggmBytes[idx] != 0)
{
verString.Append(Convert.ToChar(ggmBytes[idx]));
idx++;
}
unityVer = verString.ToString().Trim();
}
return UnityVersion.Parse(unityVer);
}
/// <summary>
/// Attempts to determine the Unity version from the given data.unity3d file
/// </summary>
/// <param name="fileStream">A stream referencing the data.unity3d file. A stream is used instead of a byte array because these files can be very large. Only the first 30-or-so bytes are used.</param>
/// <returns>A valid unity version if one can be read, else 0.0.0a0</returns>
public static UnityVersion GetVersionFromDataUnity3D(Stream fileStream)
{
//data.unity3d is a bundle file and it's used on later unity versions.
//These files are usually really large and we only want the first couple bytes, so it's done via a stream.
//e.g.: Secret Neighbour
//Fake unity version at 0xC, real one at 0x12
var verString = new StringBuilder();
if (fileStream.CanSeek)
fileStream.Seek(0x12, SeekOrigin.Begin);
else
{
if (fileStream.Read(new byte[0x12], 0, 0x12) != 0x12)
throw new("Failed to seek to 0x12 in data.unity3d");
}
while (true)
{
var read = fileStream.ReadByte();
if (read == 0)
{
//I'm using a while true..break for this, shoot me.
break;
}
verString.Append(Convert.ToChar(read));
}
var unityVer = verString.ToString().Trim();
return UnityVersion.Parse(unityVer);
}
#region Legacy static API — kept for backwards compatibility during migration
// These fields exist solely to allow external consumers to keep working during the transition to context-based APIs.
// New code should use LibCpp2IlContext directly.
private static LibCpp2IlContext? _defaultContext;
[Obsolete("Use LibCpp2IlContext instead.")]
public static LibCpp2IlContext DefaultContext
{
get => _defaultContext ?? throw new InvalidOperationException("LibCpp2IL is not Initialized");
set => _defaultContext = value;
}
[Obsolete("Use LibCpp2IlContext.Binary instead.")]
public static Il2CppBinary Binary => DefaultContext.Binary;
[Obsolete("Use LibCpp2IlContext.Metadata instead.")]
public static Il2CppMetadata TheMetadata => DefaultContext.Metadata;
[Obsolete("Use context.Il2CppTypeHasNumMods5Bits instead.")]
public static bool Il2CppTypeHasNumMods5Bits => DefaultContext.Il2CppTypeHasNumMods5Bits;
[Obsolete("Use LibCpp2IlContext.MetadataVersion instead.")]
public static float MetadataVersion => DefaultContext.Metadata.MetadataVersion;
[Obsolete("Use LibCpp2IlContext.MethodsByPtr instead.")]
public static Dictionary<ulong, List<Il2CppMethodDefinition>> MethodsByPtr => DefaultContext.MethodsByPtr;
[Obsolete("Use LibCpp2IlContextBuilder directly.")]
public static bool Initialize(byte[] binaryBytes, byte[] metadataBytes, UnityVersion unityVersion)
{
_defaultContext = LibCpp2IlContextBuilder.Build(binaryBytes, metadataBytes, unityVersion);
return true;
}
[Obsolete("Use LibCpp2IlContextBuilder directly.")]
public static bool LoadFromFile(string pePath, string metadataPath, UnityVersion unityVersion)
{
_defaultContext = LibCpp2IlContextBuilder.BuildFromFiles(pePath, metadataPath, unityVersion);
return true;
}
[Obsolete("Use context.GetManagedMethodImplementationsAtAddress instead.")]
public static List<Il2CppMethodDefinition>? GetManagedMethodImplementationsAtAddress(ulong addr) => DefaultContext.GetManagedMethodImplementationsAtAddress(addr);
[Obsolete("Use context.GetAnyGlobalByAddress instead.")]
public static MetadataUsage? GetAnyGlobalByAddress(ulong address) => DefaultContext.GetAnyGlobalByAddress(address);
[Obsolete("Use context.GetLiteralGlobalByAddress instead.")]
public static MetadataUsage? GetLiteralGlobalByAddress(ulong address) => DefaultContext.GetLiteralGlobalByAddress(address);
[Obsolete("Use context.GetLiteralByAddress instead.")]
public static string? GetLiteralByAddress(ulong address) => DefaultContext.GetLiteralByAddress(address);
[Obsolete("Use context.GetRawTypeGlobalByAddress instead.")]
public static MetadataUsage? GetRawTypeGlobalByAddress(ulong address) => DefaultContext.GetRawTypeGlobalByAddress(address);
[Obsolete("Use context.GetTypeGlobalByAddress instead.")]
public static Il2CppTypeReflectionData? GetTypeGlobalByAddress(ulong address) => DefaultContext.GetTypeGlobalByAddress(address);
[Obsolete("Use context.GetRawFieldGlobalByAddress instead.")]
public static MetadataUsage? GetRawFieldGlobalByAddress(ulong address) => DefaultContext.GetRawFieldGlobalByAddress(address);
[Obsolete("Use context.GetFieldGlobalByAddress instead.")]
public static Il2CppFieldDefinition? GetFieldGlobalByAddress(ulong address) => DefaultContext.GetFieldGlobalByAddress(address);
[Obsolete("Use context.GetMethodGlobalByAddress instead.")]
public static MetadataUsage? GetMethodGlobalByAddress(ulong address) => DefaultContext.GetMethodGlobalByAddress(address);
[Obsolete("Use context.GetMethodDefinitionByGlobalAddress instead.")]
public static Il2CppMethodDefinition? GetMethodDefinitionByGlobalAddress(ulong address) => DefaultContext.GetMethodDefinitionByGlobalAddress(address);
[Obsolete("Use LibCpp2IlContext instead.")]
public static void Reset()
{
_defaultContext = null;
}
#endregion
}