We need you! We're working hard on the next version of Developer Fusion - Let us know what you think we should be up to!

The Labs \ Source Viewer \ SSCLI \ System.Resources \ ResourceEnumerator

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** Class:  ResourceReader
  18. **
  19. **
  20. ** Purpose: Default way to read streams of resources on
  21. ** demand.
  22. **
  23. **        Version 2 support on October 6, 2003
  24. **
  25. ===========================================================*/
  26. namespace System.Resources
  27. {
  28.     using System;
  29.     using System.IO;
  30.     using System.Text;
  31.     using System.Collections;
  32.     using System.Collections.Generic;
  33.     using System.Runtime.Serialization;
  34.     using System.Runtime.Serialization.Formatters;
  35.     using System.Runtime.Serialization.Formatters.Binary;
  36.     using System.Reflection;
  37.     using System.Security.Permissions;
  38.     using System.Security;
  39.     using System.Globalization;
  40.     using System.Configuration.Assemblies;
  41.     using System.Runtime.Versioning;
  42.    
  43.     // Provides the default implementation of IResourceReader, reading
  44.     // .resources file from the system default binary format. This class
  45.     // can be treated as an enumerator once.
  46.     //
  47.     // See the RuntimeResourceSet overview for details on the system
  48.     // default file format.
  49.     //
  50.    
  51.     internal struct ResourceLocator
  52.     {
  53.         internal object _value;
  54.         internal int _dataPos;
  55.        
  56.         internal ResourceLocator(int dataPos, object value)
  57.         {
  58.             _dataPos = dataPos;
  59.             _value = value;
  60.         }
  61.        
  62.         internal int DataPosition {
  63.             get { return _dataPos; }
  64.         }
  65.         //set { _dataPos = value; }
  66.        
  67.         // Allows adding in profiling data in a future version, or a special
  68.         // resource profiling build. We could also use WeakReference.
  69.         internal object Value {
  70.             get { return _value; }
  71.             set { _value = value; }
  72.         }
  73.        
  74.         static internal bool CanCache(ResourceTypeCode value)
  75.         {
  76.             BCLDebug.Assert(value >= 0, "negative ResourceTypeCode. What?");
  77.             return value <= ResourceTypeCode.LastPrimitive;
  78.         }
  79.     }
  80.    
  81.    
  82.     [System.Runtime.InteropServices.ComVisible(true)]
  83.     public sealed class ResourceReader : IResourceReader
  84.     {
  85.         private BinaryReader _store;
  86.         // backing store we're reading from.
  87.         // Used by RuntimeResourceSet and this class's enumerator. Maps
  88.         // resource name to a value, a ResourceLocator, or a
  89.         // LooselyLinkedManifestResource.
  90.         internal Dictionary<string, ResourceLocator> _resCache;
  91.         private long _nameSectionOffset;
  92.         // Offset to name section of file.
  93.         private long _dataSectionOffset;
  94.         // Offset to Data section of file.
  95.         // Note this class is tightly coupled with UnmanagedMemoryStream.
  96.         // At runtime when getting an embedded resource from an assembly,
  97.         // we're given an UnmanagedMemoryStream referring to the mmap'ed portion
  98.         // of the assembly. The pointers here are pointers into that block of
  99.         // memory controlled by the OS's loader.
  100.         private int[] _nameHashes;
  101.         // hash values for all names.
  102.         unsafe private int* _nameHashesPtr;
  103.         // In case we're using UnmanagedMemoryStream
  104.         private int[] _namePositions;
  105.         // relative locations of names
  106.         unsafe private int* _namePositionsPtr;
  107.         // If we're using UnmanagedMemoryStream
  108.         private Type[] _typeTable;
  109.         // Lazy array of Types for resource values.
  110.         private int[] _typeNamePositions;
  111.         // To delay initialize type table
  112.         private BinaryFormatter _objFormatter;
  113.         // Deserialization stuff.
  114.         private int _numResources;
  115.         // Num of resources files, in case arrays aren't allocated.
  116.         // We'll include a separate code path that uses UnmanagedMemoryStream to
  117.         // avoid allocating String objects and the like.
  118.         private UnmanagedMemoryStream _ums;
  119.        
  120.         // Version number of .resources file, for compatibility
  121.         private int _version;
  122.        
  123.         #if RESOURCE_FILE_FORMAT_DEBUG
  124.         private bool _debug;
  125.         // Whether this file has debugging stuff in it.
  126.         #endif
  127.        
  128.         [ResourceExposure(ResourceScope.Machine)]
  129.         [ResourceConsumption(ResourceScope.Machine)]
  130.         public ResourceReader(string fileName)
  131.         {
  132.             _resCache = new Dictionary<string, ResourceLocator>(FastResourceComparer.Default);
  133.             _store = new BinaryReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8);
  134.             BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(String). UnmanagedMemoryStream: " + (_ums != null));
  135.            
  136.             try {
  137.                 ReadResources();
  138.             }
  139.             catch {
  140.                 _store.Close();
  141.                 // If we threw an exception, close the file.
  142.                 throw;
  143.             }
  144.         }
  145.        
  146.         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
  147.         public ResourceReader(Stream stream)
  148.         {
  149.             if (stream == null)
  150.                 throw new ArgumentNullException("stream");
  151.             if (!stream.CanRead)
  152.                 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable"));
  153.            
  154.             _resCache = new Dictionary<string, ResourceLocator>(FastResourceComparer.Default);
  155.             _store = new BinaryReader(stream, Encoding.UTF8);
  156.             // We have a faster code path for reading resource files from an assembly.
  157.             _ums = stream as UnmanagedMemoryStream;
  158.            
  159.             BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(Stream). UnmanagedMemoryStream: " + (_ums != null));
  160.             ReadResources();
  161.         }
  162.        
  163.         // This is the constructor the RuntimeResourceSet calls,
  164.         // passing in the stream to read from and the RuntimeResourceSet's
  165.         // internal hash table (hash table of names with file offsets
  166.         // and values, coupled to this ResourceReader).
  167.         internal ResourceReader(Stream stream, Dictionary<string, ResourceLocator> resCache)
  168.         {
  169.             BCLDebug.Assert(stream != null, "Need a stream!");
  170.             BCLDebug.Assert(stream.CanRead, "Stream should be readable!");
  171.             BCLDebug.Assert(resCache != null, "Need a Dictionary!");
  172.            
  173.             _resCache = resCache;
  174.             _store = new BinaryReader(stream, Encoding.UTF8);
  175.            
  176.             _ums = stream as UnmanagedMemoryStream;
  177.            
  178.             BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(Stream, Hashtable). UnmanagedMemoryStream: " + (_ums != null));
  179.             ReadResources();
  180.         }
  181.        
  182.        
  183.         public void Close()
  184.         {
  185.             Dispose(true);
  186.         }
  187.        
  188.         void IDisposable.Dispose()
  189.         {
  190.             Dispose(true);
  191.         }
  192.        
  193.         unsafe private void Dispose(bool disposing)
  194.         {
  195.             if (_store != null) {
  196.                 _resCache = null;
  197.                 if (disposing) {
  198.                     // Close the stream in a thread-safe way. This fix means
  199.                     // that we may call Close n times, but that's safe.
  200.                     BinaryReader copyOfStore = _store;
  201.                     _store = null;
  202.                     if (copyOfStore != null)
  203.                         copyOfStore.Close();
  204.                 }
  205.                 _store = null;
  206.                 _namePositions = null;
  207.                 _nameHashes = null;
  208.                 _ums = null;
  209.                 _namePositionsPtr = null;
  210.                 _nameHashesPtr = null;
  211.             }
  212.         }
  213.        
  214.         unsafe static internal int ReadUnalignedI4(int* p)
  215.         {
  216.             byte* buffer = (byte*)p;
  217.             // Unaligned, little endian format
  218.             return buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
  219.         }
  220.        
  221.         private void SkipInt32()
  222.         {
  223.             _store.BaseStream.Seek(4, SeekOrigin.Current);
  224.         }
  225.        
  226.        
  227.         private void SkipString()
  228.         {
  229.             int stringLength = _store.Read7BitEncodedInt();
  230.             _store.BaseStream.Seek(stringLength, SeekOrigin.Current);
  231.         }
  232.        
  233.         unsafe private int GetNameHash(int index)
  234.         {
  235.            
  236.             BCLDebug.Assert(index >= 0 && index < _numResources, "Bad index into hash array. index: " + index);
  237.             BCLDebug.Assert((_ums == null && _nameHashes != null && _nameHashesPtr == null) || (_ums != null && _nameHashes == null && _nameHashesPtr != null), "Internal state mangled.");
  238.             if (_ums == null)
  239.                 return _nameHashes[index];
  240.             else
  241.                 return ReadUnalignedI4(&_nameHashesPtr[index]);
  242.         }
  243.        
  244.         unsafe private int GetNamePosition(int index)
  245.         {
  246.             BCLDebug.Assert(index >= 0 && index < _numResources, "Bad index into name position array. index: " + index);
  247.             BCLDebug.Assert((_ums == null && _namePositions != null && _namePositionsPtr == null) || (_ums != null && _namePositions == null && _namePositionsPtr != null), "Internal state mangled.");
  248.             int r;
  249.             if (_ums == null)
  250.                 r = _namePositions[index];
  251.             else
  252.                 r = ReadUnalignedI4(&_namePositionsPtr[index]);
  253.             if (r < 0 || r > _dataSectionOffset - _nameSectionOffset) {
  254.                 BCLDebug.Assert(false, "Corrupt .resources file! NamePosition is outside of the name section!");
  255.                 throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameOutOfSection", index, r.ToString("x", CultureInfo.InvariantCulture)));
  256.             }
  257.             return r;
  258.         }
  259.        
  260.         IEnumerator IEnumerable.GetEnumerator()
  261.         {
  262.             return GetEnumerator();
  263.         }
  264.        
  265.         public IDictionaryEnumerator GetEnumerator()
  266.         {
  267.             if (_resCache == null)
  268.                 throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
  269.             return new ResourceEnumerator(this);
  270.         }
  271.        
  272.         internal ResourceEnumerator GetEnumeratorInternal()
  273.         {
  274.             return new ResourceEnumerator(this);
  275.         }
  276.        
  277.         // From a name, finds the associated virtual offset for the data.
  278.         // To read the data, seek to _dataSectionOffset + dataPos, then
  279.         // read the resource type & data.
  280.         // This does a binary search through the names.
  281.         internal int FindPosForResource(string name)
  282.         {
  283.             BCLDebug.Assert(_store != null, "ResourceReader is closed!");
  284.             int hash = FastResourceComparer.HashFunction(name);
  285.             BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for " + name + " hash: " + hash.ToString("x", CultureInfo.InvariantCulture));
  286.             // Binary search over the hashes. Use the _namePositions array to
  287.             // determine where they exist in the underlying stream.
  288.             int lo = 0;
  289.             int hi = _numResources - 1;
  290.             int index = -1;
  291.             bool success = false;
  292.             while (lo <= hi) {
  293.                 index = (lo + hi) >> 1;
  294.                 // Do NOT use subtraction here, since it will wrap for large
  295.                 // negative numbers.
  296.                 int currentHash = GetNameHash(index);
  297.                 int c;
  298.                 if (currentHash == hash)
  299.                     c = 0;
  300.                 else if (currentHash < hash)
  301.                     c = -1;
  302.                 else
  303.                     c = 1;
  304.                 //BCLDebug.Log("RESMGRFILEFORMAT", " Probing index "+index+" lo: "+lo+" hi: "+hi+" c: "+c);
  305.                 if (c == 0) {
  306.                     success = true;
  307.                     break;
  308.                 }
  309.                 if (c < 0)
  310.                     lo = index + 1;
  311.                 else
  312.                     hi = index - 1;
  313.             }
  314.             if (!success) {
  315.                 #if RESOURCE_FILE_FORMAT_DEBUG
  316.                 string lastReadString;
  317.                 lock (this) {
  318.                     _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(index), SeekOrigin.Begin);
  319.                     lastReadString = _store.ReadString();
  320.                 }
  321.                 BCLDebug.Log("RESMGRFILEFORMAT", LogLevel.Status, "FindPosForResource for ", name, " failed. i: ", index, " lo: ", lo, " hi: ", hi,
  322.                 " last read string: \"", lastReadString, '\'');
  323.                 #endif
  324.                 return -1;
  325.             }
  326.            
  327.             // index is the location in our hash array that corresponds with a
  328.             // value in the namePositions array.
  329.             // There could be collisions in our hash function. Check on both sides
  330.             // of index to find the range of hash values that are equal to the
  331.             // target hash value.
  332.             if (lo != index) {
  333.                 lo = index;
  334.                 while (lo > 0 && GetNameHash(lo - 1) == hash)
  335.                     lo--;
  336.             }
  337.             if (hi != index) {
  338.                 hi = index;
  339.                 while (hi < _numResources && GetNameHash(hi + 1) == hash)
  340.                     hi++;
  341.             }
  342.            
  343.             lock (this) {
  344.                 for (int i = lo; i <= hi; i++) {
  345.                     _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(i), SeekOrigin.Begin);
  346.                     if (CompareStringEqualsName(name)) {
  347.                         int dataPos = _store.ReadInt32();
  348.                         BCLDebug.Assert(dataPos >= 0 || dataPos < _store.BaseStream.Length - _dataSectionOffset, "Data section relative offset is out of the bounds of the data section! dataPos: " + dataPos);
  349.                         return dataPos;
  350.                     }
  351.                 }
  352.             }
  353.             BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for " + name + ": Found a hash collision, HOWEVER, neither of these collided values equaled the given string.");
  354.             return -1;
  355.         }
  356.        
  357.         // This compares the String in the .resources file at the current position
  358.         // with the string you pass in.
  359.         // Whoever calls this method should make sure that they take a lock
  360.         // so no one else can cause us to seek in the stream.
  361.         unsafe private bool CompareStringEqualsName(string name)
  362.         {
  363.             BCLDebug.Assert(_store != null, "ResourceReader is closed!");
  364.             //int byteLen = Read7BitEncodedInt(_store);
  365.             int byteLen = _store.Read7BitEncodedInt();
  366.             if (_ums != null) {
  367.                 //BCLDebug.Log("RESMGRFILEFORMAT", "CompareStringEqualsName using UnmanagedMemoryStream code path");
  368.                 byte* bytes = _ums.PositionPointer;
  369.                 // Skip over the data in the Stream, positioning ourselves right after it.
  370.                 _ums.Seek(byteLen, SeekOrigin.Current);
  371.                
  372.                 if (_ums.Position > _ums.Length)
  373.                     throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameTooLong"));
  374.                
  375.                 // On 64-bit machines, these char*'s may be misaligned. Use a
  376.                 // byte-by-byte comparison instead.
  377.                 //return FastResourceComparer.CompareOrdinal((char*)bytes, byteLen/2, name) == 0;
  378.                 return FastResourceComparer.CompareOrdinal(bytes, byteLen, name) == 0;
  379.             }
  380.             else {
  381.                 // This code needs to be fast
  382.                 byte[] bytes = new byte[byteLen];
  383.                 int numBytesToRead = byteLen;
  384.                 while (numBytesToRead > 0) {
  385.                     int n = _store.Read(bytes, byteLen - numBytesToRead, numBytesToRead);
  386.                     if (n == 0)
  387.                         throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted"));
  388.                     numBytesToRead -= n;
  389.                 }
  390.                 return FastResourceComparer.CompareOrdinal(bytes, byteLen / 2, name) == 0;
  391.             }
  392.         }
  393.        
  394.         // This is used in the enumerator. The enumerator iterates from 0 to n
  395.         // of our resources and this returns the resource name for a particular
  396.         // index. The parameter is NOT a virtual offset.
  397.         unsafe private string AllocateStringForNameIndex(int index, out int dataOffset)
  398.         {
  399.             BCLDebug.Assert(_store != null, "ResourceReader is closed!");
  400.             byte[] bytes;
  401.             int byteLen;
  402.             long nameVA = GetNamePosition(index);
  403.             lock (this) {
  404.                 _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
  405.                 // Can't use _store.ReadString, since it's using UTF-8!
  406.                 byteLen = _store.Read7BitEncodedInt();
  407.                
  408.                 if (_ums != null) {
  409.                     if (_ums.Position > _ums.Length - byteLen)
  410.                         throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesIndexTooLong", index));
  411.                    
  412.                     char* charPtr = (char*)_ums.PositionPointer;
  413.                     string s = new string(charPtr, 0, byteLen / 2);
  414.                     _ums.Position += byteLen;
  415.                     dataOffset = _store.ReadInt32();
  416.                     return s;
  417.                 }
  418.                
  419.                 bytes = new byte[byteLen];
  420.                 // We must read byteLen bytes, or we have a corrupted file.
  421.                 // Use a blocking read in case the stream doesn't give us back
  422.                 // everything immediately.
  423.                 int count = byteLen;
  424.                 while (count > 0) {
  425.                     int n = _store.Read(bytes, byteLen - count, count);
  426.                     if (n == 0)
  427.                         throw new EndOfStreamException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted_NameIndex", index));
  428.                     count -= n;
  429.                 }
  430.                 dataOffset = _store.ReadInt32();
  431.             }
  432.             return Encoding.Unicode.GetString(bytes, 0, byteLen);
  433.         }
  434.        
  435.         // This is used in the enumerator. The enumerator iterates from 0 to n
  436.         // of our resources and this returns the resource value for a particular
  437.         // index. The parameter is NOT a virtual offset.
  438.         private object GetValueForNameIndex(int index)
  439.         {
  440.             BCLDebug.Assert(_store != null, "ResourceReader is closed!");
  441.             long nameVA = GetNamePosition(index);
  442.             lock (this) {
  443.                 _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
  444.                 SkipString();
  445.                 //BCLDebug.Log("RESMGRFILEFORMAT", "GetValueForNameIndex for index: "+index+" skip (name length): "+skip);
  446.                 int dataPos = _store.ReadInt32();
  447.                 BCLDebug.Log("RESMGRFILEFORMAT", "GetValueForNameIndex: dataPos: " + dataPos);
  448.                 ResourceTypeCode junk;
  449.                 if (_version == 1)
  450.                     return LoadObjectV1(dataPos);
  451.                 else
  452.                     return LoadObjectV2(dataPos, out junk);
  453.             }
  454.         }
  455.        
  456.         // This takes a virtual offset into the data section and reads a String
  457.         // from that location.
  458.         // Anyone who calls LoadObject should make sure they take a lock so
  459.         // no one can cause us to do a seek in here.
  460.         internal string LoadString(int pos)
  461.         {
  462.             BCLDebug.Assert(_store != null, "ResourceReader is closed!");
  463.             _store.BaseStream.Seek(_dataSectionOffset + pos, SeekOrigin.Begin);
  464.             string s = null;
  465.             int typeIndex = _store.Read7BitEncodedInt();
  466.             if (_version == 1) {
  467.                 if (typeIndex == -1)
  468.                     return null;
  469.                 if (FindType(typeIndex) != typeof(string))
  470.                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Type", FindType(typeIndex).GetType().FullName));
  471.                 s = _store.ReadString();
  472.             }
  473.             else {
  474.                 ResourceTypeCode typeCode = (ResourceTypeCode)typeIndex;
  475.                 if (typeCode != ResourceTypeCode.String && typeCode != ResourceTypeCode.Null) {
  476.                     string typeString;
  477.                     if (typeCode < ResourceTypeCode.StartOfUserTypes)
  478.                         typeString = typeCode.ToString();
  479.                     else
  480.                         typeString = FindType(typeCode - ResourceTypeCode.StartOfUserTypes).FullName;
  481.                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Type", typeString));
  482.                 }
  483.                 if (typeCode == ResourceTypeCode.String)
  484.                     // ignore Null
  485.                     s = _store.ReadString();
  486.             }
  487.             BCLDebug.Log("RESMGRFILEFORMAT", "LoadString(" + pos.ToString("x", CultureInfo.InvariantCulture) + " returned " + (s == null ? "[a null string]" : s));
  488.             return s;
  489.         }
  490.        
  491.         // Called from RuntimeResourceSet
  492.         internal object LoadObject(int pos)
  493.         {
  494.             if (_version == 1)
  495.                 return LoadObjectV1(pos);
  496.             ResourceTypeCode typeCode;
  497.             return LoadObjectV2(pos, out typeCode);
  498.         }
  499.        
  500.         internal object LoadObject(int pos, out ResourceTypeCode typeCode)
  501.         {
  502.             if (_version == 1) {
  503.                 object o = LoadObjectV1(pos);
  504.                 typeCode = (o is string) ? ResourceTypeCode.String : ResourceTypeCode.StartOfUserTypes;
  505.                 return o;
  506.             }
  507.             return LoadObjectV2(pos, out typeCode);
  508.         }
  509.        
  510.        
  511.         // This takes a virtual offset into the data section and reads an Object
  512.         // from that location.
  513.         // Anyone who calls LoadObject should make sure they take a lock so
  514.         // no one can cause us to do a seek in here.
  515.         internal object LoadObjectV1(int pos)
  516.         {
  517.             BCLDebug.Assert(_store != null, "ResourceReader is closed!");
  518.             BCLDebug.Assert(_version == 1, ".resources file was not a V1 .resources file!");
  519.             _store.BaseStream.Seek(_dataSectionOffset + pos, SeekOrigin.Begin);
  520.             int typeIndex = _store.Read7BitEncodedInt();
  521.             if (typeIndex == -1)
  522.                 return null;
  523.             Type type = FindType(typeIndex);
  524.             BCLDebug.Log("RESMGRFILEFORMAT", "LoadObject type: " + type.Name + " pos: 0x" + _store.BaseStream.Position.ToString("x", CultureInfo.InvariantCulture));
  525.             if (type == typeof(string))
  526.                 return _store.ReadString();
  527.             else if (type == typeof(Int32))
  528.                 return _store.ReadInt32();
  529.             else if (type == typeof(byte))
  530.                 return _store.ReadByte();
  531.             else if (type == typeof(sbyte))
  532.                 return _store.ReadSByte();
  533.             else if (type == typeof(Int16))
  534.                 return _store.ReadInt16();
  535.             else if (type == typeof(Int64))
  536.                 return _store.ReadInt64();
  537.             else if (type == typeof(UInt16))
  538.                 return _store.ReadUInt16();
  539.             else if (type == typeof(UInt32))
  540.                 return _store.ReadUInt32();
  541.             else if (type == typeof(UInt64))
  542.                 return _store.ReadUInt64();
  543.             else if (type == typeof(float))
  544.                 return _store.ReadSingle();
  545.             else if (type == typeof(double))
  546.                 return _store.ReadDouble();
  547.             else if (type == typeof(DateTime)) {
  548.                 // Ideally we should use DateTime's ToBinary & FromBinary,
  549.                 // but we can't for compatibility reasons.
  550.                 return new DateTime(_store.ReadInt64());
  551.             }
  552.             else if (type == typeof(TimeSpan))
  553.                 return new TimeSpan(_store.ReadInt64());
  554.             else if (type == typeof(decimal)) {
  555.                 int[] bits = new int[4];
  556.                 for (int i = 0; i < bits.Length; i++)
  557.                     bits[i] = _store.ReadInt32();
  558.                 return new decimal(bits);
  559.             }
  560.             else {
  561.                 return DeserializeObject(typeIndex);
  562.             }
  563.         }
  564.        
  565.         internal object LoadObjectV2(int pos, out ResourceTypeCode typeCode)
  566.         {
  567.             BCLDebug.Assert(_store != null, "ResourceReader is closed!");
  568.             BCLDebug.Assert(_version >= 2, ".resources file was not a V2 (or higher) .resources file!");
  569.             _store.BaseStream.Seek(_dataSectionOffset + pos, SeekOrigin.Begin);
  570.             typeCode = (ResourceTypeCode)_store.Read7BitEncodedInt();
  571.            
  572.             BCLDebug.Log("RESMGRFILEFORMAT", "LoadObjectV2 type: " + typeCode + " pos: 0x" + _store.BaseStream.Position.ToString("x", CultureInfo.InvariantCulture));
  573.            
  574.             switch (typeCode) {
  575.                 case ResourceTypeCode.Null:
  576.                     return null;
  577.                 case ResourceTypeCode.String:
  578.                    
  579.                     return _store.ReadString();
  580.                 case ResourceTypeCode.Boolean:
  581.                    
  582.                     return _store.ReadBoolean();
  583.                 case ResourceTypeCode.Char:
  584.                    
  585.                     return (char)_store.ReadUInt16();
  586.                 case ResourceTypeCode.Byte:
  587.                    
  588.                     return _store.ReadByte();
  589.                 case ResourceTypeCode.SByte:
  590.                    
  591.                     return _store.ReadSByte();
  592.                 case ResourceTypeCode.Int16:
  593.                    
  594.                     return _store.ReadInt16();
  595.                 case ResourceTypeCode.UInt16:
  596.                    
  597.                     return _store.ReadUInt16();
  598.                 case ResourceTypeCode.Int32:
  599.                    
  600.                     return _store.ReadInt32();
  601.                 case ResourceTypeCode.UInt32:
  602.                    
  603.                     return _store.ReadUInt32();
  604.                 case ResourceTypeCode.Int64:
  605.                    
  606.                     return _store.ReadInt64();
  607.                 case ResourceTypeCode.UInt64:
  608.                    
  609.                     return _store.ReadUInt64();
  610.                 case ResourceTypeCode.Single:
  611.                    
  612.                     return _store.ReadSingle();
  613.                 case ResourceTypeCode.Double:
  614.                    
  615.                     return _store.ReadDouble();
  616.                 case ResourceTypeCode.Decimal:
  617.                    
  618.                     return _store.ReadDecimal();
  619.                 case ResourceTypeCode.DateTime:
  620.                    
  621.                     // Use DateTime's ToBinary & FromBinary.
  622.                     Int64 data = _store.ReadInt64();
  623.                     return DateTime.FromBinary(data);
  624.                 case ResourceTypeCode.TimeSpan:
  625.                    
  626.                     Int64 ticks = _store.ReadInt64();
  627.                     return new TimeSpan(ticks);
  628.                 case ResourceTypeCode.ByteArray:
  629.                    
  630.                     // Special types
  631.                    
  632.                     {
  633.                         int len = _store.ReadInt32();
  634.                         if (_ums == null)
  635.                             return _store.ReadBytes(len);
  636.                        
  637.                         if (len > _ums.Length - _ums.Position)
  638.                             throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataTooLong"));
  639.                        
  640.                         byte[] bytes = new byte[len];
  641.                         int r = _ums.Read(bytes, 0, len);
  642.                         BCLDebug.Assert(r == len, "ResourceReader needs to use a blocking read here. (Call _store.ReadBytes(len)?)");
  643.                         return bytes;
  644.                     }
  645.                     break;
  646.                 case ResourceTypeCode.Stream:
  647.                    
  648.                    
  649.                     {
  650.                         int len = _store.ReadInt32();
  651.                         if (_ums == null) {
  652.                             byte[] bytes = _store.ReadBytes(len);
  653.                             // Lifetime of memory == lifetime of this stream.
  654.                             return new PinnedBufferMemoryStream(bytes);
  655.                         }
  656.                        
  657.                         // make sure we don't create an UnmanagedMemoryStream that is longer than the resource stream.
  658.                         if (len > _ums.Length - _ums.Position)
  659.                             throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataTooLong"));
  660.                        
  661.                         // For the case that we've memory mapped in the .resources
  662.                         // file, just return a Stream pointing to that block of memory.
  663.                         unsafe {
  664.                             return new UnmanagedMemoryStream(_ums.PositionPointer, len, len, FileAccess.Read, true);
  665.                         }
  666.                     }
  667.                     break;
  668.                 default:
  669.                    
  670.                     BCLDebug.Assert(typeCode >= ResourceTypeCode.StartOfUserTypes, String.Format(CultureInfo.InvariantCulture, "ResourceReader: Unsupported ResourceTypeCode in .resources file! {0}", typeCode));
  671.                     // Throw new exception?
  672.                     break;
  673.             }
  674.            
  675.             // Normal serialized objects
  676.             int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
  677.             return DeserializeObject(typeIndex);
  678.         }
  679.        
  680.         // Helper method to safely deserialize a type, using a type-limiting
  681.         // deserialization binder to simulate a type-limiting deserializer.
  682.         // This method handles types that are safe to deserialize, as well as
  683.         // ensuring we only get back what we expect.
  684.         private object DeserializeObject(int typeIndex)
  685.         {
  686.             Type type = FindType(typeIndex);
  687.            
  688.             // Ensure that the object we deserialized is exactly the same
  689.             // type of object we thought we should be deserializing. This
  690.             // will help prevent hacked .resources files from using our
  691.             // serialization permission assert to deserialize anything
  692.             // via a hacked type ID.
  693.            
  694.             object graph;
  695.             graph = _objFormatter.Deserialize(_store.BaseStream);
  696.            
  697.             // This check is about correctness, not security at this point.
  698.             if (graph.GetType() != type)
  699.                 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResType&SerBlobMismatch", type.FullName, graph.GetType().FullName));
  700.            
  701.             return graph;
  702.         }
  703.        
  704.         // Reads in the header information for a .resources file. Verifies some
  705.         // of the assumptions about this resource set, and builds the class table
  706.         // for the default resource file format.
  707.         private void ReadResources()
  708.         {
  709.             BCLDebug.Assert(_store != null, "ResourceReader is closed!");
  710.             BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File | StreamingContextStates.Persistence));
  711.             _objFormatter = bf;
  712.            
  713.             try {
  714.                 // Read ResourceManager header
  715.                 // Check for magic number
  716.                 int magicNum = _store.ReadInt32();
  717.                 if (magicNum != ResourceManager.MagicNumber)
  718.                     throw new ArgumentException(Environment.GetResourceString("Resources_StreamNotValid"));
  719.                 // Assuming this is ResourceManager header V1 or greater, hopefully
  720.                 // after the version number there is a number of bytes to skip
  721.                 // to bypass the rest of the ResMgr header.
  722.                 int resMgrHeaderVersion = _store.ReadInt32();
  723.                 if (resMgrHeaderVersion > 1) {
  724.                     int numBytesToSkip = _store.ReadInt32();
  725.                     BCLDebug.Log("RESMGRFILEFORMAT", LogLevel.Status, "ReadResources: Unexpected ResMgr header version: {0} Skipping ahead {1} bytes.", resMgrHeaderVersion, numBytesToSkip);
  726.                     BCLDebug.Assert(numBytesToSkip >= 0, "numBytesToSkip in ResMgr header should be positive!");
  727.                     _store.BaseStream.Seek(numBytesToSkip, SeekOrigin.Current);
  728.                 }
  729.                 else {
  730.                     BCLDebug.Log("RESMGRFILEFORMAT", "ReadResources: Parsing ResMgr header v1.");
  731.                     SkipInt32();
  732.                     // We don't care about numBytesToSkip.
  733.                     // Read in type name for a suitable ResourceReader
  734.                     // Note ResourceWriter & InternalResGen use different Strings.
  735.                     string readerType = _store.ReadString();
  736.                     AssemblyName mscorlib = new AssemblyName(ResourceManager.MscorlibName);
  737.                    
  738.                     if (!ResourceManager.CompareNames(readerType, ResourceManager.ResReaderTypeName, mscorlib))
  739.                         throw new NotSupportedException(Environment.GetResourceString("NotSupported_WrongResourceReader_Type", readerType));
  740.                    
  741.                     // Skip over type name for a suitable ResourceSet
  742.                     SkipString();
  743.                 }
  744.                
  745.                 // Read RuntimeResourceSet header
  746.                 // Do file version check
  747.                 int version = _store.ReadInt32();
  748.                 if (version != RuntimeResourceSet.Version && version != 1)
  749.                     throw new ArgumentException(Environment.GetResourceString("Arg_ResourceFileUnsupportedVersion", RuntimeResourceSet.Version, version));
  750.                 _version = version;
  751.                
  752.                 #if RESOURCE_FILE_FORMAT_DEBUG
  753.                 // Look for ***DEBUG*** to see if this is a debuggable file.
  754.                 long oldPos = _store.BaseStream.Position;
  755.                 _debug = false;
  756.                 try {
  757.                     string debugString = _store.ReadString();
  758.                     _debug = String.Equals("***DEBUG***", debugString);
  759.                 }
  760.                 catch (IOException) {
  761.                 }
  762.                 catch (OutOfMemoryException) {
  763.                 }
  764.                 if (_debug) {
  765.                     Console.WriteLine("ResourceReader is looking at a debuggable .resources file, version {0}", _version);
  766.                 }
  767.                 else {
  768.                     _store.BaseStream.Position = oldPos;
  769.                 }
  770.                 #endif
  771.                
  772.                 _numResources = _store.ReadInt32();
  773.                 BCLDebug.Log("RESMGRFILEFORMAT", "ReadResources: Expecting " + _numResources + " resources.");
  774.                 #if _DEBUG
  775.                 if (ResourceManager.DEBUG >= 4)
  776.                     Console.WriteLine("ResourceReader::ReadResources - Reading in " + _numResources + " resources");
  777.                 #endif
  778.                
  779.                 // Read type positions into type positions array.
  780.                 // But delay initialize the type table.
  781.                 int numTypes = _store.ReadInt32();
  782.                 _typeTable = new Type[numTypes];
  783.                 _typeNamePositions = new int[numTypes];
  784.                 for (int i = 0; i < numTypes; i++) {
  785.                     _typeNamePositions[i] = (int)_store.BaseStream.Position;
  786.                    
  787.                     // Skip over the Strings in the file. Don't create types.
  788.                     SkipString();
  789.                 }
  790.                
  791.                 #if _DEBUG
  792.                 if (ResourceManager.DEBUG >= 5)
  793.                     Console.WriteLine("ResourceReader::ReadResources - Reading in " + numTypes + " type table entries");
  794.                 #endif
  795.                
  796.                 // Prepare to read in the array of name hashes
  797.                 // Note that the name hashes array is aligned to 8 bytes so
  798.                 // we can use pointers into it on 64 bit machines. (4 bytes
  799.                 // may be sufficient, but let's plan for the future)
  800.                 // Skip over alignment stuff. All public .resources files
  801.                 // should be aligned No need to verify the byte values.
  802.                 long pos = _store.BaseStream.Position;
  803.                 int alignBytes = ((int)pos) & 7;
  804.                 if (alignBytes != 0) {
  805.                     for (int i = 0; i < 8 - alignBytes; i++) {
  806.                         _store.ReadByte();
  807.                     }
  808.                 }
  809.                
  810.                 // Read in the array of name hashes
  811.                 #if RESOURCE_FILE_FORMAT_DEBUG
  812.                 // Skip over "HASHES->"
  813.                 if (_debug) {
  814.                     _store.BaseStream.Position += 8;
  815.                 }
  816.                 #endif
  817.                
  818.                 if (_ums == null) {
  819.                     _nameHashes = new int[_numResources];
  820.                     for (int i = 0; i < _numResources; i++)
  821.                         _nameHashes[i] = _store.ReadInt32();
  822.                 }
  823.                 else {
  824.                     unsafe {
  825.                         _nameHashesPtr = (int*)_ums.PositionPointer;
  826.                         // Skip over the array of nameHashes.
  827.                         _ums.Seek(4 * _numResources, SeekOrigin.Current);
  828.                         // get the position pointer once more to check that the whole table is within the stream
  829.                         byte* junk = _ums.PositionPointer;
  830.                     }
  831.                 }
  832.                
  833.                 // Read in the array of relative positions for all the names.
  834.                 #if RESOURCE_FILE_FORMAT_