forked from WolvenKit/WolvenKit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CR2WTypeManager.cs
278 lines (246 loc) · 11.2 KB
/
CR2WTypeManager.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
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using CP77.CR2W.Reflection;
namespace CP77.CR2W.Types
{
/// <summary>
/// The reflection magic happens mostly here, with System.Activator.
/// A class is instantiated from its type and the properties are deserialized from
/// cr2w later, in CVariable.Read and CR2WFile.ReadVariable
/// </summary>
public static class CR2WTypeManager
{
public static IEnumerable<string> AvailableTypes => AvailableVanillaTypes;
public static IEnumerable<string> AvailableVanillaTypes
{
get
{
const string nspace = "CP77.CR2W.Types";
var cr2wassembly = Assembly.GetExecutingAssembly();
var vanillaclassNames = cr2wassembly.GetTypes()
.Where(_ => _.IsClass && _.Namespace == nspace)
.Select(_ => _.Name);
return vanillaclassNames;
}
}
/// <summary>
/// The instantiation step of the RedEngine-3 reflection.
/// </summary>
/// <param name="typename">Can be either a generic type such as CUint32 - then converted with GetWKitTypeFromREDType, or a complex type like a
/// pointer to a class, a handle to an import file, an array, a soft reference, a static reference, various buffers, an enum.</param>
/// <param name="varname">The variable name</param>
/// <param name="cr2w">The cr2w base file</param>
/// <param name="parentVariable">The class owning this attribute</param>
/// <param name="readUnknownAsBytes"></param>
/// <returns></returns>
public static CVariable Create(string typename, string varname, CR2WFile cr2w, CVariable parentVariable, bool readUnknownAsBytes = true)
{
typename = REDReflection.GetWKitBaseTypeFromREDBaseType(typename);
var fullname = typename;
// check for normal type
if (AssemblyDictionary.TypeExists(typename))
{
var type = AssemblyDictionary.GetTypeByName(typename);
if (type != null)
{
object instance = System.Activator.CreateInstance(type, cr2w, parentVariable, varname);
return instance as CVariable;
}
}
// check for enum types
if (AssemblyDictionary.EnumExists(typename))
{
Enum e = (Enum)System.Activator.CreateInstance(AssemblyDictionary.GetEnumByName(typename));
var cenum = MakeGenericEnumType(typeof(CEnum<>), e);
return cenum;
}
else if (CR2WManager.EnumExists(typename))
{
Enum e = (Enum)System.Activator.CreateInstance(CR2WManager.GetEnumByName(typename));
var cenum = MakeGenericEnumType(typeof(CEnum<>), e);
return cenum;
}
// check for generic type
else if (typename.StartsWith('['))
{
string generictype = typename.Substring(typename.IndexOf(']') + 1);
CVariable innerobject = Create(generictype, "", cr2w, null);
var arrayacc = MakeArray(typeof(CArrayFixedSize<>), innerobject.GetType());
//arrayacc.Flags = new List<int>() { int.Parse(matchArrayType.Groups[1].Value) };
arrayacc.Elementtype = generictype;
return arrayacc as CVariable;
}
else if (typename.Contains(':'))
{
#region GENERIC TYPES
string[] splits = typename.Split(':');
string generictype = splits.First();
string innertype = string.Join(":", splits.Skip(1));
// e.g. handle:CEntityTemplate
switch (generictype)
{
case "CHandle":
case "handle":
{
CVariable innerobject = Create(innertype, "", cr2w, null);
return MakeGenericType(typeof(CHandle<>), innerobject);
}
case "wCHandle":
case "whandle":
{
CVariable innerobject = Create(innertype, "", cr2w, null);
return MakeGenericType(typeof(wCHandle<>), innerobject);
}
case "curveData":
{
var innerobject = Create(innertype, "", cr2w, null);
var obj = MakeGenericType(typeof(curveData<>), innerobject);
(obj as ICurveDataAccessor).Elementtype = innertype;
return obj;
}
case "multiChannelCurve":
{
var innerobject = Create(innertype, "", cr2w, null);
var obj = MakeGenericType(typeof(multiChannelCurve<>), innerobject);
(obj as ICurveDataAccessor).Elementtype = innertype;
return obj;
}
case "CrRef":
case "rRef":
{
CVariable innerobject = Create(innertype, "", cr2w, null);
return MakeGenericType(typeof(rRef<>), innerobject);
}
case "CraRef":
case "raRef":
{
CVariable innerobject = Create(innertype, "", cr2w, null);
return MakeGenericType(typeof(raRef<>), innerobject);
}
case "array":
{
// match pattern e.g.
// array: (array:)Float
// array of array: (array:)handle:meshMeshAppearance
CVariable innerobject = Create(innertype, "", cr2w, null);
IArrayAccessor arrayacc = MakeArray(typeof(CArray<>), innerobject.GetType());
arrayacc.Elementtype = innertype;
return arrayacc as CVariable;
}
case "static":
{
typename = generictype;
// match pattern e.g.
// static: (4),(Uint32)
var regArrayType = new Regex(@"(\d+),(.+)");
var matchArrayType = regArrayType.Match(fullname);
if (matchArrayType.Success)
{
CVariable innerobject = Create(matchArrayType.Groups[2].Value, "", cr2w, null);
var arrayacc = MakeArray(typeof(CStatic<>), innerobject.GetType());
//arrayacc.Flags = new List<int>() { int.Parse(matchArrayType.Groups[1].Value) };
arrayacc.Elementtype = matchArrayType.Groups[2].Value;
return arrayacc as CVariable;
}
else
{
throw new InvalidParsingException($"Invalid static type format: typename: {typename}.");
}
}
case "CEnum":
{
Enum innerobject = CreateEnum(innertype);
return MakeGenericEnumType(typeof(CEnum<>), innerobject);
}
default:
{
throw new MissingTypeException(generictype);
}
}
#endregion
}
else
{
// check if custom type
if (CR2WManager.TypeExists(typename))
{
var type = CR2WManager.GetTypeByName(typename);
object instance = System.Activator.CreateInstance(type, cr2w, parentVariable, varname);
return instance as CVariable;
}
// this should never happen
if (!cr2w.UnknownTypes.Contains(fullname))
cr2w.UnknownTypes.Add(fullname);
if (readUnknownAsBytes)
{
return new CBytes(cr2w, parentVariable, $"UNKNOWN:{typename}:{varname}");
}
else
return null;
}
#region LOCAL FUNCTIONS
IArrayAccessor MakeArray(Type arraytype, Type generictype)
{
Type elementType;
if (arraytype == typeof(CStatic<>))
elementType = typeof(CStatic<>).MakeGenericType(generictype);
else if (arraytype == typeof(CArrayFixedSize<>))
elementType = typeof(CArrayFixedSize<>).MakeGenericType(generictype);
else if (arraytype == typeof(CArray<>))
elementType = typeof(CArray<>).MakeGenericType(generictype);
else
{
throw new NotImplementedException();
}
var array = System.Activator.CreateInstance(elementType, cr2w, parentVariable, varname) as CVariable;
return array as IArrayAccessor;
}
CVariable MakeGenericType(Type gentype, CVariable innerobject)
{
if (innerobject != null)
{
Type elementType = gentype.MakeGenericType(innerobject.GetType());
CVariable handle = System.Activator.CreateInstance(elementType, cr2w, parentVariable, varname) as CVariable;
return handle;
}
else
{
throw new Exception();
}
}
CVariable MakeGenericEnumType(Type gentype, Enum innerobject)
{
if (innerobject != null)
{
Type elementType = gentype.MakeGenericType(innerobject.GetType());
CVariable handle = System.Activator.CreateInstance(elementType, cr2w, parentVariable, varname) as CVariable;
return handle;
}
else
{
throw new Exception();
}
}
#endregion
}
private static Enum CreateEnum(string value)
{
if (AssemblyDictionary.EnumExists(value))
{
var type = AssemblyDictionary.GetEnumByName(value);
Enum e = (Enum)System.Activator.CreateInstance(type);
return e;
}
else if (CR2WManager.EnumExists(value))
{
var type = CR2WManager.GetEnumByName(value);
Enum e = (Enum)System.Activator.CreateInstance(type);
return e;
}
return null;
}
}
}