Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / resources / resourcereader.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  ResourceReader
9 ** 
10 ** <OWNER>Microsoft</OWNER>
11 **
12 **
13 ** Purpose: Default way to read streams of resources on 
14 ** demand.
15 **
16 **         Version 2 support on October 6, 2003
17 ** 
18 ===========================================================*/
19 namespace System.Resources {
20     using System;
21     using System.IO;
22     using System.Text;
23     using System.Collections;
24     using System.Collections.Generic;
25 #if FEATURE_SERIALIZATION
26     using System.Runtime.Serialization;
27     using System.Runtime.Serialization.Formatters;
28     using System.Runtime.Serialization.Formatters.Binary;
29 #endif // FEATURE_SERIALIZATION
30     using System.Reflection;
31     using System.Security.Permissions;
32     using System.Security;
33     using System.Globalization;
34     using System.Configuration.Assemblies;
35     using System.Runtime.Versioning;
36     using System.Diagnostics.Contracts;
37
38     // Provides the default implementation of IResourceReader, reading
39     // .resources file from the system default binary format.  This class
40     // can be treated as an enumerator once.
41     // 
42     // See the RuntimeResourceSet overview for details on the system 
43     // default file format.
44     // 
45
46     internal struct ResourceLocator
47     {
48         internal Object _value;  // Can be null.  Consider WeakReference instead?
49         internal int _dataPos;
50
51         internal ResourceLocator(int dataPos, Object value)
52         {
53             _dataPos = dataPos;
54             _value = value;
55         }
56
57         internal int DataPosition {
58             get { return _dataPos; }
59             //set { _dataPos = value; }
60         }
61
62         // Allows adding in profiling data in a future version, or a special
63         // resource profiling build.  We could also use WeakReference.
64         internal Object Value {
65             get { return _value; }
66             set { _value = value; }
67         }
68
69         internal static bool CanCache(ResourceTypeCode value)
70         {
71             Contract.Assert(value >= 0, "negative ResourceTypeCode.  What?");
72             return value <= ResourceTypeCode.LastPrimitive;
73         }   
74     }
75
76
77     [System.Runtime.InteropServices.ComVisible(true)]
78     public sealed class ResourceReader : IResourceReader
79     {
80         // A reasonable default buffer size for reading from files, especially
81         // when we will likely be seeking frequently.  Could be smaller, but does
82         // it make sense to use anything less than one page?
83         private const int DefaultFileStreamBufferSize = 4096;
84
85         private BinaryReader _store;    // backing store we're reading from.
86         // Used by RuntimeResourceSet and this class's enumerator.  Maps
87         // resource name to a value, a ResourceLocator, or a 
88         // LooselyLinkedManifestResource.
89         internal Dictionary<String, ResourceLocator> _resCache;
90         private long _nameSectionOffset;  // Offset to name section of file.
91         private long _dataSectionOffset;  // Offset to Data section of file.
92
93         // Note this class is tightly coupled with UnmanagedMemoryStream.
94         // At runtime when getting an embedded resource from an assembly, 
95         // we're given an UnmanagedMemoryStream referring to the mmap'ed portion
96         // of the assembly.  The pointers here are pointers into that block of
97         // memory controlled by the OS's loader.
98         private int[] _nameHashes;    // hash values for all names.
99         [SecurityCritical]
100         private unsafe int* _nameHashesPtr;  // In case we're using UnmanagedMemoryStream
101         private int[] _namePositions; // relative locations of names
102         [SecurityCritical]
103         private unsafe int* _namePositionsPtr;  // If we're using UnmanagedMemoryStream
104         private RuntimeType[] _typeTable;    // Lazy array of Types for resource values.
105         private int[] _typeNamePositions;  // To delay initialize type table
106 #if FEATURE_SERIALIZATION
107         private BinaryFormatter _objFormatter; // Deserialization stuff.
108 #endif // FEATURE_SERIALIZATION
109         private int _numResources;    // Num of resources files, in case arrays aren't allocated.        
110
111         // We'll include a separate code path that uses UnmanagedMemoryStream to
112         // avoid allocating String objects and the like.
113         private UnmanagedMemoryStream _ums;
114
115         // Version number of .resources file, for compatibility
116         private int _version;
117
118 #if RESOURCE_FILE_FORMAT_DEBUG
119         private bool _debug;   // Whether this file has debugging stuff in it.
120 #endif
121
122 #if !FEATURE_PAL && FEATURE_SERIALIZATION
123         private bool[] _safeToDeserialize; // Whether to assert serialization permission
124         private TypeLimitingDeserializationBinder _typeLimitingBinder;
125
126         // One of our goals is to make sure localizable Windows Forms apps
127         // work in semi-trusted scenarios (ie, without serialization permission).
128         // Unfortunate we're serializing out some complex types that currently
129         // require a security check on deserialization.  We may fix this
130         // in a next version, but for now just hard-code a list.
131         // Hard-code in the assembly name (minus version) so people can't spoof us.
132         private static readonly String[] TypesSafeForDeserialization = {
133             "System.String[], mscorlib, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
134             "System.DateTime[], mscorlib, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
135             "System.Drawing.Bitmap, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
136             "System.Drawing.Imaging.Metafile, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
137             "System.Drawing.Point, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
138             "System.Drawing.PointF, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
139             "System.Drawing.Size, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
140             "System.Drawing.SizeF, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
141             "System.Drawing.Font, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
142             "System.Drawing.Icon, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
143             "System.Drawing.Color, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
144             "System.Windows.Forms.Cursor, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
145             "System.Windows.Forms.Padding, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
146             "System.Windows.Forms.LinkArea, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
147             "System.Windows.Forms.ImageListStreamer, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
148             "System.Windows.Forms.ListViewGroup, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
149             "System.Windows.Forms.ListViewItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
150             "System.Windows.Forms.ListViewItem+ListViewSubItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
151             "System.Windows.Forms.ListViewItem+ListViewSubItem+SubItemStyle, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
152             "System.Windows.Forms.OwnerDrawPropertyBag, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
153             "System.Windows.Forms.TreeNode, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken
154         };
155 #endif // !FEATURE_PAL && FEATURE_SERIALIZATION
156
157         #if FEATURE_CORECLR
158         [System.Security.SecurityCritical] // auto-generated
159         #else
160         [System.Security.SecuritySafeCritical]
161         #endif
162         [ResourceExposure(ResourceScope.Machine)]
163         [ResourceConsumption(ResourceScope.Machine)]
164         public ResourceReader(String fileName)
165         {
166             _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default);
167             _store = new BinaryReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.RandomAccess, Path.GetFileName(fileName), false), Encoding.UTF8);
168             BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(String).  UnmanagedMemoryStream: "+(_ums!=null));
169
170             try {
171                 ReadResources();
172             }
173             catch {
174                 _store.Close(); // If we threw an exception, close the file.
175                 throw;
176             }
177         }
178     
179         [System.Security.SecurityCritical]  // auto-generated_required
180         public ResourceReader(Stream stream)
181         {
182             if (stream==null)
183                 throw new ArgumentNullException("stream");
184             if (!stream.CanRead)
185                 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable"));
186             Contract.EndContractBlock();
187
188             _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default);
189             _store = new BinaryReader(stream, Encoding.UTF8);
190             // We have a faster code path for reading resource files from an assembly.
191             _ums = stream as UnmanagedMemoryStream;
192
193             BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(Stream).  UnmanagedMemoryStream: "+(_ums!=null));
194             ReadResources();
195         }
196     
197         // This is the constructor the RuntimeResourceSet calls,
198         // passing in the stream to read from and the RuntimeResourceSet's 
199         // internal hash table (hash table of names with file offsets
200         // and values, coupled to this ResourceReader).
201         [System.Security.SecurityCritical]  // auto-generated
202         internal ResourceReader(Stream stream, Dictionary<String, ResourceLocator> resCache)
203         {
204             Contract.Requires(stream != null, "Need a stream!");
205             Contract.Requires(stream.CanRead, "Stream should be readable!");
206             Contract.Requires(resCache != null, "Need a Dictionary!");
207
208             _resCache = resCache;
209             _store = new BinaryReader(stream, Encoding.UTF8);
210
211             _ums = stream as UnmanagedMemoryStream;
212
213             BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(Stream, Hashtable).  UnmanagedMemoryStream: "+(_ums!=null));
214             ReadResources();
215         }
216         
217
218         public void Close()
219         {
220             Dispose(true);
221         }
222         
223         public void Dispose()
224         {
225             Close();
226         }
227
228         [System.Security.SecuritySafeCritical]  // auto-generated
229         private unsafe void Dispose(bool disposing)
230         {
231             if (_store != null) {
232                 _resCache = null;
233                 if (disposing) {
234                     // Close the stream in a thread-safe way.  This fix means 
235                     // that we may call Close n times, but that's safe.
236                     BinaryReader copyOfStore = _store;
237                     _store = null;
238                     if (copyOfStore != null)
239                         copyOfStore.Close();
240                 }
241                 _store = null;
242                 _namePositions = null;
243                 _nameHashes = null;
244                 _ums = null;
245                 _namePositionsPtr = null;
246                 _nameHashesPtr = null;
247             }
248         }
249         
250         [System.Security.SecurityCritical]  // auto-generated
251         internal static unsafe int ReadUnalignedI4(int* p)
252         {
253             byte* buffer = (byte*)p;
254             // Unaligned, little endian format
255             return buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
256         }
257
258         private void SkipInt32() {
259             _store.BaseStream.Seek(4, SeekOrigin.Current);
260         }
261             
262
263         private void SkipString() {
264             int stringLength = _store.Read7BitEncodedInt();
265             if (stringLength < 0) {
266                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
267             }
268             _store.BaseStream.Seek(stringLength, SeekOrigin.Current);
269         }
270
271         [System.Security.SecuritySafeCritical]  // auto-generated
272         private unsafe int GetNameHash(int index)
273         {
274             Contract.Assert(index >=0 && index < _numResources, "Bad index into hash array.  index: "+index);
275             Contract.Assert((_ums == null && _nameHashes != null && _nameHashesPtr == null) || 
276                             (_ums != null && _nameHashes == null && _nameHashesPtr != null), "Internal state mangled.");
277             if (_ums == null)
278                 return _nameHashes[index];
279             else
280                 return ReadUnalignedI4(&_nameHashesPtr[index]);
281         }
282
283         [System.Security.SecuritySafeCritical]  // auto-generated
284         private unsafe int GetNamePosition(int index)
285         {
286             Contract.Assert(index >=0 && index < _numResources, "Bad index into name position array.  index: "+index);
287             Contract.Assert((_ums == null && _namePositions != null && _namePositionsPtr == null) || 
288                             (_ums != null && _namePositions == null && _namePositionsPtr != null), "Internal state mangled.");
289             int r;
290             if (_ums == null)
291                 r = _namePositions[index];
292             else
293                 r = ReadUnalignedI4(&_namePositionsPtr[index]);
294             if (r < 0 || r > _dataSectionOffset - _nameSectionOffset) {
295                 throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameInvalidOffset", r));
296             }
297             return r;
298         }
299
300         IEnumerator IEnumerable.GetEnumerator()
301         {
302             return GetEnumerator();
303         }
304
305         public IDictionaryEnumerator GetEnumerator()
306         {
307             if (_resCache == null)
308                 throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
309             return new ResourceEnumerator(this);
310         }
311
312         internal ResourceEnumerator GetEnumeratorInternal()
313         {
314             return new ResourceEnumerator(this);
315         }
316
317         // From a name, finds the associated virtual offset for the data.
318         // To read the data, seek to _dataSectionOffset + dataPos, then
319         // read the resource type & data.
320         // This does a binary search through the names.
321         internal int FindPosForResource(String name)
322         {
323             Contract.Assert(_store != null, "ResourceReader is closed!");
324             int hash = FastResourceComparer.HashFunction(name);
325             BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for "+name+"  hash: "+hash.ToString("x", CultureInfo.InvariantCulture));
326             // Binary search over the hashes.  Use the _namePositions array to 
327             // determine where they exist in the underlying stream.
328             int lo = 0;
329             int hi = _numResources - 1;
330             int index = -1;
331             bool success = false;
332             while (lo <= hi) {
333                 index = (lo + hi) >> 1;
334                 // Do NOT use subtraction here, since it will wrap for large
335                 // negative numbers. 
336                 int currentHash = GetNameHash(index);
337                 int c;
338                 if (currentHash == hash)
339                     c = 0;
340                 else if (currentHash < hash)
341                     c = -1;
342                 else
343                     c = 1;
344                 //BCLDebug.Log("RESMGRFILEFORMAT", "  Probing index "+index+"  lo: "+lo+"  hi: "+hi+"  c: "+c);
345                 if (c == 0) {
346                     success = true;
347                     break;
348                 }
349                 if (c < 0)
350                     lo = index + 1;
351                 else
352                     hi = index - 1;
353             }
354             if (!success) {
355 #if RESOURCE_FILE_FORMAT_DEBUG
356                 String lastReadString;
357                 lock(this) {
358                     _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(index), SeekOrigin.Begin);
359                     lastReadString = _store.ReadString();
360                 }
361                 BCLDebug.Log("RESMGRFILEFORMAT", LogLevel.Status, "FindPosForResource for ", name, " failed.  i: ", index, "  lo: ", lo, "  hi: ", hi, "  last read string: \"", lastReadString, '\'');
362 #endif
363                 return -1;
364             }
365             
366             // index is the location in our hash array that corresponds with a 
367             // value in the namePositions array.
368             // There could be collisions in our hash function.  Check on both sides 
369             // of index to find the range of hash values that are equal to the
370             // target hash value.
371             if (lo != index) {
372                 lo = index;
373                 while (lo > 0 && GetNameHash(lo - 1) == hash)
374                     lo--;
375             }
376             if (hi != index) {
377                 hi = index;
378                 while (hi < _numResources - 1 && GetNameHash(hi + 1) == hash)
379                     hi++;
380             }
381
382             lock(this) {
383                 for(int i = lo; i<=hi; i++) {
384                     _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(i), SeekOrigin.Begin);
385                     if (CompareStringEqualsName(name)) {
386                         int dataPos = _store.ReadInt32();
387                         if (dataPos < 0 || dataPos >= _store.BaseStream.Length - _dataSectionOffset) {
388                             throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataPos));
389                         }
390                         return dataPos;
391                     }
392                 }
393             }
394             BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for "+name+": Found a hash collision, HOWEVER, neither of these collided values equaled the given string.");
395             return -1;
396         }
397
398         // This compares the String in the .resources file at the current position
399         // with the string you pass in. 
400         // Whoever calls this method should make sure that they take a lock
401         // so no one else can cause us to seek in the stream.
402         [System.Security.SecuritySafeCritical]  // auto-generated
403         private unsafe bool CompareStringEqualsName(String name)
404         {
405             Contract.Assert(_store != null, "ResourceReader is closed!");
406             int byteLen = _store.Read7BitEncodedInt();
407             if (byteLen < 0) {
408                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
409             }
410             if (_ums != null) {
411                 byte* bytes = _ums.PositionPointer;
412                 // Skip over the data in the Stream, positioning ourselves right after it.
413                 _ums.Seek(byteLen, SeekOrigin.Current);
414                 if (_ums.Position > _ums.Length) {
415                     throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameTooLong"));
416                 }
417
418                 // On 64-bit machines, these char*'s may be misaligned.  Use a
419                 // byte-by-byte comparison instead.
420                 //return FastResourceComparer.CompareOrdinal((char*)bytes, byteLen/2, name) == 0;
421                 return FastResourceComparer.CompareOrdinal(bytes, byteLen, name) == 0;
422             }
423             else {
424                 // This code needs to be fast
425                 byte[] bytes = new byte[byteLen];
426                 int numBytesToRead = byteLen;
427                 while(numBytesToRead > 0) {
428                     int n = _store.Read(bytes, byteLen - numBytesToRead, numBytesToRead);
429                     if (n == 0)
430                         throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted"));
431                     numBytesToRead -= n;
432                 }
433                 return FastResourceComparer.CompareOrdinal(bytes, byteLen/2, name) == 0;
434             }
435         }
436
437         // This is used in the enumerator.  The enumerator iterates from 0 to n
438         // of our resources and this returns the resource name for a particular
439         // index.  The parameter is NOT a virtual offset.
440         [System.Security.SecurityCritical]  // auto-generated
441         private unsafe String AllocateStringForNameIndex(int index, out int dataOffset)
442         {
443             Contract.Assert(_store != null, "ResourceReader is closed!");
444             byte[] bytes;
445             int byteLen;
446             long nameVA = GetNamePosition(index);
447             lock (this) {
448                 _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
449                 // Can't use _store.ReadString, since it's using UTF-8!
450                 byteLen = _store.Read7BitEncodedInt();
451                 if (byteLen < 0) {
452                     throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
453                 }
454
455                 if (_ums != null) {
456                     if (_ums.Position > _ums.Length - byteLen)
457                         throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesIndexTooLong", index));
458
459                     String s = null;
460                     char* charPtr = (char*)_ums.PositionPointer;
461 #if IA64
462                     if (((int)charPtr & 1) != 0) {
463                         char[] destArray = new char[byteLen/2];
464                         fixed(char* pDest = destArray) {
465                             Buffer.Memcpy((byte*)pDest, (byte*)charPtr, byteLen);
466                         }
467                         s = new String(destArray);
468                     }
469                     else {
470 #endif //IA64
471                     if (!BitConverter.IsLittleEndian) {
472                         byte* bytePtr = (byte*) charPtr;
473                         var dest = new byte[byteLen];
474                         for (int i = 0; i < byteLen; i += 2) {
475                             dest[i] = *(bytePtr+i+1);
476                             dest[i+1] = *(bytePtr+i);
477                         }
478
479                         fixed(byte *pDest = dest) {
480                             s = new String((char *)pDest, 0, byteLen/2);
481                         }
482                     } else
483                         s = new String(charPtr, 0, byteLen/2);
484 #if IA64
485                     }
486 #endif //IA64
487                     _ums.Position += byteLen;
488                     dataOffset = _store.ReadInt32();
489                     if (dataOffset < 0 || dataOffset >= _store.BaseStream.Length - _dataSectionOffset) {
490                         throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataOffset));
491                     }
492                     return s;
493                 }
494
495                 bytes = new byte[byteLen];
496                 // We must read byteLen bytes, or we have a corrupted file.
497                 // Use a blocking read in case the stream doesn't give us back
498                 // everything immediately.
499                 int count = byteLen;
500                 while(count > 0) {
501                     int n = _store.Read(bytes, byteLen - count, count);
502                     if (n == 0)
503                         throw new EndOfStreamException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted_NameIndex", index));
504                     count -= n;
505                 }
506                 dataOffset = _store.ReadInt32();
507                 if (dataOffset < 0 || dataOffset >= _store.BaseStream.Length - _dataSectionOffset) {
508                     throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataOffset));
509                 }
510             }
511             return Encoding.Unicode.GetString(bytes, 0, byteLen);
512         }
513
514         // This is used in the enumerator.  The enumerator iterates from 0 to n
515         // of our resources and this returns the resource value for a particular
516         // index.  The parameter is NOT a virtual offset.
517         private Object GetValueForNameIndex(int index)
518         {
519             Contract.Assert(_store != null, "ResourceReader is closed!");
520             long nameVA = GetNamePosition(index);
521             lock(this) {
522                 _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
523                 SkipString();
524                 //BCLDebug.Log("RESMGRFILEFORMAT", "GetValueForNameIndex for index: "+index+"  skip (name length): "+skip);
525                 int dataPos = _store.ReadInt32();
526                 if (dataPos < 0 || dataPos >= _store.BaseStream.Length - _dataSectionOffset) {
527                     throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataPos));
528                 }
529                 BCLDebug.Log("RESMGRFILEFORMAT", "GetValueForNameIndex: dataPos: "+dataPos);
530                 ResourceTypeCode junk;
531                 if (_version == 1)
532                     return LoadObjectV1(dataPos);
533                 else
534                     return LoadObjectV2(dataPos, out junk);
535             }
536         }
537
538         // This takes a virtual offset into the data section and reads a String
539         // from that location.
540         // Anyone who calls LoadObject should make sure they take a lock so 
541         // no one can cause us to do a seek in here.
542         internal String LoadString(int pos)
543         {
544             Contract.Assert(_store != null, "ResourceReader is closed!");
545             _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
546             String s = null;
547             int typeIndex = _store.Read7BitEncodedInt();
548             if (_version == 1) {
549                 if (typeIndex == -1)
550                     return null;
551                 if (FindType(typeIndex) != typeof(String))
552                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Type", FindType(typeIndex).FullName));
553                 s = _store.ReadString();
554             }
555             else {
556                 ResourceTypeCode typeCode = (ResourceTypeCode) typeIndex;
557                 if (typeCode != ResourceTypeCode.String && typeCode != ResourceTypeCode.Null) {
558                     String typeString;
559                     if (typeCode < ResourceTypeCode.StartOfUserTypes)
560                         typeString = typeCode.ToString();
561                     else
562                         typeString = FindType(typeCode - ResourceTypeCode.StartOfUserTypes).FullName;
563                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Type", typeString));
564                 }
565                 if (typeCode == ResourceTypeCode.String) // ignore Null
566                     s = _store.ReadString();
567             }
568             BCLDebug.Log("RESMGRFILEFORMAT", "LoadString("+pos.ToString("x", CultureInfo.InvariantCulture)+" returned "+(s==null ? "[a null string]" : s));
569             return s;
570         }
571
572         // Called from RuntimeResourceSet
573         internal Object LoadObject(int pos)
574         {
575             if (_version == 1)
576                 return LoadObjectV1(pos);
577             ResourceTypeCode typeCode;
578             return LoadObjectV2(pos, out typeCode);
579         }
580
581         internal Object LoadObject(int pos, out ResourceTypeCode typeCode)
582         {
583             if (_version == 1) {
584                 Object o = LoadObjectV1(pos);
585                 typeCode = (o is String) ? ResourceTypeCode.String : ResourceTypeCode.StartOfUserTypes;
586                 return o;
587             }
588             return LoadObjectV2(pos, out typeCode);
589         }
590
591         // This takes a virtual offset into the data section and reads an Object
592         // from that location.
593         // Anyone who calls LoadObject should make sure they take a lock so 
594         // no one can cause us to do a seek in here.
595         internal Object LoadObjectV1(int pos)
596         {
597             Contract.Assert(_store != null, "ResourceReader is closed!");
598             Contract.Assert(_version == 1, ".resources file was not a V1 .resources file!");
599
600             try {
601                 // mega try-catch performs exceptionally bad on x64; factored out body into 
602                 // _LoadObjectV1 and wrap here.
603                 return _LoadObjectV1(pos);
604             }
605             catch (EndOfStreamException eof) {
606                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), eof);
607             }
608             catch (ArgumentOutOfRangeException e) {
609                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), e);
610             }
611         }
612
613 #if FEATURE_SERIALIZATION
614         [SecuritySafeCritical]
615 #endif
616         private Object _LoadObjectV1(int pos) {
617             _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
618             int typeIndex = _store.Read7BitEncodedInt();
619             if (typeIndex == -1)
620                 return null;
621             RuntimeType type = FindType(typeIndex);
622             BCLDebug.Log("RESMGRFILEFORMAT", "LoadObject type: "+type.Name+"  pos: 0x"+_store.BaseStream.Position.ToString("x", CultureInfo.InvariantCulture));
623             // Consider putting in logic to see if this type is a 
624             // primitive or a value type first, so we can reach the 
625             // deserialization code faster for arbitrary objects.
626
627             if (type == typeof(String))
628                 return _store.ReadString();
629             else if (type == typeof(Int32))
630                 return _store.ReadInt32();
631             else if (type == typeof(Byte))
632                 return _store.ReadByte();
633             else if (type == typeof(SByte))
634                 return _store.ReadSByte();
635             else if (type == typeof(Int16))
636                 return _store.ReadInt16();
637             else if (type == typeof(Int64))
638                 return _store.ReadInt64();
639             else if (type == typeof(UInt16))
640                 return _store.ReadUInt16();
641             else if (type == typeof(UInt32))
642                 return _store.ReadUInt32();
643             else if (type == typeof(UInt64))
644                 return _store.ReadUInt64();
645             else if (type == typeof(Single))
646                 return _store.ReadSingle();
647             else if (type == typeof(Double))
648                 return _store.ReadDouble();
649             else if (type == typeof(DateTime)) {
650                 // Ideally we should use DateTime's ToBinary & FromBinary,
651                 // but we can't for compatibility reasons.
652                 return new DateTime(_store.ReadInt64());
653             }
654             else if (type == typeof(TimeSpan))
655                 return new TimeSpan(_store.ReadInt64());
656             else if (type == typeof(Decimal)) {
657                 int[] bits = new int[4];
658                 for(int i=0; i<bits.Length; i++)
659                     bits[i] = _store.ReadInt32();
660                 return new Decimal(bits);
661             }
662             else {
663 #if FEATURE_SERIALIZATION
664                 return DeserializeObject(typeIndex);
665 #else
666                 throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
667 #endif // FEATURE_SERIALIZATION
668             }
669         }
670
671         internal Object LoadObjectV2(int pos, out ResourceTypeCode typeCode)
672         {
673             Contract.Assert(_store != null, "ResourceReader is closed!");
674             Contract.Assert(_version >= 2, ".resources file was not a V2 (or higher) .resources file!");
675
676             try {
677                 // mega try-catch performs exceptionally bad on x64; factored out body into 
678                 // _LoadObjectV2 and wrap here.
679                 return _LoadObjectV2(pos, out typeCode);
680             }
681             catch (EndOfStreamException eof) {
682                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), eof);
683             }
684             catch (ArgumentOutOfRangeException e) {
685                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), e);
686             }
687         }
688
689         [System.Security.SecuritySafeCritical]  // auto-generated
690         private Object _LoadObjectV2(int pos, out ResourceTypeCode typeCode) {
691             _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
692             typeCode = (ResourceTypeCode) _store.Read7BitEncodedInt();
693
694             BCLDebug.Log("RESMGRFILEFORMAT", "LoadObjectV2 type: "+typeCode+"  pos: 0x"+_store.BaseStream.Position.ToString("x", CultureInfo.InvariantCulture));
695             
696             switch(typeCode) {
697             case ResourceTypeCode.Null:
698                 return null;
699
700             case ResourceTypeCode.String:
701                 return _store.ReadString();
702                 
703             case ResourceTypeCode.Boolean:
704                 return _store.ReadBoolean();
705
706             case ResourceTypeCode.Char:
707                 return (char) _store.ReadUInt16();
708
709             case ResourceTypeCode.Byte:
710                 return _store.ReadByte();
711
712             case ResourceTypeCode.SByte:
713                 return _store.ReadSByte();
714
715             case ResourceTypeCode.Int16:
716                 return _store.ReadInt16();
717
718             case ResourceTypeCode.UInt16:
719                 return _store.ReadUInt16();
720
721             case ResourceTypeCode.Int32:
722                 return _store.ReadInt32();
723
724             case ResourceTypeCode.UInt32:
725                 return _store.ReadUInt32();
726
727             case ResourceTypeCode.Int64:
728                 return _store.ReadInt64();
729
730             case ResourceTypeCode.UInt64:
731                 return _store.ReadUInt64();
732
733             case ResourceTypeCode.Single:
734                 return _store.ReadSingle();
735
736             case ResourceTypeCode.Double:
737                 return _store.ReadDouble();
738
739             case ResourceTypeCode.Decimal:
740                 return _store.ReadDecimal();
741
742             case ResourceTypeCode.DateTime:
743                 // Use DateTime's ToBinary & FromBinary.
744                 Int64 data = _store.ReadInt64();
745                 return DateTime.FromBinary(data);
746                
747             case ResourceTypeCode.TimeSpan:
748                 Int64 ticks = _store.ReadInt64();
749                 return new TimeSpan(ticks);
750
751             // Special types
752             case ResourceTypeCode.ByteArray: {
753                     int len = _store.ReadInt32();
754                     if (len < 0) {
755                         throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
756                     }
757
758                     if (_ums == null) {
759                         if (len > _store.BaseStream.Length) {
760                             throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
761                         }
762                         return _store.ReadBytes(len);
763                     }
764
765                     if (len > _ums.Length - _ums.Position) {
766                         throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
767                     }
768
769                     byte[] bytes = new byte[len];
770                     int r = _ums.Read(bytes, 0, len);
771                     Contract.Assert(r == len, "ResourceReader needs to use a blocking read here.  (Call _store.ReadBytes(len)?)");
772                     return bytes;
773                 }
774
775             case ResourceTypeCode.Stream: {
776                     int len = _store.ReadInt32();
777                     if (len < 0) {
778                         throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
779                     }
780                     if (_ums == null) {
781                         byte[] bytes = _store.ReadBytes(len);
782                         // Lifetime of memory == lifetime of this stream.
783                         return new PinnedBufferMemoryStream(bytes);
784                     }
785
786                     // make sure we don't create an UnmanagedMemoryStream that is longer than the resource stream. 
787                     if (len > _ums.Length - _ums.Position) {
788                         throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
789                     }
790
791                     // For the case that we've memory mapped in the .resources
792                     // file, just return a Stream pointing to that block of memory.
793                     unsafe {
794                         return new UnmanagedMemoryStream(_ums.PositionPointer, len, len, FileAccess.Read, true);
795                     }
796                 }
797                 
798             default:                
799                 if (typeCode < ResourceTypeCode.StartOfUserTypes) {
800                     throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"));
801                 }
802                 break;
803             }
804
805             // Normal serialized objects
806 #if FEATURE_SERIALIZATION
807             int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
808             return DeserializeObject(typeIndex);
809 #else 
810             throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
811 #endif // FEATURE_SERIALIZATION
812         }
813
814
815 #if FEATURE_SERIALIZATION
816         // Helper method to safely deserialize a type, using a type-limiting
817         // deserialization binder to simulate a type-limiting deserializer.
818         // This method handles types that are safe to deserialize, as well as
819         // ensuring we only get back what we expect.
820         [System.Security.SecurityCritical]  // auto-generated
821         private Object DeserializeObject(int typeIndex)
822         {
823             RuntimeType type = FindType(typeIndex);
824 #if !FEATURE_PAL
825             // Initialize deserialization permission array, if needed
826             if (_safeToDeserialize == null)
827                 InitSafeToDeserializeArray();
828 #endif // !FEATURE_PAL
829
830             // Ensure that the object we deserialized is exactly the same
831             // type of object we thought we should be deserializing.  This
832             // will help prevent hacked .resources files from using our
833             // serialization permission assert to deserialize anything
834             // via a hacked type ID.   
835
836             Object graph;
837 #if FEATURE_PAL
838             graph = _objFormatter.Deserialize(_store.BaseStream);
839 #else
840             if (_safeToDeserialize[typeIndex]) {
841                 // Don't assert serialization permission - just ask the binary 
842                 // formatter to avoid a permission check.  This ensures that any
843                 // types which do demand serialization permission in their 
844                 // deserialization .cctors will fail.
845                 // Also, use a serialization binder to limit bind requests to 
846                 // our allowed list of Microsoft types.
847                 _objFormatter.Binder = _typeLimitingBinder;
848                 _typeLimitingBinder.ExpectingToDeserialize(type);
849                 graph = _objFormatter.UnsafeDeserialize(_store.BaseStream, null);
850             }
851             else {
852                 _objFormatter.Binder = null;
853                 graph = _objFormatter.Deserialize(_store.BaseStream);
854             }
855 #endif // FEATURE_PAL
856             
857             // This check is about correctness, not security at this point.
858             if (graph.GetType() != type)
859                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResType&SerBlobMismatch", type.FullName, graph.GetType().FullName));
860
861             return graph;
862         }
863 #endif // FEATURE_SERIALIZATION
864
865         // Reads in the header information for a .resources file.  Verifies some
866         // of the assumptions about this resource set, and builds the class table
867         // for the default resource file format.
868         [System.Security.SecurityCritical]  // auto-generated
869         private void ReadResources()
870         {
871             Contract.Assert(_store != null, "ResourceReader is closed!");
872 #if FEATURE_SERIALIZATION
873             BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File | StreamingContextStates.Persistence));
874 #if !FEATURE_PAL
875             _typeLimitingBinder = new TypeLimitingDeserializationBinder();
876             bf.Binder = _typeLimitingBinder;
877 #endif
878             _objFormatter = bf;
879 #endif // FEATURE_SERIALIZATION
880
881             try {
882                 // mega try-catch performs exceptionally bad on x64; factored out body into 
883                 // _ReadResources and wrap here.
884                 _ReadResources();
885             }
886             catch (EndOfStreamException eof) {
887                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"), eof);
888             }
889             catch (IndexOutOfRangeException e) {
890                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"), e);
891             }
892         }
893
894         [System.Security.SecurityCritical]  // auto-generated
895         private void _ReadResources()
896         {
897             // Read ResourceManager header
898             // Check for magic number
899             int magicNum = _store.ReadInt32();
900             if (magicNum != ResourceManager.MagicNumber)
901                 throw new ArgumentException(Environment.GetResourceString("Resources_StreamNotValid"));
902             // Assuming this is ResourceManager header V1 or greater, hopefully
903             // after the version number there is a number of bytes to skip
904             // to bypass the rest of the ResMgr header. For V2 or greater, we
905             // use this to skip to the end of the header
906             int resMgrHeaderVersion = _store.ReadInt32();
907             int numBytesToSkip = _store.ReadInt32();
908             if (numBytesToSkip < 0 || resMgrHeaderVersion < 0) {
909                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
910             }
911             if (resMgrHeaderVersion > 1) {
912                 BCLDebug.Log("RESMGRFILEFORMAT", LogLevel.Status, "ReadResources: Unexpected ResMgr header version: {0}  Skipping ahead {1} bytes.", resMgrHeaderVersion, numBytesToSkip);
913                 _store.BaseStream.Seek(numBytesToSkip, SeekOrigin.Current);
914             }
915             else {
916                 BCLDebug.Log("RESMGRFILEFORMAT", "ReadResources: Parsing ResMgr header v1.");
917                 // We don't care about numBytesToSkip; read the rest of the header
918
919                 // Read in type name for a suitable ResourceReader
920                 // Note ResourceWriter & InternalResGen use different Strings.
921                 String readerType = _store.ReadString();
922                 AssemblyName mscorlib = new AssemblyName(ResourceManager.MscorlibName);
923
924                 if (!ResourceManager.CompareNames(readerType, ResourceManager.ResReaderTypeName, mscorlib))
925                     throw new NotSupportedException(Environment.GetResourceString("NotSupported_WrongResourceReader_Type", readerType));
926
927                 // Skip over type name for a suitable ResourceSet
928                 SkipString();
929             }
930
931             // Read RuntimeResourceSet header
932             // Do file version check
933             int version = _store.ReadInt32();
934             if (version != RuntimeResourceSet.Version && version != 1)
935                 throw new ArgumentException(Environment.GetResourceString("Arg_ResourceFileUnsupportedVersion", RuntimeResourceSet.Version, version));
936             _version = version;
937
938 #if RESOURCE_FILE_FORMAT_DEBUG
939             // Look for ***DEBUG*** to see if this is a debuggable file.
940             long oldPos = _store.BaseStream.Position;
941             _debug = false;
942             try {
943                 String debugString = _store.ReadString();
944                 _debug = String.Equals("***DEBUG***", debugString);
945             }
946             catch(IOException) {
947             }
948             catch(OutOfMemoryException) {
949             }
950             if (_debug) {
951                 Console.WriteLine("ResourceReader is looking at a debuggable .resources file, version {0}", _version);
952             }
953             else {
954                 _store.BaseStream.Position = oldPos;
955             }
956 #endif
957
958             _numResources = _store.ReadInt32();
959             if (_numResources < 0) {
960                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
961             }
962             BCLDebug.Log("RESMGRFILEFORMAT", "ReadResources: Expecting " + _numResources + " resources.");
963 #if _DEBUG      
964             if (ResourceManager.DEBUG >= 4)
965                 Console.WriteLine("ResourceReader::ReadResources - Reading in "+_numResources+" resources");
966 #endif
967
968             // Read type positions into type positions array.
969             // But delay initialize the type table.
970             int numTypes = _store.ReadInt32();
971             if (numTypes < 0) {
972                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
973             }
974             _typeTable = new RuntimeType[numTypes];
975             _typeNamePositions = new int[numTypes];
976             for (int i=0; i<numTypes; i++) {
977                 _typeNamePositions[i] = (int) _store.BaseStream.Position;
978
979                 // Skip over the Strings in the file.  Don't create types.
980                 SkipString();
981             }
982
983 #if _DEBUG
984                 if (ResourceManager.DEBUG >= 5)
985                     Console.WriteLine("ResourceReader::ReadResources - Reading in "+numTypes+" type table entries");
986 #endif
987
988             // Prepare to read in the array of name hashes
989             //  Note that the name hashes array is aligned to 8 bytes so 
990             //  we can use pointers into it on 64 bit machines. (4 bytes 
991             //  may be sufficient, but let's plan for the future)
992             //  Skip over alignment stuff.  All public .resources files
993             //  should be aligned   No need to verify the byte values.
994             long pos = _store.BaseStream.Position;
995             int alignBytes = ((int)pos) & 7;
996             if (alignBytes != 0) {
997                 for (int i = 0; i < 8 - alignBytes; i++) {
998                     _store.ReadByte();
999                 }
1000             }
1001
1002             // Read in the array of name hashes
1003 #if RESOURCE_FILE_FORMAT_DEBUG
1004                 //  Skip over "HASHES->"
1005                 if (_debug) {
1006                     _store.BaseStream.Position += 8;
1007                 }
1008 #endif
1009
1010             if (_ums == null) {
1011                 _nameHashes = new int[_numResources];
1012                 for (int i = 0; i < _numResources; i++) {
1013                     _nameHashes[i] = _store.ReadInt32();
1014                 }
1015             }
1016             else {
1017                 // The hexadecimal E translates to binary 1110
1018                 // So, with this & condition we are checking that none of the highest 3 bits are
1019                 // set before multiplying, as that would cause an overflow.
1020                 if ((_numResources & 0xE0000000) != 0) {
1021                     throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
1022                 }
1023
1024                 int seekPos = unchecked(4 * _numResources);
1025                 unsafe {
1026                     _nameHashesPtr = (int*)_ums.PositionPointer;
1027                     // Skip over the array of nameHashes.
1028                     _ums.Seek(seekPos, SeekOrigin.Current);
1029                     // get the position pointer once more to check that the whole table is within the stream
1030                     byte* junk = _ums.PositionPointer;
1031                 }
1032             }
1033
1034             // Read in the array of relative positions for all the names.
1035 #if RESOURCE_FILE_FORMAT_DEBUG
1036             // Skip over "POS---->"
1037             if (_debug) {
1038                 _store.BaseStream.Position += 8;
1039             }
1040 #endif
1041             if (_ums == null) {
1042                 _namePositions = new int[_numResources];
1043                 for (int i = 0; i < _numResources; i++) {
1044                     int namePosition = _store.ReadInt32();
1045                     if (namePosition < 0) {
1046                         throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
1047                     }
1048
1049                     _namePositions[i] = namePosition;
1050                 }
1051             }
1052             else {
1053                 // The hexadecimal E translates to binary 1110
1054                 // So, with this & condition we are checking that none of the highest 3 bits are
1055                 // set before multiplying, as that would cause an overflow.
1056                 if ((_numResources & 0xE0000000) != 0) {
1057                     throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
1058                 }
1059
1060                 int seekPos = unchecked(4 * _numResources);
1061                 unsafe {
1062                     _namePositionsPtr = (int*)_ums.PositionPointer;
1063                     // Skip over the array of namePositions.
1064                     _ums.Seek(seekPos, SeekOrigin.Current);
1065                     // get the position pointer once more to check that the whole table is within the stream
1066                     byte* junk = _ums.PositionPointer;
1067                 }
1068             }
1069
1070             // Read location of data section.
1071             _dataSectionOffset = _store.ReadInt32();
1072             if (_dataSectionOffset < 0) {
1073                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
1074             }
1075
1076             // Store current location as start of name section
1077             _nameSectionOffset = _store.BaseStream.Position;
1078
1079             // _nameSectionOffset should be <= _dataSectionOffset; if not, it's corrupt
1080             if (_dataSectionOffset < _nameSectionOffset) {
1081                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
1082             }
1083
1084             BCLDebug.Log("RESMGRFILEFORMAT", String.Format(CultureInfo.InvariantCulture, "ReadResources: _nameOffset = 0x{0:x}  _dataOffset = 0x{1:x}", _nameSectionOffset, _dataSectionOffset));
1085         }
1086
1087         // This allows us to delay-initialize the Type[].  This might be a 
1088         // good startup time savings, since we might have to load assemblies
1089         // and initialize Reflection.
1090         private RuntimeType FindType(int typeIndex)
1091         {
1092             if (typeIndex < 0 || typeIndex >= _typeTable.Length) {
1093                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_InvalidType"));
1094             }
1095             if (_typeTable[typeIndex] == null) {
1096                 long oldPos = _store.BaseStream.Position;
1097                 try {
1098                     _store.BaseStream.Position = _typeNamePositions[typeIndex];
1099                     String typeName = _store.ReadString();
1100                     _typeTable[typeIndex] = (RuntimeType)Type.GetType(typeName, true);
1101                 }
1102 #if !FEATURE_SERIALIZATION
1103                 // If serialization isn't supported, we convert FileNotFoundException to 
1104                 // NotSupportedException for consistency with v2. This is a corner-case, but the 
1105                 // idea is that we want to give the user a more accurate error message. Even if
1106                 // the dependency were found, we know it will require serialization since it
1107                 // can't be one of the types we special case. So if the dependency were found,
1108                 // it would go down the serialization code path, resulting in NotSupported for 
1109                 // SKUs without serialization.
1110                 //
1111                 // We don't want to regress the expected case by checking the type info before 
1112                 // getting to Type.GetType -- this is costly with v1 resource formats.
1113                 catch (FileNotFoundException)
1114                 {
1115                     throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
1116                 }
1117 #endif // FEATURE_SERIALIZATION
1118                 finally {
1119                     _store.BaseStream.Position = oldPos;
1120                 }
1121             }
1122             Contract.Assert(_typeTable[typeIndex] != null, "Should have found a type!");
1123             return _typeTable[typeIndex];
1124         }
1125
1126 #if !FEATURE_PAL && FEATURE_SERIALIZATION
1127         [System.Security.SecurityCritical]  // auto-generated
1128         private void InitSafeToDeserializeArray()
1129         {
1130             _safeToDeserialize = new bool[_typeTable.Length];
1131             for(int i=0; i<_typeTable.Length; i++) {
1132                 long oldPos = _store.BaseStream.Position;
1133                 String typeName;
1134                 try {
1135                     _store.BaseStream.Position = _typeNamePositions[i];
1136                     typeName = _store.ReadString();
1137                 }
1138                 finally {
1139                     _store.BaseStream.Position = oldPos;
1140                 }
1141
1142                 AssemblyName an;
1143                 String typePart;
1144                 RuntimeType resourceType = (RuntimeType)Type.GetType(typeName, false);
1145                 if (resourceType == null) {
1146                     an = null;
1147                     typePart = typeName;
1148                 }
1149                 else {
1150                     // Enums should be safe to deserialize, and this helps out
1151                     // partially trusted, localized Microsoft apps.
1152                     if (resourceType.BaseType == typeof(Enum)) {
1153                         _safeToDeserialize[i] = true;
1154                         continue;
1155                     }
1156
1157                     // For most types, check our TypesSafeForDeserialization.
1158                     typePart = resourceType.FullName;
1159
1160                     an = new AssemblyName();
1161                     
1162                     // resourceType is retrieved via Type.GetType and must be a RuntimeType
1163                     RuntimeAssembly a = (RuntimeAssembly)resourceType.Assembly;
1164                     an.Init(a.GetSimpleName(), 
1165                             a.GetPublicKey(),
1166                             null, // public key token
1167                             null, // version
1168                             a.GetLocale(),
1169                             AssemblyHashAlgorithm.None,
1170                             AssemblyVersionCompatibility.SameMachine,
1171                             null, // codebase
1172                             AssemblyNameFlags.PublicKey,
1173                             null); // strong name key pair
1174                 }
1175
1176                 foreach(String safeType in TypesSafeForDeserialization) {
1177                     if (ResourceManager.CompareNames(safeType, typePart, an)) {
1178 #if _DEBUG
1179                         if (ResourceManager.DEBUG >= 7)
1180                             Console.WriteLine("ResReader: Found a type-safe type to deserialize!  Type name: {0}", typeName);
1181 #endif
1182                         _safeToDeserialize[i] = true;
1183                         continue;
1184                     }
1185                 }
1186
1187 #if _DEBUG
1188                 if (ResourceManager.DEBUG >= 7)
1189                     if (!_safeToDeserialize[i])
1190                         Console.WriteLine("ResReader: Found a type that wasn't safe to deserialize: {0}", typeName);
1191 #endif
1192             }
1193         }
1194 #endif // !FEATURE_PAL && FEATURE_SERIALIZATION
1195
1196         public void GetResourceData(String resourceName, out String resourceType, out byte[] resourceData)
1197         {
1198             if (resourceName == null)
1199                 throw new ArgumentNullException("resourceName");
1200             Contract.EndContractBlock();
1201             if (_resCache == null)
1202                 throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
1203
1204             // Get the type information from the data section.  Also,
1205             // sort all of the data section's indexes to compute length of
1206             // the serialized data for this type (making sure to subtract
1207             // off the length of the type code).
1208             int[] sortedDataPositions = new int[_numResources];
1209             int dataPos = FindPosForResource(resourceName);
1210             if( dataPos == -1) {
1211                 throw new ArgumentException(Environment.GetResourceString("Arg_ResourceNameNotExist", resourceName));
1212             }
1213             
1214             lock(this) {
1215                 // Read all the positions of data within the data section.
1216                 for(int i=0; i<_numResources; i++) {
1217                     _store.BaseStream.Position = _nameSectionOffset + GetNamePosition(i);
1218                     // Skip over name of resource
1219                     int numBytesToSkip = _store.Read7BitEncodedInt();
1220                     if (numBytesToSkip < 0) {
1221                         throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameInvalidOffset", numBytesToSkip));
1222                     }
1223                     _store.BaseStream.Position += numBytesToSkip;
1224
1225                     int dPos = _store.ReadInt32();
1226                     if (dPos < 0 || dPos >= _store.BaseStream.Length - _dataSectionOffset) {
1227                         throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dPos));
1228                     }
1229                     sortedDataPositions[i] = dPos;
1230                 }
1231                 Array.Sort(sortedDataPositions);
1232
1233                 int index = Array.BinarySearch(sortedDataPositions, dataPos);
1234                 Contract.Assert(index >= 0 && index < _numResources, "Couldn't find data position within sorted data positions array!");
1235                 long nextData = (index < _numResources - 1) ? sortedDataPositions[index + 1] + _dataSectionOffset : _store.BaseStream.Length;
1236                 int len = (int) (nextData - (dataPos + _dataSectionOffset));
1237                 Contract.Assert(len >= 0 && len <= (int) _store.BaseStream.Length - dataPos + _dataSectionOffset, "Length was negative or outside the bounds of the file!");
1238
1239                 // Read type code then byte[]
1240                 _store.BaseStream.Position = _dataSectionOffset + dataPos;
1241                 ResourceTypeCode typeCode = (ResourceTypeCode) _store.Read7BitEncodedInt();
1242                 if (typeCode < 0 || typeCode >= ResourceTypeCode.StartOfUserTypes + _typeTable.Length) {
1243                     throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_InvalidType"));
1244                 }
1245                 resourceType = TypeNameFromTypeCode(typeCode);
1246
1247                 // The length must be adjusted to subtract off the number 
1248                 // of bytes in the 7 bit encoded type code.
1249                 len -= (int) (_store.BaseStream.Position - (_dataSectionOffset + dataPos));
1250                 byte[] bytes = _store.ReadBytes(len);
1251                 if (bytes.Length != len)
1252                     throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted"));
1253                 resourceData = bytes;
1254             }
1255         }
1256
1257         private String TypeNameFromTypeCode(ResourceTypeCode typeCode)
1258         {
1259             Contract.Requires(typeCode >= 0, "can't be negative");
1260             if (typeCode < ResourceTypeCode.StartOfUserTypes) {
1261                 Contract.Assert(!String.Equals(typeCode.ToString(), "LastPrimitive"), "Change ResourceTypeCode metadata order so LastPrimitive isn't what Enum.ToString prefers.");
1262                 return "ResourceTypeCode." + typeCode.ToString();
1263             }
1264             else {
1265                 int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
1266                 Contract.Assert(typeIndex >= 0 && typeIndex < _typeTable.Length, "TypeCode is broken or corrupted!");
1267                 long oldPos = _store.BaseStream.Position;
1268                 try {
1269                     _store.BaseStream.Position = _typeNamePositions[typeIndex];
1270                     return _store.ReadString();
1271                 }
1272                 finally {
1273                     _store.BaseStream.Position = oldPos;
1274                 }
1275             }
1276         }
1277
1278 #if !FEATURE_PAL && FEATURE_SERIALIZATION
1279         // We need to build a type-limiting deserializer.  We know exactly which
1280         // type we want to deserialize, and if someone tells us we have type X
1281         // (which might be safe to deserialize) and they give us a serialized 
1282         // form of type Y, we don't want to run the deserialization constructor
1283         // because we've asserted serialization formatter permission.  Instead,
1284         // limit the binary formatter's type binding to precisely the type we 
1285         // expect.  If they ever don't match, that's a corrupt .resources file.
1286         // We also must check the complete object graph to ensure all of the 
1287         // graph contains safe objects.
1288         // Note this is tightly coupled to the BinaryFormatter, since we use 
1289         // its internal ObjectReader::FastBindToType method, and we had to
1290         // change the ObjectReader to register itself with this type. 
1291         internal sealed class TypeLimitingDeserializationBinder : SerializationBinder 
1292         {
1293             private RuntimeType _typeToDeserialize;
1294             // This is tightly coupled with the binary formatter, because we
1295             // want to use exactly the same code found in the ObjectReader
1296             // to do the lookup, then just give a thumbs up or down based on
1297             // a type equality comparison.  In the future, we could consider 
1298             // some better refactoring of this code.
1299             private ObjectReader _objectReader;
1300
1301             internal ObjectReader ObjectReader {
1302                 get { return _objectReader; }
1303                 set { _objectReader = value; }
1304             }
1305
1306             internal void ExpectingToDeserialize(RuntimeType type)
1307             {
1308                 _typeToDeserialize = type;
1309             }
1310             
1311             [System.Security.SecuritySafeCritical] // overrides transparent public member
1312             public override Type BindToType(string assemblyName, string typeName)
1313             {
1314                 // BinaryObjectReader::Bind tries us first, then its own code.
1315                 // Returning null means let the default binding rules happen.
1316                 AssemblyName an = new AssemblyName(assemblyName);
1317
1318                 bool safe = false;
1319                 foreach(String safeType in TypesSafeForDeserialization) {
1320                     if (ResourceManager.CompareNames(safeType, typeName, an)) {
1321                         safe = true;
1322                         break;
1323                     }
1324                 }
1325                 
1326                 // Microsoft types may internally use some enums that aren't 
1327                 // on our safe to deserialize list, like Font using FontStyle.
1328                 Type t = ObjectReader.FastBindToType(assemblyName, typeName);
1329                 if (t.IsEnum)
1330                     safe = true;
1331
1332                 if (safe)
1333                     return null;
1334
1335                 // Throw instead of returning null.
1336                 // If you're looking at this in a debugger, you've either 
1337                 // got a hacked .resources file on your hands, or Microsoft 
1338                 // types have taken a new dependency on another type.  Check 
1339                 // whether assemblyName & typeName refer to a trustworthy type,
1340                 // & consider adding it to the TypesSafeToDeserialize list.
1341                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResType&SerBlobMismatch", _typeToDeserialize.FullName, typeName));
1342             }
1343         }
1344 #endif // !FEATURE_PAL && FEATURE_SERIALIZATION
1345
1346
1347         internal sealed class ResourceEnumerator : IDictionaryEnumerator
1348         {
1349             private const int ENUM_DONE = Int32.MinValue;
1350             private const int ENUM_NOT_STARTED = -1;
1351
1352             private ResourceReader _reader;
1353             private bool _currentIsValid;
1354             private int _currentName;
1355             private int _dataPosition; // cached for case-insensitive table
1356
1357             internal ResourceEnumerator(ResourceReader reader)
1358             {
1359                 _currentName = ENUM_NOT_STARTED;
1360                 _reader = reader;
1361                 _dataPosition = -2;
1362             }
1363
1364             public bool MoveNext()
1365             {
1366                 if (_currentName == _reader._numResources - 1 || _currentName == ENUM_DONE) {
1367                     _currentIsValid = false;
1368                     _currentName = ENUM_DONE;
1369                     return false;
1370                 }
1371                 _currentIsValid = true;
1372                 _currentName++;
1373                 return true;
1374             }
1375         
1376             public Object Key {
1377                 [System.Security.SecuritySafeCritical]  // auto-generated
1378                 get {
1379                     if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
1380                     if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
1381                     if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
1382
1383                     return _reader.AllocateStringForNameIndex(_currentName, out _dataPosition);
1384                 }
1385             }
1386         
1387             public Object Current {
1388                 get {
1389                     return Entry;
1390                 }
1391             }
1392
1393             // Warning: This requires that you call the Key or Entry property FIRST before calling it!
1394             internal int DataPosition {
1395                 get {
1396                     return _dataPosition;
1397                 }
1398             }
1399
1400             public DictionaryEntry Entry {
1401                 [System.Security.SecuritySafeCritical]  // auto-generated
1402                 get {
1403                     if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
1404                     if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
1405                     if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
1406
1407                     String key;
1408                     Object value = null;
1409                     lock (_reader) { // locks should be taken in the same order as in RuntimeResourceSet.GetObject to avoid deadlock
1410                         lock (_reader._resCache) {
1411                             key = _reader.AllocateStringForNameIndex(_currentName, out _dataPosition); // AllocateStringForNameIndex could lock on _reader
1412                             ResourceLocator locator;
1413                             if (_reader._resCache.TryGetValue(key, out locator)) {
1414                                 value = locator.Value;
1415                             }
1416                             if (value == null) {
1417                                 if (_dataPosition == -1) 
1418                                     value = _reader.GetValueForNameIndex(_currentName);
1419                                 else 
1420                                     value = _reader.LoadObject(_dataPosition);
1421                                 // If enumeration and subsequent lookups happen very
1422                                 // frequently in the same process, add a ResourceLocator
1423                                 // to _resCache here.  But Microsoft enumerates and
1424                                 // just about everyone else does lookups.  So caching
1425                                 // here may bloat working set.
1426                             }
1427                         }
1428                     }
1429                     return new DictionaryEntry(key, value);
1430                 }
1431             }
1432     
1433             public Object Value {
1434                 get {
1435                     if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
1436                     if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
1437                     if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
1438
1439                     // Consider using _resCache here, eventually, if
1440                     // this proves to be an interesting perf scenario.
1441                     // But mixing lookups and enumerators shouldn't be
1442                     // particularly compelling.
1443                     return _reader.GetValueForNameIndex(_currentName);
1444                 }
1445             }
1446
1447             public void Reset()
1448             {
1449                 if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
1450                 _currentIsValid = false;
1451                 _currentName = ENUM_NOT_STARTED;
1452             }
1453         }
1454     }
1455 }