/
ContentTypeReaderManager.cs
379 lines (336 loc) · 11.6 KB
/
ContentTypeReaderManager.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
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
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2024 Ethan Lee and the MonoGame Team
*
* Released under the Microsoft Public License.
* See LICENSE for details.
*/
/* Derived from code by the Mono.Xna Team (Copyright 2006).
* Released under the MIT License. See monoxna.LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
#endregion
namespace Microsoft.Xna.Framework.Content
{
public sealed class ContentTypeReaderManager
{
#region Private Variables
private Dictionary<Type, ContentTypeReader> contentReaders;
#endregion
#region Private Static Variables
private static readonly object locker;
private static readonly string assemblyName;
private static readonly Dictionary<Type, ContentTypeReader> contentReadersCache;
private static readonly string[] nestedMark = { "[[" };
// Trick to prevent the linker removing the code, but not actually execute the code
private static bool falseflag = false;
/* Static map of type names to creation functions. Required as iOS requires all
* types at compile time
*/
private static Dictionary<string, Func<ContentTypeReader>> typeCreators =
new Dictionary<string, Func<ContentTypeReader>>();
#endregion
#region Private Static Constructor
static ContentTypeReaderManager()
{
locker = new object();
contentReadersCache = new Dictionary<Type, ContentTypeReader>(255);
assemblyName = typeof(ContentTypeReaderManager).Assembly.FullName;
}
#endregion
#region Internal Constructor
internal ContentTypeReaderManager()
{
}
#endregion
#region Public Methods
public ContentTypeReader GetTypeReader(Type targetType)
{
ContentTypeReader reader;
if (contentReaders.TryGetValue(targetType, out reader))
{
return reader;
}
/* If you got here, you're in a really nasty spot...
* In extremely rare cases, a nested type will show up
* and it will STILL depend on the Microsoft DLL names.
* So, we get to prepare one more time. I bet this is
* super annoying for anyone that wants the null case
* rather than whatever the hell this mess is.
* FIXME: Do we need FullName or can we trust ToString?
* -flibit
*/
Type fixType = Type.GetType(PrepareType(targetType.FullName), false);
if (fixType != null && contentReaders.TryGetValue(fixType, out reader))
{
return reader;
}
return null;
}
#endregion
#region Internal Death Defying Method
internal ContentTypeReader[] LoadAssetReaders(ContentReader reader)
{
#pragma warning disable 0219, 0649
/* Trick to prevent the linker removing the code, but not actually execute the code
* FIXME: Do we really need this in FNA?
*/
if (falseflag)
{
/* Dummy variables required for it to work on iDevices ** DO NOT DELETE **
* This forces the classes not to be optimized out when deploying to iDevices
*/
ByteReader hByteReader = new ByteReader();
SByteReader hSByteReader = new SByteReader();
DateTimeReader hDateTimeReader = new DateTimeReader();
DecimalReader hDecimalReader = new DecimalReader();
BoundingSphereReader hBoundingSphereReader = new BoundingSphereReader();
BoundingFrustumReader hBoundingFrustumReader = new BoundingFrustumReader();
RayReader hRayReader = new RayReader();
ListReader<char> hCharListReader = new ListReader<Char>();
ListReader<Rectangle> hRectangleListReader = new ListReader<Rectangle>();
ArrayReader<Rectangle> hRectangleArrayReader = new ArrayReader<Rectangle>();
ListReader<Vector3> hVector3ListReader = new ListReader<Vector3>();
ListReader<StringReader> hStringListReader = new ListReader<StringReader>();
ListReader<int> hIntListReader = new ListReader<Int32>();
SpriteFontReader hSpriteFontReader = new SpriteFontReader();
Texture2DReader hTexture2DReader = new Texture2DReader();
CharReader hCharReader = new CharReader();
RectangleReader hRectangleReader = new RectangleReader();
StringReader hStringReader = new StringReader();
Vector2Reader hVector2Reader = new Vector2Reader();
Vector3Reader hVector3Reader = new Vector3Reader();
Vector4Reader hVector4Reader = new Vector4Reader();
CurveReader hCurveReader = new CurveReader();
IndexBufferReader hIndexBufferReader = new IndexBufferReader();
BoundingBoxReader hBoundingBoxReader = new BoundingBoxReader();
MatrixReader hMatrixReader = new MatrixReader();
BasicEffectReader hBasicEffectReader = new BasicEffectReader();
VertexBufferReader hVertexBufferReader = new VertexBufferReader();
AlphaTestEffectReader hAlphaTestEffectReader = new AlphaTestEffectReader();
EnumReader<Microsoft.Xna.Framework.Graphics.SpriteEffects> hEnumSpriteEffectsReader = new EnumReader<Graphics.SpriteEffects>();
ArrayReader<float> hArrayFloatReader = new ArrayReader<float>();
ArrayReader<Vector2> hArrayVector2Reader = new ArrayReader<Vector2>();
ListReader<Vector2> hListVector2Reader = new ListReader<Vector2>();
ArrayReader<Matrix> hArrayMatrixReader = new ArrayReader<Matrix>();
EnumReader<Microsoft.Xna.Framework.Graphics.Blend> hEnumBlendReader = new EnumReader<Graphics.Blend>();
NullableReader<Rectangle> hNullableRectReader = new NullableReader<Rectangle>();
EffectMaterialReader hEffectMaterialReader = new EffectMaterialReader();
ExternalReferenceReader hExternalReferenceReader = new ExternalReferenceReader();
SoundEffectReader hSoundEffectReader = new SoundEffectReader();
SongReader hSongReader = new SongReader();
ModelReader hModelReader = new ModelReader();
Int32Reader hInt32Reader = new Int32Reader();
}
#pragma warning restore 0219, 0649
/* The first content byte i read tells me the number of
* content readers in this XNB file.
*/
int numberOfReaders = reader.Read7BitEncodedInt();
ContentTypeReader[] newReaders = new ContentTypeReader[numberOfReaders];
BitArray needsInitialize = new BitArray(numberOfReaders);
contentReaders = new Dictionary<Type, ContentTypeReader>(numberOfReaders);
/* Lock until we're done allocating and initializing any new
* content type readers... this ensures we can load content
* from multiple threads and still cache the readers.
*/
lock (locker)
{
/* For each reader in the file, we read out the
* length of the string which contains the type
* of the reader, then we read out the string.
* Finally we instantiate an instance of that
* reader using reflection.
*/
for (int i = 0; i < numberOfReaders; i += 1)
{
/* This string tells us what reader we
* need to decode the following data.
*/
string originalReaderTypeString = reader.ReadString();
Func<ContentTypeReader> readerFunc;
if (typeCreators.TryGetValue(originalReaderTypeString, out readerFunc))
{
newReaders[i] = readerFunc();
needsInitialize[i] = true;
}
else
{
// Need to resolve namespace differences
string readerTypeString = originalReaderTypeString;
readerTypeString = PrepareType(readerTypeString);
Type l_readerType = Type.GetType(readerTypeString);
if (l_readerType != null)
{
ContentTypeReader typeReader;
if (!contentReadersCache.TryGetValue(l_readerType, out typeReader))
{
try
{
typeReader = l_readerType.GetDefaultConstructor().Invoke(null) as ContentTypeReader;
}
catch (TargetInvocationException ex)
{
/* If you are getting here, the Mono runtime
* is most likely not able to JIT the type.
* In particular, MonoTouch needs help
* instantiating types that are only defined
* in strings in Xnb files.
*/
throw new InvalidOperationException(
"Failed to get default constructor for ContentTypeReader. " +
"To work around, add a creation function to ContentTypeReaderManager.AddTypeCreator() " +
"with the following failed type string: " + originalReaderTypeString,
ex
);
}
catch (NullReferenceException ex)
{
/* If you are getting here, you are
* probably using .NET AOT and have
* an incomplete rd.xml, to aid with
* this, show a helpful exception
*/
throw new InvalidOperationException(
"Failed to get default constructor for ContentTypeReader. " +
"If you're using .NET Native AOT, ensure your rd.xml contains the following type: " +
originalReaderTypeString,
ex
);
}
needsInitialize[i] = true;
contentReadersCache.Add(l_readerType, typeReader);
}
newReaders[i] = typeReader;
}
else
{
throw new ContentLoadException(
"Could not find ContentTypeReader Type. " +
"Please ensure the name of the Assembly that " +
"contains the Type matches the assembly in the full type name: " +
originalReaderTypeString + " (" + readerTypeString + ")"
);
}
}
if (newReaders[i].TargetType != null)
{
contentReaders.Add(newReaders[i].TargetType, newReaders[i]);
}
/* I think the next 4 bytes refer to the "Version" of the type reader,
* although it always seems to be zero.
*/
reader.ReadInt32();
}
// Initialize any new readers.
for (int i = 0; i < newReaders.Length; i += 1)
{
if (needsInitialize.Get(i))
{
newReaders[i].Initialize(this);
}
}
} // lock (locker)
return newReaders;
}
#endregion
#region Internal Static Methods
/// <summary>
/// Adds the type creator.
/// </summary>
/// <param name='typeString'>
/// Type string.
/// </param>
/// <param name='createFunction'>
/// Create function.
/// </param>
internal static void AddTypeCreator(
string typeString,
Func<ContentTypeReader> createFunction
) {
if (!typeCreators.ContainsKey(typeString))
{
typeCreators.Add(typeString, createFunction);
}
}
internal static void ClearTypeCreators()
{
typeCreators.Clear();
}
/// <summary>
/// Removes Version, Culture and PublicKeyToken from a type string.
/// </summary>
/// <remarks>
/// Supports multiple generic types (e.g. Dictionary<TKey,TValue>)
/// and nested generic types (e.g. List<List<int>>).
/// </remarks>
/// <param name="type">
/// A <see cref="System.String"/>
/// </param>
/// <returns>
/// A <see cref="System.String"/>
/// </returns>
internal static string PrepareType(string type)
{
// Needed to support nested types
int count = type.Split(
nestedMark,
StringSplitOptions.None
).Length - 1;
string preparedType = type;
for (int i = 0; i < count; i += 1)
{
preparedType = Regex.Replace(
preparedType,
@"\[(.+?), Version=.+?\]",
"[$1]"
);
}
// Handle non generic types
if (preparedType.Contains("PublicKeyToken"))
{
preparedType = Regex.Replace(
preparedType,
@"(.+?), Version=.+?$",
"$1"
);
}
preparedType = preparedType.Replace(
", Microsoft.Xna.Framework.Graphics",
string.Format(
", {0}",
assemblyName
)
);
preparedType = preparedType.Replace(
", Microsoft.Xna.Framework.Video",
string.Format(
", {0}",
assemblyName
)
);
preparedType = preparedType.Replace(
", Microsoft.Xna.Framework",
string.Format(
", {0}",
assemblyName
)
);
preparedType = preparedType.Replace(
", MonoGame.Framework",
string.Format(
", {0}",
assemblyName
)
);
return preparedType;
}
#endregion
}
}