File: system\resources\resourcereader.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
/*============================================================
**
** Class:  ResourceReader
** 
** <OWNER>Microsoft</OWNER>
**
**
** Purpose: Default way to read streams of resources on 
** demand.
**
**         Version 2 support on October 6, 2003
** 
===========================================================*/
namespace System.Resources {
    using System;
    using System.IO;
    using System.Text;
    using System.Collections;
    using System.Collections.Generic;
#if FEATURE_SERIALIZATION
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters;
    using System.Runtime.Serialization.Formatters.Binary;
#endif // FEATURE_SERIALIZATION
    using System.Reflection;
    using System.Security.Permissions;
    using System.Security;
    using System.Globalization;
    using System.Configuration.Assemblies;
    using System.Runtime.Versioning;
    using System.Diagnostics.Contracts;
 
    // Provides the default implementation of IResourceReader, reading
    // .resources file from the system default binary format.  This class
    // can be treated as an enumerator once.
    // 
    // See the RuntimeResourceSet overview for details on the system 
    // default file format.
    // 
 
    internal struct ResourceLocator
    {
        internal Object _value;  // Can be null.  Consider WeakReference instead?
        internal int _dataPos;
 
        internal ResourceLocator(int dataPos, Object value)
        {
            _dataPos = dataPos;
            _value = value;
        }
 
        internal int DataPosition {
            get { return _dataPos; }
            //set { _dataPos = value; }
        }
 
        // Allows adding in profiling data in a future version, or a special
        // resource profiling build.  We could also use WeakReference.
        internal Object Value {
            get { return _value; }
            set { _value = value; }
        }
 
        internal static bool CanCache(ResourceTypeCode value)
        {
            Contract.Assert(value >= 0, "negative ResourceTypeCode.  What?");
            return value <= ResourceTypeCode.LastPrimitive;
        }   
    }
 
 
    [System.Runtime.InteropServices.ComVisible(true)]
    public sealed class ResourceReader : IResourceReader
    {
        // A reasonable default buffer size for reading from files, especially
        // when we will likely be seeking frequently.  Could be smaller, but does
        // it make sense to use anything less than one page?
        private const int DefaultFileStreamBufferSize = 4096;
 
        private BinaryReader _store;    // backing store we're reading from.
        // Used by RuntimeResourceSet and this class's enumerator.  Maps
        // resource name to a value, a ResourceLocator, or a 
        // LooselyLinkedManifestResource.
        internal Dictionary<String, ResourceLocator> _resCache;
        private long _nameSectionOffset;  // Offset to name section of file.
        private long _dataSectionOffset;  // Offset to Data section of file.
 
        // Note this class is tightly coupled with UnmanagedMemoryStream.
        // At runtime when getting an embedded resource from an assembly, 
        // we're given an UnmanagedMemoryStream referring to the mmap'ed portion
        // of the assembly.  The pointers here are pointers into that block of
        // memory controlled by the OS's loader.
        private int[] _nameHashes;    // hash values for all names.
        [SecurityCritical]
        private unsafe int* _nameHashesPtr;  // In case we're using UnmanagedMemoryStream
        private int[] _namePositions; // relative locations of names
        [SecurityCritical]
        private unsafe int* _namePositionsPtr;  // If we're using UnmanagedMemoryStream
        private RuntimeType[] _typeTable;    // Lazy array of Types for resource values.
        private int[] _typeNamePositions;  // To delay initialize type table
#if FEATURE_SERIALIZATION
        private BinaryFormatter _objFormatter; // Deserialization stuff.
#endif // FEATURE_SERIALIZATION
        private int _numResources;    // Num of resources files, in case arrays aren't allocated.        
 
        // We'll include a separate code path that uses UnmanagedMemoryStream to
        // avoid allocating String objects and the like.
        private UnmanagedMemoryStream _ums;
 
        // Version number of .resources file, for compatibility
        private int _version;
 
#if RESOURCE_FILE_FORMAT_DEBUG
        private bool _debug;   // Whether this file has debugging stuff in it.
#endif
 
#if !FEATURE_PAL && FEATURE_SERIALIZATION
        private bool[] _safeToDeserialize; // Whether to assert serialization permission
        private TypeLimitingDeserializationBinder _typeLimitingBinder;
 
        // One of our goals is to make sure localizable Windows Forms apps
        // work in semi-trusted scenarios (ie, without serialization permission).
        // Unfortunate we're serializing out some complex types that currently
        // require a security check on deserialization.  We may fix this
        // in a next version, but for now just hard-code a list.
        // Hard-code in the assembly name (minus version) so people can't spoof us.
        private static readonly String[] TypesSafeForDeserialization = {
            "System.String[], mscorlib, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
            "System.DateTime[], mscorlib, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
            "System.Drawing.Bitmap, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
            "System.Drawing.Imaging.Metafile, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
            "System.Drawing.Point, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
            "System.Drawing.PointF, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
            "System.Drawing.Size, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
            "System.Drawing.SizeF, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
            "System.Drawing.Font, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
            "System.Drawing.Icon, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
            "System.Drawing.Color, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
            "System.Windows.Forms.Cursor, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
            "System.Windows.Forms.Padding, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
            "System.Windows.Forms.LinkArea, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
            "System.Windows.Forms.ImageListStreamer, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
            "System.Windows.Forms.ListViewGroup, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
            "System.Windows.Forms.ListViewItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
            "System.Windows.Forms.ListViewItem+ListViewSubItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
            "System.Windows.Forms.ListViewItem+ListViewSubItem+SubItemStyle, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
            "System.Windows.Forms.OwnerDrawPropertyBag, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
            "System.Windows.Forms.TreeNode, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken
        };
#endif // !FEATURE_PAL && FEATURE_SERIALIZATION
 
        #if FEATURE_CORECLR
        [System.Security.SecurityCritical] // auto-generated
        #else
        [System.Security.SecuritySafeCritical]
        #endif
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public ResourceReader(String fileName)
        {
            _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default);
            _store = new BinaryReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.RandomAccess, Path.GetFileName(fileName), false), Encoding.UTF8);
            BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(String).  UnmanagedMemoryStream: "+(_ums!=null));
 
            try {
                ReadResources();
            }
            catch {
                _store.Close(); // If we threw an exception, close the file.
                throw;
            }
        }
    
        [System.Security.SecurityCritical]  // auto-generated_required
        public ResourceReader(Stream stream)
        {
            if (stream==null)
                throw new ArgumentNullException("stream");
            if (!stream.CanRead)
                throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable"));
            Contract.EndContractBlock();
 
            _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default);
            _store = new BinaryReader(stream, Encoding.UTF8);
            // We have a faster code path for reading resource files from an assembly.
            _ums = stream as UnmanagedMemoryStream;
 
            BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(Stream).  UnmanagedMemoryStream: "+(_ums!=null));
            ReadResources();
        }
    
        // This is the constructor the RuntimeResourceSet calls,
        // passing in the stream to read from and the RuntimeResourceSet's 
        // internal hash table (hash table of names with file offsets
        // and values, coupled to this ResourceReader).
        [System.Security.SecurityCritical]  // auto-generated
        internal ResourceReader(Stream stream, Dictionary<String, ResourceLocator> resCache)
        {
            Contract.Requires(stream != null, "Need a stream!");
            Contract.Requires(stream.CanRead, "Stream should be readable!");
            Contract.Requires(resCache != null, "Need a Dictionary!");
 
            _resCache = resCache;
            _store = new BinaryReader(stream, Encoding.UTF8);
 
            _ums = stream as UnmanagedMemoryStream;
 
            BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(Stream, Hashtable).  UnmanagedMemoryStream: "+(_ums!=null));
            ReadResources();
        }
        
 
        public void Close()
        {
            Dispose(true);
        }
        
        public void Dispose()
        {
            Close();
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        private unsafe void Dispose(bool disposing)
        {
            if (_store != null) {
                _resCache = null;
                if (disposing) {
                    // Close the stream in a thread-safe way.  This fix means 
                    // that we may call Close n times, but that's safe.
                    BinaryReader copyOfStore = _store;
                    _store = null;
                    if (copyOfStore != null)
                        copyOfStore.Close();
                }
                _store = null;
                _namePositions = null;
                _nameHashes = null;
                _ums = null;
                _namePositionsPtr = null;
                _nameHashesPtr = null;
            }
        }
        
        [System.Security.SecurityCritical]  // auto-generated
        internal static unsafe int ReadUnalignedI4(int* p)
        {
            byte* buffer = (byte*)p;
            // Unaligned, little endian format
            return buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
        }
 
        private void SkipInt32() {
            _store.BaseStream.Seek(4, SeekOrigin.Current);
        }
            
 
        private void SkipString() {
            int stringLength = _store.Read7BitEncodedInt();
            if (stringLength < 0) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
            }
            _store.BaseStream.Seek(stringLength, SeekOrigin.Current);
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        private unsafe int GetNameHash(int index)
        {
            Contract.Assert(index >=0 && index < _numResources, "Bad index into hash array.  index: "+index);
            Contract.Assert((_ums == null && _nameHashes != null && _nameHashesPtr == null) || 
                            (_ums != null && _nameHashes == null && _nameHashesPtr != null), "Internal state mangled.");
            if (_ums == null)
                return _nameHashes[index];
            else
                return ReadUnalignedI4(&_nameHashesPtr[index]);
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        private unsafe int GetNamePosition(int index)
        {
            Contract.Assert(index >=0 && index < _numResources, "Bad index into name position array.  index: "+index);
            Contract.Assert((_ums == null && _namePositions != null && _namePositionsPtr == null) || 
                            (_ums != null && _namePositions == null && _namePositionsPtr != null), "Internal state mangled.");
            int r;
            if (_ums == null)
                r = _namePositions[index];
            else
                r = ReadUnalignedI4(&_namePositionsPtr[index]);
            if (r < 0 || r > _dataSectionOffset - _nameSectionOffset) {
                throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameInvalidOffset", r));
            }
            return r;
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
 
        public IDictionaryEnumerator GetEnumerator()
        {
            if (_resCache == null)
                throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
            return new ResourceEnumerator(this);
        }
 
        internal ResourceEnumerator GetEnumeratorInternal()
        {
            return new ResourceEnumerator(this);
        }
 
        // From a name, finds the associated virtual offset for the data.
        // To read the data, seek to _dataSectionOffset + dataPos, then
        // read the resource type & data.
        // This does a binary search through the names.
        internal int FindPosForResource(String name)
        {
            Contract.Assert(_store != null, "ResourceReader is closed!");
            int hash = FastResourceComparer.HashFunction(name);
            BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for "+name+"  hash: "+hash.ToString("x", CultureInfo.InvariantCulture));
            // Binary search over the hashes.  Use the _namePositions array to 
            // determine where they exist in the underlying stream.
            int lo = 0;
            int hi = _numResources - 1;
            int index = -1;
            bool success = false;
            while (lo <= hi) {
                index = (lo + hi) >> 1;
                // Do NOT use subtraction here, since it will wrap for large
                // negative numbers. 
                int currentHash = GetNameHash(index);
                int c;
                if (currentHash == hash)
                    c = 0;
                else if (currentHash < hash)
                    c = -1;
                else
                    c = 1;
                //BCLDebug.Log("RESMGRFILEFORMAT", "  Probing index "+index+"  lo: "+lo+"  hi: "+hi+"  c: "+c);
                if (c == 0) {
                    success = true;
                    break;
                }
                if (c < 0)
                    lo = index + 1;
                else
                    hi = index - 1;
            }
            if (!success) {
#if RESOURCE_FILE_FORMAT_DEBUG
                String lastReadString;
                lock(this) {
                    _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(index), SeekOrigin.Begin);
                    lastReadString = _store.ReadString();
                }
                BCLDebug.Log("RESMGRFILEFORMAT", LogLevel.Status, "FindPosForResource for ", name, " failed.  i: ", index, "  lo: ", lo, "  hi: ", hi, "  last read string: \"", lastReadString, '\'');
#endif
                return -1;
            }
            
            // index is the location in our hash array that corresponds with a 
            // value in the namePositions array.
            // There could be collisions in our hash function.  Check on both sides 
            // of index to find the range of hash values that are equal to the
            // target hash value.
            if (lo != index) {
                lo = index;
                while (lo > 0 && GetNameHash(lo - 1) == hash)
                    lo--;
            }
            if (hi != index) {
                hi = index;
                while (hi < _numResources - 1 && GetNameHash(hi + 1) == hash)
                    hi++;
            }
 
            lock(this) {
                for(int i = lo; i<=hi; i++) {
                    _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(i), SeekOrigin.Begin);
                    if (CompareStringEqualsName(name)) {
                        int dataPos = _store.ReadInt32();
                        if (dataPos < 0 || dataPos >= _store.BaseStream.Length - _dataSectionOffset) {
                            throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataPos));
                        }
                        return dataPos;
                    }
                }
            }
            BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for "+name+": Found a hash collision, HOWEVER, neither of these collided values equaled the given string.");
            return -1;
        }
 
        // This compares the String in the .resources file at the current position
        // with the string you pass in. 
        // Whoever calls this method should make sure that they take a lock
        // so no one else can cause us to seek in the stream.
        [System.Security.SecuritySafeCritical]  // auto-generated
        private unsafe bool CompareStringEqualsName(String name)
        {
            Contract.Assert(_store != null, "ResourceReader is closed!");
            int byteLen = _store.Read7BitEncodedInt();
            if (byteLen < 0) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
            }
            if (_ums != null) {
                byte* bytes = _ums.PositionPointer;
                // Skip over the data in the Stream, positioning ourselves right after it.
                _ums.Seek(byteLen, SeekOrigin.Current);
                if (_ums.Position > _ums.Length) {
                    throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameTooLong"));
                }
 
                // On 64-bit machines, these char*'s may be misaligned.  Use a
                // byte-by-byte comparison instead.
                //return FastResourceComparer.CompareOrdinal((char*)bytes, byteLen/2, name) == 0;
                return FastResourceComparer.CompareOrdinal(bytes, byteLen, name) == 0;
            }
            else {
                // This code needs to be fast
                byte[] bytes = new byte[byteLen];
                int numBytesToRead = byteLen;
                while(numBytesToRead > 0) {
                    int n = _store.Read(bytes, byteLen - numBytesToRead, numBytesToRead);
                    if (n == 0)
                        throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted"));
                    numBytesToRead -= n;
                }
                return FastResourceComparer.CompareOrdinal(bytes, byteLen/2, name) == 0;
            }
        }
 
        // This is used in the enumerator.  The enumerator iterates from 0 to n
        // of our resources and this returns the resource name for a particular
        // index.  The parameter is NOT a virtual offset.
        [System.Security.SecurityCritical]  // auto-generated
        private unsafe String AllocateStringForNameIndex(int index, out int dataOffset)
        {
            Contract.Assert(_store != null, "ResourceReader is closed!");
            byte[] bytes;
            int byteLen;
            long nameVA = GetNamePosition(index);
            lock (this) {
                _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
                // Can't use _store.ReadString, since it's using UTF-8!
                byteLen = _store.Read7BitEncodedInt();
                if (byteLen < 0) {
                    throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
                }
 
                if (_ums != null) {
                    if (_ums.Position > _ums.Length - byteLen)
                        throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesIndexTooLong", index));
 
                    String s = null;
                    char* charPtr = (char*)_ums.PositionPointer;
#if IA64
                    if (((int)charPtr & 1) != 0) {
                        char[] destArray = new char[byteLen/2];
                        fixed(char* pDest = destArray) {
                            Buffer.Memcpy((byte*)pDest, (byte*)charPtr, byteLen);
                        }
                        s = new String(destArray);
                    }
                    else {
#endif //IA64
                    s = new String(charPtr, 0, byteLen/2);
#if IA64
                    }
#endif //IA64
                    _ums.Position += byteLen;
                    dataOffset = _store.ReadInt32();
                    if (dataOffset < 0 || dataOffset >= _store.BaseStream.Length - _dataSectionOffset) {
                        throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataOffset));
                    }
                    return s;
                }
 
                bytes = new byte[byteLen];
                // We must read byteLen bytes, or we have a corrupted file.
                // Use a blocking read in case the stream doesn't give us back
                // everything immediately.
                int count = byteLen;
                while(count > 0) {
                    int n = _store.Read(bytes, byteLen - count, count);
                    if (n == 0)
                        throw new EndOfStreamException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted_NameIndex", index));
                    count -= n;
                }
                dataOffset = _store.ReadInt32();
                if (dataOffset < 0 || dataOffset >= _store.BaseStream.Length - _dataSectionOffset) {
                    throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataOffset));
                }
            }
            return Encoding.Unicode.GetString(bytes, 0, byteLen);
        }
 
        // This is used in the enumerator.  The enumerator iterates from 0 to n
        // of our resources and this returns the resource value for a particular
        // index.  The parameter is NOT a virtual offset.
        private Object GetValueForNameIndex(int index)
        {
            Contract.Assert(_store != null, "ResourceReader is closed!");
            long nameVA = GetNamePosition(index);
            lock(this) {
                _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
                SkipString();
                //BCLDebug.Log("RESMGRFILEFORMAT", "GetValueForNameIndex for index: "+index+"  skip (name length): "+skip);
                int dataPos = _store.ReadInt32();
                if (dataPos < 0 || dataPos >= _store.BaseStream.Length - _dataSectionOffset) {
                    throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataPos));
                }
                BCLDebug.Log("RESMGRFILEFORMAT", "GetValueForNameIndex: dataPos: "+dataPos);
                ResourceTypeCode junk;
                if (_version == 1)
                    return LoadObjectV1(dataPos);
                else
                    return LoadObjectV2(dataPos, out junk);
            }
        }
 
        // This takes a virtual offset into the data section and reads a String
        // from that location.
        // Anyone who calls LoadObject should make sure they take a lock so 
        // no one can cause us to do a seek in here.
        internal String LoadString(int pos)
        {
            Contract.Assert(_store != null, "ResourceReader is closed!");
            _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
            String s = null;
            int typeIndex = _store.Read7BitEncodedInt();
            if (_version == 1) {
                if (typeIndex == -1)
                    return null;
                if (FindType(typeIndex) != typeof(String))
                    throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Type", FindType(typeIndex).FullName));
                s = _store.ReadString();
            }
            else {
                ResourceTypeCode typeCode = (ResourceTypeCode) typeIndex;
                if (typeCode != ResourceTypeCode.String && typeCode != ResourceTypeCode.Null) {
                    String typeString;
                    if (typeCode < ResourceTypeCode.StartOfUserTypes)
                        typeString = typeCode.ToString();
                    else
                        typeString = FindType(typeCode - ResourceTypeCode.StartOfUserTypes).FullName;
                    throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Type", typeString));
                }
                if (typeCode == ResourceTypeCode.String) // ignore Null
                    s = _store.ReadString();
            }
            BCLDebug.Log("RESMGRFILEFORMAT", "LoadString("+pos.ToString("x", CultureInfo.InvariantCulture)+" returned "+(s==null ? "[a null string]" : s));
            return s;
        }
 
        // Called from RuntimeResourceSet
        internal Object LoadObject(int pos)
        {
            if (_version == 1)
                return LoadObjectV1(pos);
            ResourceTypeCode typeCode;
            return LoadObjectV2(pos, out typeCode);
        }
 
        internal Object LoadObject(int pos, out ResourceTypeCode typeCode)
        {
            if (_version == 1) {
                Object o = LoadObjectV1(pos);
                typeCode = (o is String) ? ResourceTypeCode.String : ResourceTypeCode.StartOfUserTypes;
                return o;
            }
            return LoadObjectV2(pos, out typeCode);
        }
 
        // This takes a virtual offset into the data section and reads an Object
        // from that location.
        // Anyone who calls LoadObject should make sure they take a lock so 
        // no one can cause us to do a seek in here.
        internal Object LoadObjectV1(int pos)
        {
            Contract.Assert(_store != null, "ResourceReader is closed!");
            Contract.Assert(_version == 1, ".resources file was not a V1 .resources file!");
 
            try {
                // mega try-catch performs exceptionally bad on x64; factored out body into 
                // _LoadObjectV1 and wrap here.
                return _LoadObjectV1(pos);
            }
            catch (EndOfStreamException eof) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), eof);
            }
            catch (ArgumentOutOfRangeException e) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), e);
            }
        }
 
#if FEATURE_SERIALIZATION
        [SecuritySafeCritical]
#endif
        private Object _LoadObjectV1(int pos) {
            _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
            int typeIndex = _store.Read7BitEncodedInt();
            if (typeIndex == -1)
                return null;
            RuntimeType type = FindType(typeIndex);
            BCLDebug.Log("RESMGRFILEFORMAT", "LoadObject type: "+type.Name+"  pos: 0x"+_store.BaseStream.Position.ToString("x", CultureInfo.InvariantCulture));
            // Consider putting in logic to see if this type is a 
            // primitive or a value type first, so we can reach the 
            // deserialization code faster for arbitrary objects.
 
            if (type == typeof(String))
                return _store.ReadString();
            else if (type == typeof(Int32))
                return _store.ReadInt32();
            else if (type == typeof(Byte))
                return _store.ReadByte();
            else if (type == typeof(SByte))
                return _store.ReadSByte();
            else if (type == typeof(Int16))
                return _store.ReadInt16();
            else if (type == typeof(Int64))
                return _store.ReadInt64();
            else if (type == typeof(UInt16))
                return _store.ReadUInt16();
            else if (type == typeof(UInt32))
                return _store.ReadUInt32();
            else if (type == typeof(UInt64))
                return _store.ReadUInt64();
            else if (type == typeof(Single))
                return _store.ReadSingle();
            else if (type == typeof(Double))
                return _store.ReadDouble();
            else if (type == typeof(DateTime)) {
                // Ideally we should use DateTime's ToBinary & FromBinary,
                // but we can't for compatibility reasons.
                return new DateTime(_store.ReadInt64());
            }
            else if (type == typeof(TimeSpan))
                return new TimeSpan(_store.ReadInt64());
            else if (type == typeof(Decimal)) {
                int[] bits = new int[4];
                for(int i=0; i<bits.Length; i++)
                    bits[i] = _store.ReadInt32();
                return new Decimal(bits);
            }
            else {
#if FEATURE_SERIALIZATION
                return DeserializeObject(typeIndex);
#else
                throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
#endif // FEATURE_SERIALIZATION
            }
        }
 
        internal Object LoadObjectV2(int pos, out ResourceTypeCode typeCode)
        {
            Contract.Assert(_store != null, "ResourceReader is closed!");
            Contract.Assert(_version >= 2, ".resources file was not a V2 (or higher) .resources file!");
 
            try {
                // mega try-catch performs exceptionally bad on x64; factored out body into 
                // _LoadObjectV2 and wrap here.
                return _LoadObjectV2(pos, out typeCode);
            }
            catch (EndOfStreamException eof) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), eof);
            }
            catch (ArgumentOutOfRangeException e) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), e);
            }
        }
 
        [System.Security.SecuritySafeCritical]  // auto-generated
        private Object _LoadObjectV2(int pos, out ResourceTypeCode typeCode) {
            _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
            typeCode = (ResourceTypeCode) _store.Read7BitEncodedInt();
 
            BCLDebug.Log("RESMGRFILEFORMAT", "LoadObjectV2 type: "+typeCode+"  pos: 0x"+_store.BaseStream.Position.ToString("x", CultureInfo.InvariantCulture));
            
            switch(typeCode) {
            case ResourceTypeCode.Null:
                return null;
 
            case ResourceTypeCode.String:
                return _store.ReadString();
                
            case ResourceTypeCode.Boolean:
                return _store.ReadBoolean();
 
            case ResourceTypeCode.Char:
                return (char) _store.ReadUInt16();
 
            case ResourceTypeCode.Byte:
                return _store.ReadByte();
 
            case ResourceTypeCode.SByte:
                return _store.ReadSByte();
 
            case ResourceTypeCode.Int16:
                return _store.ReadInt16();
 
            case ResourceTypeCode.UInt16:
                return _store.ReadUInt16();
 
            case ResourceTypeCode.Int32:
                return _store.ReadInt32();
 
            case ResourceTypeCode.UInt32:
                return _store.ReadUInt32();
 
            case ResourceTypeCode.Int64:
                return _store.ReadInt64();
 
            case ResourceTypeCode.UInt64:
                return _store.ReadUInt64();
 
            case ResourceTypeCode.Single:
                return _store.ReadSingle();
 
            case ResourceTypeCode.Double:
                return _store.ReadDouble();
 
            case ResourceTypeCode.Decimal:
                return _store.ReadDecimal();
 
            case ResourceTypeCode.DateTime:
                // Use DateTime's ToBinary & FromBinary.
                Int64 data = _store.ReadInt64();
                return DateTime.FromBinary(data);
               
            case ResourceTypeCode.TimeSpan:
                Int64 ticks = _store.ReadInt64();
                return new TimeSpan(ticks);
 
            // Special types
            case ResourceTypeCode.ByteArray: {
                    int len = _store.ReadInt32();
                    if (len < 0) {
                        throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
                    }
 
                    if (_ums == null) {
                        if (len > _store.BaseStream.Length) {
                            throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
                        }
                        return _store.ReadBytes(len);
                    }
 
                    if (len > _ums.Length - _ums.Position) {
                        throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
                    }
 
                    byte[] bytes = new byte[len];
                    int r = _ums.Read(bytes, 0, len);
                    Contract.Assert(r == len, "ResourceReader needs to use a blocking read here.  (Call _store.ReadBytes(len)?)");
                    return bytes;
                }
 
            case ResourceTypeCode.Stream: {
                    int len = _store.ReadInt32();
                    if (len < 0) {
                        throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
                    }
                    if (_ums == null) {
                        byte[] bytes = _store.ReadBytes(len);
                        // Lifetime of memory == lifetime of this stream.
                        return new PinnedBufferMemoryStream(bytes);
                    }
 
                    // make sure we don't create an UnmanagedMemoryStream that is longer than the resource stream. 
                    if (len > _ums.Length - _ums.Position) {
                        throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
                    }
 
                    // For the case that we've memory mapped in the .resources
                    // file, just return a Stream pointing to that block of memory.
                    unsafe {
                        return new UnmanagedMemoryStream(_ums.PositionPointer, len, len, FileAccess.Read, true);
                    }
                }
                
            default:                
                if (typeCode < ResourceTypeCode.StartOfUserTypes) {
                    throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"));
                }
                break;
            }
 
            // Normal serialized objects
#if FEATURE_SERIALIZATION
            int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
            return DeserializeObject(typeIndex);
#else 
            throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
#endif // FEATURE_SERIALIZATION
        }
 
 
#if FEATURE_SERIALIZATION
        // Helper method to safely deserialize a type, using a type-limiting
        // deserialization binder to simulate a type-limiting deserializer.
        // This method handles types that are safe to deserialize, as well as
        // ensuring we only get back what we expect.
        [System.Security.SecurityCritical]  // auto-generated
        private Object DeserializeObject(int typeIndex)
        {
            RuntimeType type = FindType(typeIndex);
#if !FEATURE_PAL
            // Initialize deserialization permission array, if needed
            if (_safeToDeserialize == null)
                InitSafeToDeserializeArray();
#endif // !FEATURE_PAL
 
            // Ensure that the object we deserialized is exactly the same
            // type of object we thought we should be deserializing.  This
            // will help prevent hacked .resources files from using our
            // serialization permission assert to deserialize anything
            // via a hacked type ID.   
 
            Object graph;
#if FEATURE_PAL
            graph = _objFormatter.Deserialize(_store.BaseStream);
#else
            if (_safeToDeserialize[typeIndex]) {
                // Don't assert serialization permission - just ask the binary 
                // formatter to avoid a permission check.  This ensures that any
                // types which do demand serialization permission in their 
                // deserialization .cctors will fail.
                // Also, use a serialization binder to limit bind requests to 
                // our allowed list of Microsoft types.
                _objFormatter.Binder = _typeLimitingBinder;
                _typeLimitingBinder.ExpectingToDeserialize(type);
                graph = _objFormatter.UnsafeDeserialize(_store.BaseStream, null);
            }
            else {
                _objFormatter.Binder = null;
                graph = _objFormatter.Deserialize(_store.BaseStream);
            }
#endif // FEATURE_PAL
            
            // This check is about correctness, not security at this point.
            if (graph.GetType() != type)
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResType&SerBlobMismatch", type.FullName, graph.GetType().FullName));
 
            return graph;
        }
#endif // FEATURE_SERIALIZATION
 
        // Reads in the header information for a .resources file.  Verifies some
        // of the assumptions about this resource set, and builds the class table
        // for the default resource file format.
        [System.Security.SecurityCritical]  // auto-generated
        private void ReadResources()
        {
            Contract.Assert(_store != null, "ResourceReader is closed!");
#if FEATURE_SERIALIZATION
            BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File | StreamingContextStates.Persistence));
#if !FEATURE_PAL
            _typeLimitingBinder = new TypeLimitingDeserializationBinder();
            bf.Binder = _typeLimitingBinder;
#endif
            _objFormatter = bf;
#endif // FEATURE_SERIALIZATION
 
            try {
                // mega try-catch performs exceptionally bad on x64; factored out body into 
                // _ReadResources and wrap here.
                _ReadResources();
            }
            catch (EndOfStreamException eof) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"), eof);
            }
            catch (IndexOutOfRangeException e) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"), e);
            }
        }
 
        [System.Security.SecurityCritical]  // auto-generated
        private void _ReadResources()
        {
            // Read ResourceManager header
            // Check for magic number
            int magicNum = _store.ReadInt32();
            if (magicNum != ResourceManager.MagicNumber)
                throw new ArgumentException(Environment.GetResourceString("Resources_StreamNotValid"));
            // Assuming this is ResourceManager header V1 or greater, hopefully
            // after the version number there is a number of bytes to skip
            // to bypass the rest of the ResMgr header. For V2 or greater, we
            // use this to skip to the end of the header
            int resMgrHeaderVersion = _store.ReadInt32();
            int numBytesToSkip = _store.ReadInt32();
            if (numBytesToSkip < 0 || resMgrHeaderVersion < 0) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
            }
            if (resMgrHeaderVersion > 1) {
                BCLDebug.Log("RESMGRFILEFORMAT", LogLevel.Status, "ReadResources: Unexpected ResMgr header version: {0}  Skipping ahead {1} bytes.", resMgrHeaderVersion, numBytesToSkip);
                _store.BaseStream.Seek(numBytesToSkip, SeekOrigin.Current);
            }
            else {
                BCLDebug.Log("RESMGRFILEFORMAT", "ReadResources: Parsing ResMgr header v1.");
                // We don't care about numBytesToSkip; read the rest of the header
 
                // Read in type name for a suitable ResourceReader
                // Note ResourceWriter & InternalResGen use different Strings.
                String readerType = _store.ReadString();
                AssemblyName mscorlib = new AssemblyName(ResourceManager.MscorlibName);
 
                if (!ResourceManager.CompareNames(readerType, ResourceManager.ResReaderTypeName, mscorlib))
                    throw new NotSupportedException(Environment.GetResourceString("NotSupported_WrongResourceReader_Type", readerType));
 
                // Skip over type name for a suitable ResourceSet
                SkipString();
            }
 
            // Read RuntimeResourceSet header
            // Do file version check
            int version = _store.ReadInt32();
            if (version != RuntimeResourceSet.Version && version != 1)
                throw new ArgumentException(Environment.GetResourceString("Arg_ResourceFileUnsupportedVersion", RuntimeResourceSet.Version, version));
            _version = version;
 
#if RESOURCE_FILE_FORMAT_DEBUG
            // Look for ***DEBUG*** to see if this is a debuggable file.
            long oldPos = _store.BaseStream.Position;
            _debug = false;
            try {
                String debugString = _store.ReadString();
                _debug = String.Equals("***DEBUG***", debugString);
            }
            catch(IOException) {
            }
            catch(OutOfMemoryException) {
            }
            if (_debug) {
                Console.WriteLine("ResourceReader is looking at a debuggable .resources file, version {0}", _version);
            }
            else {
                _store.BaseStream.Position = oldPos;
            }
#endif
 
            _numResources = _store.ReadInt32();
            if (_numResources < 0) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
            }
            BCLDebug.Log("RESMGRFILEFORMAT", "ReadResources: Expecting " + _numResources + " resources.");
#if _DEBUG      
            if (ResourceManager.DEBUG >= 4)
                Console.WriteLine("ResourceReader::ReadResources - Reading in "+_numResources+" resources");
#endif
 
            // Read type positions into type positions array.
            // But delay initialize the type table.
            int numTypes = _store.ReadInt32();
            if (numTypes < 0) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
            }
            _typeTable = new RuntimeType[numTypes];
            _typeNamePositions = new int[numTypes];
            for (int i=0; i<numTypes; i++) {
                _typeNamePositions[i] = (int) _store.BaseStream.Position;
 
                // Skip over the Strings in the file.  Don't create types.
                SkipString();
            }
 
#if _DEBUG
                if (ResourceManager.DEBUG >= 5)
                    Console.WriteLine("ResourceReader::ReadResources - Reading in "+numTypes+" type table entries");
#endif
 
            // Prepare to read in the array of name hashes
            //  Note that the name hashes array is aligned to 8 bytes so 
            //  we can use pointers into it on 64 bit machines. (4 bytes 
            //  may be sufficient, but let's plan for the future)
            //  Skip over alignment stuff.  All public .resources files
            //  should be aligned   No need to verify the byte values.
            long pos = _store.BaseStream.Position;
            int alignBytes = ((int)pos) & 7;
            if (alignBytes != 0) {
                for (int i = 0; i < 8 - alignBytes; i++) {
                    _store.ReadByte();
                }
            }
 
            // Read in the array of name hashes
#if RESOURCE_FILE_FORMAT_DEBUG
                //  Skip over "HASHES->"
                if (_debug) {
                    _store.BaseStream.Position += 8;
                }
#endif
 
            if (_ums == null) {
                _nameHashes = new int[_numResources];
                for (int i = 0; i < _numResources; i++) {
                    _nameHashes[i] = _store.ReadInt32();
                }
            }
            else {
                // The hexadecimal E translates to binary 1110
                // So, with this & condition we are checking that none of the highest 3 bits are
                // set before multiplying, as that would cause an overflow.
                if ((_numResources & 0xE0000000) != 0) {
                    throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
                }
 
                int seekPos = unchecked(4 * _numResources);
                unsafe {
                    _nameHashesPtr = (int*)_ums.PositionPointer;
                    // Skip over the array of nameHashes.
                    _ums.Seek(seekPos, SeekOrigin.Current);
                    // get the position pointer once more to check that the whole table is within the stream
                    byte* junk = _ums.PositionPointer;
                }
            }
 
            // Read in the array of relative positions for all the names.
#if RESOURCE_FILE_FORMAT_DEBUG
            // Skip over "POS---->"
            if (_debug) {
                _store.BaseStream.Position += 8;
            }
#endif
            if (_ums == null) {
                _namePositions = new int[_numResources];
                for (int i = 0; i < _numResources; i++) {
                    int namePosition = _store.ReadInt32();
                    if (namePosition < 0) {
                        throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
                    }
 
                    _namePositions[i] = namePosition;
                }
            }
            else {
                // The hexadecimal E translates to binary 1110
                // So, with this & condition we are checking that none of the highest 3 bits are
                // set before multiplying, as that would cause an overflow.
                if ((_numResources & 0xE0000000) != 0) {
                    throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
                }
 
                int seekPos = unchecked(4 * _numResources);
                unsafe {
                    _namePositionsPtr = (int*)_ums.PositionPointer;
                    // Skip over the array of namePositions.
                    _ums.Seek(seekPos, SeekOrigin.Current);
                    // get the position pointer once more to check that the whole table is within the stream
                    byte* junk = _ums.PositionPointer;
                }
            }
 
            // Read location of data section.
            _dataSectionOffset = _store.ReadInt32();
            if (_dataSectionOffset < 0) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
            }
 
            // Store current location as start of name section
            _nameSectionOffset = _store.BaseStream.Position;
 
            // _nameSectionOffset should be <= _dataSectionOffset; if not, it's corrupt
            if (_dataSectionOffset < _nameSectionOffset) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
            }
 
            BCLDebug.Log("RESMGRFILEFORMAT", String.Format(CultureInfo.InvariantCulture, "ReadResources: _nameOffset = 0x{0:x}  _dataOffset = 0x{1:x}", _nameSectionOffset, _dataSectionOffset));
        }
 
        // This allows us to delay-initialize the Type[].  This might be a 
        // good startup time savings, since we might have to load assemblies
        // and initialize Reflection.
        private RuntimeType FindType(int typeIndex)
        {
            if (typeIndex < 0 || typeIndex >= _typeTable.Length) {
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_InvalidType"));
            }
            if (_typeTable[typeIndex] == null) {
                long oldPos = _store.BaseStream.Position;
                try {
                    _store.BaseStream.Position = _typeNamePositions[typeIndex];
                    String typeName = _store.ReadString();
                    _typeTable[typeIndex] = (RuntimeType)Type.GetType(typeName, true);
                }
#if !FEATURE_SERIALIZATION
                // If serialization isn't supported, we convert FileNotFoundException to 
                // NotSupportedException for consistency with v2. This is a corner-case, but the 
                // idea is that we want to give the user a more accurate error message. Even if
                // the dependency were found, we know it will require serialization since it
                // can't be one of the types we special case. So if the dependency were found,
                // it would go down the serialization code path, resulting in NotSupported for 
                // SKUs without serialization.
                //
                // We don't want to regress the expected case by checking the type info before 
                // getting to Type.GetType -- this is costly with v1 resource formats.
                catch (FileNotFoundException)
                {
                    throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
                }
#endif // FEATURE_SERIALIZATION
                finally {
                    _store.BaseStream.Position = oldPos;
                }
            }
            Contract.Assert(_typeTable[typeIndex] != null, "Should have found a type!");
            return _typeTable[typeIndex];
        }
 
#if !FEATURE_PAL && FEATURE_SERIALIZATION
        [System.Security.SecurityCritical]  // auto-generated
        private void InitSafeToDeserializeArray()
        {
            _safeToDeserialize = new bool[_typeTable.Length];
            for(int i=0; i<_typeTable.Length; i++) {
                long oldPos = _store.BaseStream.Position;
                String typeName;
                try {
                    _store.BaseStream.Position = _typeNamePositions[i];
                    typeName = _store.ReadString();
                }
                finally {
                    _store.BaseStream.Position = oldPos;
                }
 
                AssemblyName an;
                String typePart;
                RuntimeType resourceType = (RuntimeType)Type.GetType(typeName, false);
                if (resourceType == null) {
                    an = null;
                    typePart = typeName;
                }
                else {
                    // Enums should be safe to deserialize, and this helps out
                    // partially trusted, localized Microsoft apps.
                    if (resourceType.BaseType == typeof(Enum)) {
                        _safeToDeserialize[i] = true;
                        continue;
                    }
 
                    // For most types, check our TypesSafeForDeserialization.
                    typePart = resourceType.FullName;
 
                    an = new AssemblyName();
                    
                    // resourceType is retrieved via Type.GetType and must be a RuntimeType
                    RuntimeAssembly a = (RuntimeAssembly)resourceType.Assembly;
                    an.Init(a.GetSimpleName(), 
                            a.GetPublicKey(),
                            null, // public key token
                            null, // version
                            a.GetLocale(),
                            AssemblyHashAlgorithm.None,
                            AssemblyVersionCompatibility.SameMachine,
                            null, // codebase
                            AssemblyNameFlags.PublicKey,
                            null); // strong name key pair
                }
 
                foreach(String safeType in TypesSafeForDeserialization) {
                    if (ResourceManager.CompareNames(safeType, typePart, an)) {
#if _DEBUG
                        if (ResourceManager.DEBUG >= 7)
                            Console.WriteLine("ResReader: Found a type-safe type to deserialize!  Type name: {0}", typeName);
#endif
                        _safeToDeserialize[i] = true;
                        continue;
                    }
                }
 
#if _DEBUG
                if (ResourceManager.DEBUG >= 7)
                    if (!_safeToDeserialize[i])
                        Console.WriteLine("ResReader: Found a type that wasn't safe to deserialize: {0}", typeName);
#endif
            }
        }
#endif // !FEATURE_PAL && FEATURE_SERIALIZATION
 
        public void GetResourceData(String resourceName, out String resourceType, out byte[] resourceData)
        {
            if (resourceName == null)
                throw new ArgumentNullException("resourceName");
            Contract.EndContractBlock();
            if (_resCache == null)
                throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
 
            // Get the type information from the data section.  Also,
            // sort all of the data section's indexes to compute length of
            // the serialized data for this type (making sure to subtract
            // off the length of the type code).
            int[] sortedDataPositions = new int[_numResources];
            int dataPos = FindPosForResource(resourceName);
            if( dataPos == -1) {
                throw new ArgumentException(Environment.GetResourceString("Arg_ResourceNameNotExist", resourceName));
            }
            
            lock(this) {
                // Read all the positions of data within the data section.
                for(int i=0; i<_numResources; i++) {
                    _store.BaseStream.Position = _nameSectionOffset + GetNamePosition(i);
                    // Skip over name of resource
                    int numBytesToSkip = _store.Read7BitEncodedInt();
                    if (numBytesToSkip < 0) {
                        throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameInvalidOffset", numBytesToSkip));
                    }
                    _store.BaseStream.Position += numBytesToSkip;
 
                    int dPos = _store.ReadInt32();
                    if (dPos < 0 || dPos >= _store.BaseStream.Length - _dataSectionOffset) {
                        throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dPos));
                    }
                    sortedDataPositions[i] = dPos;
                }
                Array.Sort(sortedDataPositions);
 
                int index = Array.BinarySearch(sortedDataPositions, dataPos);
                Contract.Assert(index >= 0 && index < _numResources, "Couldn't find data position within sorted data positions array!");
                long nextData = (index < _numResources - 1) ? sortedDataPositions[index + 1] + _dataSectionOffset : _store.BaseStream.Length;
                int len = (int) (nextData - (dataPos + _dataSectionOffset));
                Contract.Assert(len >= 0 && len <= (int) _store.BaseStream.Length - dataPos + _dataSectionOffset, "Length was negative or outside the bounds of the file!");
 
                // Read type code then byte[]
                _store.BaseStream.Position = _dataSectionOffset + dataPos;
                ResourceTypeCode typeCode = (ResourceTypeCode) _store.Read7BitEncodedInt();
                if (typeCode < 0 || typeCode >= ResourceTypeCode.StartOfUserTypes + _typeTable.Length) {
                    throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_InvalidType"));
                }
                resourceType = TypeNameFromTypeCode(typeCode);
 
                // The length must be adjusted to subtract off the number 
                // of bytes in the 7 bit encoded type code.
                len -= (int) (_store.BaseStream.Position - (_dataSectionOffset + dataPos));
                byte[] bytes = _store.ReadBytes(len);
                if (bytes.Length != len)
                    throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted"));
                resourceData = bytes;
            }
        }
 
        private String TypeNameFromTypeCode(ResourceTypeCode typeCode)
        {
            Contract.Requires(typeCode >= 0, "can't be negative");
            if (typeCode < ResourceTypeCode.StartOfUserTypes) {
                Contract.Assert(!String.Equals(typeCode.ToString(), "LastPrimitive"), "Change ResourceTypeCode metadata order so LastPrimitive isn't what Enum.ToString prefers.");
                return "ResourceTypeCode." + typeCode.ToString();
            }
            else {
                int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
                Contract.Assert(typeIndex >= 0 && typeIndex < _typeTable.Length, "TypeCode is broken or corrupted!");
                long oldPos = _store.BaseStream.Position;
                try {
                    _store.BaseStream.Position = _typeNamePositions[typeIndex];
                    return _store.ReadString();
                }
                finally {
                    _store.BaseStream.Position = oldPos;
                }
            }
        }
 
#if !FEATURE_PAL && FEATURE_SERIALIZATION
        // We need to build a type-limiting deserializer.  We know exactly which
        // type we want to deserialize, and if someone tells us we have type X
        // (which might be safe to deserialize) and they give us a serialized 
        // form of type Y, we don't want to run the deserialization constructor
        // because we've asserted serialization formatter permission.  Instead,
        // limit the binary formatter's type binding to precisely the type we 
        // expect.  If they ever don't match, that's a corrupt .resources file.
        // We also must check the complete object graph to ensure all of the 
        // graph contains safe objects.
        // Note this is tightly coupled to the BinaryFormatter, since we use 
        // its internal ObjectReader::FastBindToType method, and we had to
        // change the ObjectReader to register itself with this type. 
        internal sealed class TypeLimitingDeserializationBinder : SerializationBinder 
        {
            private RuntimeType _typeToDeserialize;
            // This is tightly coupled with the binary formatter, because we
            // want to use exactly the same code found in the ObjectReader
            // to do the lookup, then just give a thumbs up or down based on
            // a type equality comparison.  In the future, we could consider 
            // some better refactoring of this code.
            private ObjectReader _objectReader;
 
            internal ObjectReader ObjectReader {
                get { return _objectReader; }
                set { _objectReader = value; }
            }
 
            internal void ExpectingToDeserialize(RuntimeType type)
            {
                _typeToDeserialize = type;
            }
            
            [System.Security.SecuritySafeCritical] // overrides transparent public member
            public override Type BindToType(string assemblyName, string typeName)
            {
                // BinaryObjectReader::Bind tries us first, then its own code.
                // Returning null means let the default binding rules happen.
                AssemblyName an = new AssemblyName(assemblyName);
 
                bool safe = false;
                foreach(String safeType in TypesSafeForDeserialization) {
                    if (ResourceManager.CompareNames(safeType, typeName, an)) {
                        safe = true;
                        break;
                    }
                }
                
                // Microsoft types may internally use some enums that aren't 
                // on our safe to deserialize list, like Font using FontStyle.
                Type t = ObjectReader.FastBindToType(assemblyName, typeName);
                if (t.IsEnum)
                    safe = true;
 
                if (safe)
                    return null;
 
                // Throw instead of returning null.
                // If you're looking at this in a debugger, you've either 
                // got a hacked .resources file on your hands, or Microsoft 
                // types have taken a new dependency on another type.  Check 
                // whether assemblyName & typeName refer to a trustworthy type,
                // & consider adding it to the TypesSafeToDeserialize list.
                throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResType&SerBlobMismatch", _typeToDeserialize.FullName, typeName));
            }
        }
#endif // !FEATURE_PAL && FEATURE_SERIALIZATION
 
 
        internal sealed class ResourceEnumerator : IDictionaryEnumerator
        {
            private const int ENUM_DONE = Int32.MinValue;
            private const int ENUM_NOT_STARTED = -1;
 
            private ResourceReader _reader;
            private bool _currentIsValid;
            private int _currentName;
            private int _dataPosition; // cached for case-insensitive table
 
            internal ResourceEnumerator(ResourceReader reader)
            {
                _currentName = ENUM_NOT_STARTED;
                _reader = reader;
                _dataPosition = -2;
            }
 
            public bool MoveNext()
            {
                if (_currentName == _reader._numResources - 1 || _currentName == ENUM_DONE) {
                    _currentIsValid = false;
                    _currentName = ENUM_DONE;
                    return false;
                }
                _currentIsValid = true;
                _currentName++;
                return true;
            }
        
            public Object Key {
                [System.Security.SecuritySafeCritical]  // auto-generated
                get {
                    if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
                    if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
                    if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
 
                    return _reader.AllocateStringForNameIndex(_currentName, out _dataPosition);
                }
            }
        
            public Object Current {
                get {
                    return Entry;
                }
            }
 
            // Warning: This requires that you call the Key or Entry property FIRST before calling it!
            internal int DataPosition {
                get {
                    return _dataPosition;
                }
            }
 
            public DictionaryEntry Entry {
                [System.Security.SecuritySafeCritical]  // auto-generated
                get {
                    if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
                    if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
                    if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
 
                    String key;
                    Object value = null;
                    lock (_reader) { // locks should be taken in the same order as in RuntimeResourceSet.GetObject to avoid deadlock
                        lock (_reader._resCache) {
                            key = _reader.AllocateStringForNameIndex(_currentName, out _dataPosition); // AllocateStringForNameIndex could lock on _reader
                            ResourceLocator locator;
                            if (_reader._resCache.TryGetValue(key, out locator)) {
                                value = locator.Value;
                            }
                            if (value == null) {
                                if (_dataPosition == -1) 
                                    value = _reader.GetValueForNameIndex(_currentName);
                                else 
                                    value = _reader.LoadObject(_dataPosition);
                                // If enumeration and subsequent lookups happen very
                                // frequently in the same process, add a ResourceLocator
                                // to _resCache here.  But Microsoft enumerates and
                                // just about everyone else does lookups.  So caching
                                // here may bloat working set.
                            }
                        }
                    }
                    return new DictionaryEntry(key, value);
                }
            }
    
            public Object Value {
                get {
                    if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
                    if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
                    if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
 
                    // Consider using _resCache here, eventually, if
                    // this proves to be an interesting perf scenario.
                    // But mixing lookups and enumerators shouldn't be
                    // particularly compelling.
                    return _reader.GetValueForNameIndex(_currentName);
                }
            }
 
            public void Reset()
            {
                if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
                _currentIsValid = false;
                _currentName = ENUM_NOT_STARTED;
            }
        }
    }
}