public void Generate() { if (this._resourceList == null) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved")); } BinaryWriter writer = new BinaryWriter(this._output, Encoding.UTF8); List <string> types = new List <string>(); writer.Write(ResourceManager.MagicNumber); writer.Write(ResourceManager.HeaderVersionNumber); MemoryStream output = new MemoryStream(240); BinaryWriter writer2 = new BinaryWriter(output); writer2.Write(MultitargetingHelpers.GetAssemblyQualifiedName(typeof(ResourceReader), this.typeConverter)); writer2.Write(ResourceManager.ResSetTypeName); writer2.Flush(); writer.Write((int)output.Length); writer.Write(output.GetBuffer(), 0, (int)output.Length); writer.Write(2); int count = this._resourceList.Count; if (this._preserializedData != null) { count += this._preserializedData.Count; } writer.Write(count); int[] keys = new int[count]; int[] items = new int[count]; int index = 0; MemoryStream stream2 = new MemoryStream(count * 40); BinaryWriter writer3 = new BinaryWriter(stream2, Encoding.Unicode); Stream stream3 = null; string path = null; PermissionSet set = new PermissionSet(PermissionState.None); set.AddPermission(new EnvironmentPermission(PermissionState.Unrestricted)); set.AddPermission(new FileIOPermission(PermissionState.Unrestricted)); try { set.Assert(); path = Path.GetTempFileName(); File.SetAttributes(path, FileAttributes.NotContentIndexed | FileAttributes.Temporary); stream3 = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 0x1000, FileOptions.SequentialScan | FileOptions.DeleteOnClose); } catch (UnauthorizedAccessException) { stream3 = new MemoryStream(); } catch (IOException) { stream3 = new MemoryStream(); } finally { PermissionSet.RevertAssert(); } using (stream3) { BinaryWriter store = new BinaryWriter(stream3, Encoding.UTF8); IFormatter objFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Persistence | StreamingContextStates.File)); SortedList list2 = new SortedList(this._resourceList, FastResourceComparer.Default); if (this._preserializedData != null) { foreach (KeyValuePair <string, PrecannedResource> pair in this._preserializedData) { list2.Add(pair.Key, pair.Value); } } IDictionaryEnumerator enumerator = list2.GetEnumerator(); while (enumerator.MoveNext()) { keys[index] = FastResourceComparer.HashFunction((string)enumerator.Key); items[index++] = (int)writer3.Seek(0, SeekOrigin.Current); writer3.Write((string)enumerator.Key); writer3.Write((int)store.Seek(0, SeekOrigin.Current)); object obj2 = enumerator.Value; ResourceTypeCode typeCode = this.FindTypeCode(obj2, types); Write7BitEncodedInt(store, (int)typeCode); PrecannedResource resource = obj2 as PrecannedResource; if (resource != null) { store.Write(resource.Data); } else { this.WriteValue(typeCode, obj2, store, objFormatter); } } writer.Write(types.Count); for (int i = 0; i < types.Count; i++) { writer.Write(types[i]); } Array.Sort <int, int>(keys, items); writer.Flush(); int num4 = ((int)writer.BaseStream.Position) & 7; if (num4 > 0) { for (int j = 0; j < (8 - num4); j++) { writer.Write("PAD"[j % 3]); } } foreach (int num6 in keys) { writer.Write(num6); } foreach (int num7 in items) { writer.Write(num7); } writer.Flush(); writer3.Flush(); store.Flush(); int num8 = (int)(writer.Seek(0, SeekOrigin.Current) + stream2.Length); num8 += 4; writer.Write(num8); writer.Write(stream2.GetBuffer(), 0, (int)stream2.Length); writer3.Close(); stream3.Position = 0L; stream3.CopyTo(writer.BaseStream); store.Close(); } writer.Flush(); this._resourceList = null; }
[SecuritySafeCritical] // Asserts permission to create & delete a temp file. public void Generate() { if (_resourceList == null) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved")); } BinaryWriter bw = new BinaryWriter(_output, Encoding.UTF8); List <String> typeNames = new List <String>(); // Write out the ResourceManager header // Write out magic number bw.Write(ResourceManager.MagicNumber); // Write out ResourceManager header version number bw.Write(ResourceManager.HeaderVersionNumber); MemoryStream resMgrHeaderBlob = new MemoryStream(240); BinaryWriter resMgrHeaderPart = new BinaryWriter(resMgrHeaderBlob); // Write out class name of IResourceReader capable of handling // this file. resMgrHeaderPart.Write(MultitargetingHelpers.GetAssemblyQualifiedName(typeof(ResourceReader), typeConverter)); // Write out class name of the ResourceSet class best suited to // handling this file. // This needs to be the same even with multi-targeting. It's the // full name -- not the ----sembly qualified name. resMgrHeaderPart.Write(ResourceManager.ResSetTypeName); resMgrHeaderPart.Flush(); // Write number of bytes to skip over to get past ResMgr header bw.Write((int)resMgrHeaderBlob.Length); // Write the rest of the ResMgr header bw.Write(resMgrHeaderBlob.GetBuffer(), 0, (int)resMgrHeaderBlob.Length); // End ResourceManager header // Write out the RuntimeResourceSet header // Version number bw.Write(RuntimeResourceSet.Version); #if RESOURCE_FILE_FORMAT_DEBUG // Write out a tag so we know whether to enable or disable // debugging support when reading the file. bw.Write("***DEBUG***"); #endif // number of resources int numResources = _resourceList.Count; if (_preserializedData != null) { numResources += _preserializedData.Count; } bw.Write(numResources); // Store values in temporary streams to write at end of file. int[] nameHashes = new int[numResources]; int[] namePositions = new int[numResources]; int curNameNumber = 0; MemoryStream nameSection = new MemoryStream(numResources * AverageNameSize); BinaryWriter names = new BinaryWriter(nameSection, Encoding.Unicode); // The data section can be very large, and worse yet, we can grow the byte[] used // for the data section repeatedly. When using large resources like ~100 images, // this can lead to both a fragmented large object heap as well as allocating about // 2-3x of our storage needs in extra overhead. Using a temp file can avoid this. // Assert permission to get a temp file name, which requires two permissions. // Additionally, we may be running under an account that doesn't have permission to // write to the temp directory (enforced via a Windows ACL). Fall back to a MemoryStream. Stream dataSection = null; // Either a FileStream or a MemoryStream String tempFile = null; #if MONO_FEATURE_CAS PermissionSet permSet = new PermissionSet(PermissionState.None); permSet.AddPermission(new EnvironmentPermission(PermissionState.Unrestricted)); permSet.AddPermission(new FileIOPermission(PermissionState.Unrestricted)); #endif try { #if MONO_FEATURE_CAS permSet.Assert(); #endif tempFile = Path.GetTempFileName(); File.SetAttributes(tempFile, FileAttributes.Temporary | FileAttributes.NotContentIndexed); // Explicitly opening with FileOptions.DeleteOnClose to avoid complicated File.Delete // (safe from ----s w/ antivirus software, etc) dataSection = new FileStream(tempFile, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose | FileOptions.SequentialScan); } catch (UnauthorizedAccessException) { // In case we're running under an account that can't access a temp directory. dataSection = new MemoryStream(); } catch (IOException) { // In case Path.GetTempFileName fails because no unique file names are available dataSection = new MemoryStream(); } finally { #if MONO_FEATURE_CAS PermissionSet.RevertAssert(); #endif } using (dataSection) { BinaryWriter data = new BinaryWriter(dataSection, Encoding.UTF8); #if FEATURE_SERIALIZATION IFormatter objFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File | StreamingContextStates.Persistence)); #endif // FEATURE_SERIALIZATION #if RESOURCE_FILE_FORMAT_DEBUG // Write NAMES right before the names section. names.Write(new byte[] { (byte)'N', (byte)'A', (byte)'M', (byte)'E', (byte)'S', (byte)'-', (byte)'-', (byte)'>' }); // Write DATA at the end of the name table section. data.Write(new byte[] { (byte)'D', (byte)'A', (byte)'T', (byte)'A', (byte)'-', (byte)'-', (byte)'-', (byte)'>' }); #endif // We've stored our resources internally in a Hashtable, which // makes no guarantees about the ordering while enumerating. // While we do our own sorting of the resource names based on their // hash values, that's only sorting the nameHashes and namePositions // arrays. That's all that is strictly required for correctness, // but for ease of generating a patch in the future that // modifies just .resources files, we should re-sort them. SortedList sortedResources = new SortedList(_resourceList, FastResourceComparer.Default); if (_preserializedData != null) { foreach (KeyValuePair <String, PrecannedResource> entry in _preserializedData) { sortedResources.Add(entry.Key, entry.Value); } } IDictionaryEnumerator items = sortedResources.GetEnumerator(); // Write resource name and position to the file, and the value // to our temporary buffer. Save Type as well. while (items.MoveNext()) { nameHashes[curNameNumber] = FastResourceComparer.HashFunction((String)items.Key); namePositions[curNameNumber++] = (int)names.Seek(0, SeekOrigin.Current); names.Write((String)items.Key); // key names.Write((int)data.Seek(0, SeekOrigin.Current)); // virtual offset of value. #if RESOURCE_FILE_FORMAT_DEBUG names.Write((byte)'*'); #endif Object value = items.Value; ResourceTypeCode typeCode = FindTypeCode(value, typeNames); // Write out type code Write7BitEncodedInt(data, (int)typeCode); // Write out value PrecannedResource userProvidedResource = value as PrecannedResource; if (userProvidedResource != null) { data.Write(userProvidedResource.Data); } else { #if FEATURE_SERIALIZATION WriteValue(typeCode, value, data, objFormatter); #else WriteValue(typeCode, value, data); #endif } #if RESOURCE_FILE_FORMAT_DEBUG data.Write(new byte[] { (byte)'S', (byte)'T', (byte)'O', (byte)'P' }); #endif } // At this point, the ResourceManager header has been written. // Finish RuntimeResourceSet header // Write size & contents of class table bw.Write(typeNames.Count); for (int i = 0; i < typeNames.Count; i++) { bw.Write(typeNames[i]); } // Write out the name-related items for lookup. // Note that the hash array and the namePositions array must // be sorted in parallel. Array.Sort(nameHashes, namePositions); // Prepare to write sorted name hashes (alignment fixup) // Note: For 64-bit machines, these MUST be aligned on 8 byte // boundaries! Pointers on IA64 must be aligned! And we'll // run faster on X86 machines too. bw.Flush(); int alignBytes = ((int)bw.BaseStream.Position) & 7; if (alignBytes > 0) { for (int i = 0; i < 8 - alignBytes; i++) { bw.Write("PAD"[i % 3]); } } // Write out sorted name hashes. // Align to 8 bytes. Contract.Assert((bw.BaseStream.Position & 7) == 0, "ResourceWriter: Name hashes array won't be 8 byte aligned! Ack!"); #if RESOURCE_FILE_FORMAT_DEBUG bw.Write(new byte[] { (byte)'H', (byte)'A', (byte)'S', (byte)'H', (byte)'E', (byte)'S', (byte)'-', (byte)'>' }); #endif foreach (int hash in nameHashes) { bw.Write(hash); } #if RESOURCE_FILE_FORMAT_DEBUG Console.Write("Name hashes: "); foreach (int hash in nameHashes) { Console.Write(hash.ToString("x") + " "); } Console.WriteLine(); #endif // Write relative positions of all the names in the file. // Note: this data is 4 byte aligned, occuring immediately // after the 8 byte aligned name hashes (whose length may // potentially be odd). Contract.Assert((bw.BaseStream.Position & 3) == 0, "ResourceWriter: Name positions array won't be 4 byte aligned! Ack!"); #if RESOURCE_FILE_FORMAT_DEBUG bw.Write(new byte[] { (byte)'P', (byte)'O', (byte)'S', (byte)'-', (byte)'-', (byte)'-', (byte)'-', (byte)'>' }); #endif foreach (int pos in namePositions) { bw.Write(pos); } #if RESOURCE_FILE_FORMAT_DEBUG Console.Write("Name positions: "); foreach (int pos in namePositions) { Console.Write(pos.ToString("x") + " "); } Console.WriteLine(); #endif // Flush all BinaryWriters to their underlying streams. bw.Flush(); names.Flush(); data.Flush(); // Write offset to data section int startOfDataSection = (int)(bw.Seek(0, SeekOrigin.Current) + nameSection.Length); startOfDataSection += 4; // We're writing an int to store this data, adding more bytes to the header BCLDebug.Log("RESMGRFILEFORMAT", "Generate: start of DataSection: 0x" + startOfDataSection.ToString("x", CultureInfo.InvariantCulture) + " nameSection length: " + nameSection.Length); bw.Write(startOfDataSection); // Write name section. bw.Write(nameSection.GetBuffer(), 0, (int)nameSection.Length); names.Close(); // Write data section. Contract.Assert(startOfDataSection == bw.Seek(0, SeekOrigin.Current), "ResourceWriter::Generate - start of data section is wrong!"); dataSection.Position = 0; dataSection.CopyTo(bw.BaseStream); data.Close(); } // using(dataSection) <--- Closes dataSection, which was opened w/ FileOptions.DeleteOnClose bw.Flush(); // Indicate we've called Generate _resourceList = null; }
// After calling AddResource, Generate() writes out all resources to the // output stream in the system default format. // If an exception occurs during object serialization or during IO, // the .resources file is closed and deleted, since it is most likely // invalid. public void Generate() { if (_resourceList == null) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved")); } BinaryWriter bw = new BinaryWriter(_output, Encoding.UTF8); List <String> typeNames = new List <String>(); // Write out the ResourceManager header // Write out magic number bw.Write(ResourceManager.MagicNumber); // Write out ResourceManager header version number bw.Write(ResourceManager.HeaderVersionNumber); MemoryStream resMgrHeaderBlob = new MemoryStream(240); BinaryWriter resMgrHeaderPart = new BinaryWriter(resMgrHeaderBlob); // Write out class name of IResourceReader capable of handling // this file. resMgrHeaderPart.Write(typeof(ResourceReader).AssemblyQualifiedName); // Write out class name of the ResourceSet class best suited to // handling this file. resMgrHeaderPart.Write(ResourceManager.ResSetTypeName); resMgrHeaderPart.Flush(); // Write number of bytes to skip over to get past ResMgr header bw.Write((int)resMgrHeaderBlob.Length); // Write the rest of the ResMgr header bw.Write(resMgrHeaderBlob.GetBuffer(), 0, (int)resMgrHeaderBlob.Length); // End ResourceManager header // Write out the RuntimeResourceSet header // Version number bw.Write(RuntimeResourceSet.Version); #if RESOURCE_FILE_FORMAT_DEBUG // Write out a tag so we know whether to enable or disable // debugging support when reading the file. bw.Write("***DEBUG***"); #endif // number of resources int numResources = _resourceList.Count; if (_preserializedData != null) { numResources += _preserializedData.Count; } bw.Write(numResources); // Store values in temporary streams to write at end of file. int[] nameHashes = new int[numResources]; int[] namePositions = new int[numResources]; int curNameNumber = 0; MemoryStream nameSection = new MemoryStream(numResources * AverageNameSize); BinaryWriter names = new BinaryWriter(nameSection, Encoding.Unicode); MemoryStream dataSection = new MemoryStream(numResources * AverageValueSize); BinaryWriter data = new BinaryWriter(dataSection, Encoding.UTF8); IFormatter objFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File | StreamingContextStates.Persistence)); #if RESOURCE_FILE_FORMAT_DEBUG // Write NAMES right before the names section. names.Write(new byte[] { (byte)'N', (byte)'A', (byte)'M', (byte)'E', (byte)'S', (byte)'-', (byte)'-', (byte)'>' }); // Write DATA at the end of the name table section. data.Write(new byte[] { (byte)'D', (byte)'A', (byte)'T', (byte)'A', (byte)'-', (byte)'-', (byte)'-', (byte)'>' }); #endif // We've stored our resources internally in a Hashtable, which // makes no guarantees about the ordering while enumerating. // While we do our own sorting of the resource names based on their // hash values, that's only sorting the nameHashes and namePositions // arrays. That's all that is strictly required for correctness, // but for ease of generating a patch in the future that // modifies just .resources files, we should re-sort them. SortedList sortedResources = new SortedList(_resourceList, FastResourceComparer.Default); if (_preserializedData != null) { foreach (DictionaryEntry entry in _preserializedData) { sortedResources.Add(entry.Key, entry.Value); } } IDictionaryEnumerator items = sortedResources.GetEnumerator(); // Write resource name and position to the file, and the value // to our temporary buffer. Save Type as well. while (items.MoveNext()) { nameHashes[curNameNumber] = FastResourceComparer.HashFunction((String)items.Key); namePositions[curNameNumber++] = (int)names.Seek(0, SeekOrigin.Current); names.Write((String)items.Key); // key names.Write((int)data.Seek(0, SeekOrigin.Current)); // virtual offset of value. #if RESOURCE_FILE_FORMAT_DEBUG names.Write((byte)'*'); #endif Object value = items.Value; ResourceTypeCode typeCode = FindTypeCode(value, typeNames); // Write out type code Write7BitEncodedInt(data, (int)typeCode); // Write out value PrecannedResource userProvidedResource = value as PrecannedResource; if (userProvidedResource != null) { data.Write(userProvidedResource.Data); } else { WriteValue(typeCode, value, data, objFormatter); } #if RESOURCE_FILE_FORMAT_DEBUG data.Write(new byte[] { (byte)'S', (byte)'T', (byte)'O', (byte)'P' }); #endif } // At this point, the ResourceManager header has been written. // Finish RuntimeResourceSet header // Write size & contents of class table bw.Write(typeNames.Count); for (int i = 0; i < typeNames.Count; i++) { bw.Write(typeNames[i]); } // Write out the name-related items for lookup. // Note that the hash array and the namePositions array must // be sorted in parallel. Array.Sort(nameHashes, namePositions); // Prepare to write sorted name hashes (alignment fixup) // Note: For 64-bit machines, these MUST be aligned on 8 byte // boundaries! Pointers on IA64 must be aligned! And we'll // run faster on X86 machines too. bw.Flush(); int alignBytes = ((int)bw.BaseStream.Position) & 7; if (alignBytes > 0) { for (int i = 0; i < 8 - alignBytes; i++) { bw.Write("PAD"[i % 3]); } } // Write out sorted name hashes. // Align to 8 bytes. BCLDebug.Assert((bw.BaseStream.Position & 7) == 0, "ResourceWriter: Name hashes array won't be 8 byte aligned! Ack!"); #if RESOURCE_FILE_FORMAT_DEBUG bw.Write(new byte[] { (byte)'H', (byte)'A', (byte)'S', (byte)'H', (byte)'E', (byte)'S', (byte)'-', (byte)'>' }); #endif foreach (int hash in nameHashes) { bw.Write(hash); } #if RESOURCE_FILE_FORMAT_DEBUG Console.Write("Name hashes: "); foreach (int hash in nameHashes) { Console.Write(hash.ToString("x") + " "); } Console.WriteLine(); #endif // Write relative positions of all the names in the file. // Note: this data is 4 byte aligned, occuring immediately // after the 8 byte aligned name hashes (whose length may // potentially be odd). BCLDebug.Assert((bw.BaseStream.Position & 3) == 0, "ResourceWriter: Name positions array won't be 4 byte aligned! Ack!"); #if RESOURCE_FILE_FORMAT_DEBUG bw.Write(new byte[] { (byte)'P', (byte)'O', (byte)'S', (byte)'-', (byte)'-', (byte)'-', (byte)'-', (byte)'>' }); #endif foreach (int pos in namePositions) { bw.Write(pos); } #if RESOURCE_FILE_FORMAT_DEBUG Console.Write("Name positions: "); foreach (int pos in namePositions) { Console.Write(pos.ToString("x") + " "); } Console.WriteLine(); #endif // Flush all BinaryWriters to MemoryStreams. bw.Flush(); names.Flush(); data.Flush(); // Write offset to data section int startOfDataSection = (int)(bw.Seek(0, SeekOrigin.Current) + nameSection.Length); startOfDataSection += 4; // We're writing an int to store this data, adding more bytes to the header BCLDebug.Log("RESMGRFILEFORMAT", "Generate: start of DataSection: 0x" + startOfDataSection.ToString("x", CultureInfo.InvariantCulture) + " nameSection length: " + nameSection.Length); bw.Write(startOfDataSection); // Write name section. bw.Write(nameSection.GetBuffer(), 0, (int)nameSection.Length); names.Close(); // Write data section. BCLDebug.Assert(startOfDataSection == bw.Seek(0, SeekOrigin.Current), "ResourceWriter::Generate - start of data section is wrong!"); bw.Write(dataSection.GetBuffer(), 0, (int)dataSection.Length); data.Close(); bw.Flush(); // Indicate we've called Generate _resourceList = null; }