ILRepack/Steps/Win32Resources/RsrcWriter.cs (133 lines of code) (raw):

// // Copyright (c) 2012 Francois Valdy // Copyright (c) 2018 Alexander Vostres // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // using System.Collections.Generic; using ILRepacking.Steps.Win32Resources.PE; namespace ILRepacking.Steps.Win32Resources { class RsrcWriter { private static int GetDirectoryLength(ResourceDirectory dir) { int length = 16 + dir.Entries.Count * 8; foreach (ResourceEntry entry in dir.Entries) length += GetDirectoryLength(entry); return length; } private static int GetDirectoryLength(ResourceEntry entry) { if (entry.Data != null) return 16; return GetDirectoryLength(entry.Directory); } public static ByteBuffer WriteWin32ResourcesDirectory(uint virtualAddress, ResourceDirectory directory) { var result = new ByteBuffer(); if (directory.Entries.Count != 0) { int stringTableOffset = GetDirectoryLength(directory); Dictionary<string, int> strings = new Dictionary<string, int>(); ByteBuffer stringTable = new ByteBuffer(16); int offset = 16 + directory.Entries.Count * 8; for (int pass = 0; pass < 3; pass++) Write(result, directory, pass, 0, ref offset, strings, ref stringTableOffset, stringTable); // the pecoff spec says that the string table is between the directory entries and the data entries, // but the windows linker puts them after the data entries, so we do too. stringTable.Align(4); offset += stringTable.length; WriteResourceDataEntries(result, virtualAddress, directory, ref offset); result.WriteBytes(stringTable); WriteData(result, directory); } return result; } private static void WriteResourceDataEntries(ByteBuffer win32_resources, uint virtualAddress, ResourceDirectory directory, ref int offset) { foreach (ResourceEntry entry in directory.Entries) { if (entry.Data != null) { win32_resources.WriteUInt32((uint) (virtualAddress + offset)); win32_resources.WriteInt32(entry.Data.Length); win32_resources.WriteUInt32(entry.CodePage); win32_resources.WriteUInt32(entry.Reserved); offset += (entry.Data.Length + 3) & ~3; } else { WriteResourceDataEntries(win32_resources, virtualAddress, entry.Directory, ref offset); } } } private static void WriteData(ByteBuffer win32_resources, ResourceDirectory directory) { foreach (ResourceEntry entry in directory.Entries) { if (entry.Data != null) { win32_resources.WriteBytes(entry.Data); win32_resources.Align(4); } else { WriteData(win32_resources, entry.Directory); } } } private static void Write(ByteBuffer win32_resources, ResourceDirectory directory, int writeDepth, int currentDepth, ref int offset, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable) { if (currentDepth == writeDepth) { ushort namedEntries = directory.SortEntries(); // directory header win32_resources.WriteUInt32(directory.Characteristics); win32_resources.WriteUInt32(directory.TimeDateStamp); win32_resources.WriteUInt16(directory.MajorVersion); win32_resources.WriteUInt16(directory.MinVersion); win32_resources.WriteUInt16(namedEntries); win32_resources.WriteUInt16((ushort)(directory.Entries.Count - namedEntries)); foreach (ResourceEntry entry in directory.Entries) { WriteEntry(win32_resources, entry, ref offset, strings, ref stringTableOffset, stringTable); } } else { foreach (ResourceEntry entry in directory.Entries) { Write(win32_resources, entry.Directory, writeDepth, currentDepth + 1, ref offset, strings, ref stringTableOffset, stringTable); } } } private static void WriteEntry(ByteBuffer win32_resources, ResourceEntry entry, ref int offset, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable) { WriteNameOrOrdinal(win32_resources, entry, strings, ref stringTableOffset, stringTable); if (entry.Data == null) { win32_resources.WriteUInt32(0x80000000U | (uint)offset); offset += entry.Directory.Entries.Count * 8; } else { win32_resources.WriteUInt32((uint)offset); } offset += 16; } private static void WriteNameOrOrdinal(ByteBuffer win32_resources, ResourceEntry entry, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable) { if (entry.Name == null) { win32_resources.WriteUInt32(entry.Id); } else { int stringOffset; if (!strings.TryGetValue(entry.Name, out stringOffset)) { stringOffset = stringTableOffset; strings.Add(entry.Name, stringOffset); stringTableOffset += entry.Name.Length * 2 + 2; stringTable.WriteUInt16((ushort)entry.Name.Length); foreach (char c in entry.Name) stringTable.WriteInt16((short)c); } win32_resources.WriteUInt32(0x80000000U | (uint)stringOffset); } } } }