2 Copyright (C) 2010-2012 Jeroen Frijters
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
25 using System.Collections.Generic;
28 using IKVM.Reflection.Reader;
30 namespace IKVM.Reflection.Writer
32 sealed class ResourceSection
34 private const int RT_ICON = 3;
35 private const int RT_GROUP_ICON = 14;
36 private const int RT_VERSION = 16;
37 private const int RT_MANIFEST = 24;
38 private ResourceDirectoryEntry root = new ResourceDirectoryEntry(new OrdinalOrName("root"));
39 private ByteBuffer bb;
40 private List<int> linkOffsets;
42 internal void AddVersionInfo(ByteBuffer versionInfo)
44 root[new OrdinalOrName(RT_VERSION)][new OrdinalOrName(1)][new OrdinalOrName(0)].Data = versionInfo;
47 internal void AddIcon(byte[] iconFile)
49 BinaryReader br = new BinaryReader(new MemoryStream(iconFile));
50 ushort idReserved = br.ReadUInt16();
51 ushort idType = br.ReadUInt16();
52 ushort idCount = br.ReadUInt16();
53 if (idReserved != 0 || idType != 1)
55 throw new ArgumentException("The supplied byte array is not a valid .ico file.");
57 ByteBuffer group = new ByteBuffer(6 + 14 * idCount);
58 group.Write(idReserved);
61 for (int i = 0; i < idCount; i++)
63 byte bWidth = br.ReadByte();
64 byte bHeight = br.ReadByte();
65 byte bColorCount = br.ReadByte();
66 byte bReserved = br.ReadByte();
67 ushort wPlanes = br.ReadUInt16();
68 ushort wBitCount = br.ReadUInt16();
69 uint dwBytesInRes = br.ReadUInt32();
70 uint dwImageOffset = br.ReadUInt32();
72 // we start the icon IDs at 2
73 ushort id = (ushort)(2 + i);
77 group.Write(bColorCount);
78 group.Write(bReserved);
80 group.Write(wBitCount);
81 group.Write(dwBytesInRes);
84 byte[] icon = new byte[dwBytesInRes];
85 Buffer.BlockCopy(iconFile, (int)dwImageOffset, icon, 0, icon.Length);
86 root[new OrdinalOrName(RT_ICON)][new OrdinalOrName(id)][new OrdinalOrName(0)].Data = ByteBuffer.Wrap(icon);
88 root[new OrdinalOrName(RT_GROUP_ICON)][new OrdinalOrName(32512)][new OrdinalOrName(0)].Data = group;
91 internal void AddManifest(byte[] manifest, ushort resourceID)
93 root[new OrdinalOrName(RT_MANIFEST)][new OrdinalOrName(resourceID)][new OrdinalOrName(0)].Data = ByteBuffer.Wrap(manifest);
96 internal void ExtractResources(byte[] buf)
98 ByteReader br = new ByteReader(buf, 0, buf.Length);
99 while (br.Length >= 32)
102 RESOURCEHEADER hdr = new RESOURCEHEADER(br);
103 if (hdr.DataSize != 0)
105 root[hdr.TYPE][hdr.NAME][new OrdinalOrName(hdr.LanguageId)].Data = ByteBuffer.Wrap(br.ReadBytes(hdr.DataSize));
110 internal void Finish()
114 throw new InvalidOperationException();
116 bb = new ByteBuffer(1024);
117 linkOffsets = new List<int>();
118 root.Write(bb, linkOffsets);
124 get { return bb.Length; }
127 internal void Write(MetadataWriter mw, uint rva)
129 foreach (int offset in linkOffsets)
131 bb.Position = offset;
132 bb.Write(bb.GetInt32AtCurrentPosition() + (int)rva);
138 sealed class ResourceDirectoryEntry
140 internal readonly OrdinalOrName OrdinalOrName;
141 internal ByteBuffer Data;
142 private int namedEntries;
143 private readonly List<ResourceDirectoryEntry> entries = new List<ResourceDirectoryEntry>();
145 internal ResourceDirectoryEntry(OrdinalOrName id)
147 this.OrdinalOrName = id;
150 internal ResourceDirectoryEntry this[OrdinalOrName id]
154 foreach (ResourceDirectoryEntry entry in entries)
156 if (entry.OrdinalOrName.IsEqual(id))
161 // the entries must be sorted
162 ResourceDirectoryEntry newEntry = new ResourceDirectoryEntry(id);
165 for (int i = namedEntries; i < entries.Count; i++)
167 if (entries[i].OrdinalOrName.IsGreaterThan(id))
169 entries.Insert(i, newEntry);
173 entries.Add(newEntry);
178 for (int i = 0; i < namedEntries; i++)
180 if (entries[i].OrdinalOrName.IsGreaterThan(id))
182 entries.Insert(i, newEntry);
187 entries.Insert(namedEntries++, newEntry);
193 private int DirectoryLength
203 int length = 16 + entries.Count * 8;
204 foreach (ResourceDirectoryEntry entry in entries)
206 length += entry.DirectoryLength;
213 internal void Write(ByteBuffer bb, List<int> linkOffsets)
215 if (entries.Count != 0)
217 int stringTableOffset = this.DirectoryLength;
218 Dictionary<string, int> strings = new Dictionary<string, int>();
219 ByteBuffer stringTable = new ByteBuffer(16);
220 int offset = 16 + entries.Count * 8;
221 for (int pass = 0; pass < 3; pass++)
223 Write(bb, pass, 0, ref offset, strings, ref stringTableOffset, stringTable);
225 // the pecoff spec says that the string table is between the directory entries and the data entries,
226 // but the windows linker puts them after the data entries, so we do too.
227 stringTable.Align(4);
228 offset += stringTable.Length;
229 WriteResourceDataEntries(bb, linkOffsets, ref offset);
230 bb.Write(stringTable);
235 private void WriteResourceDataEntries(ByteBuffer bb, List<int> linkOffsets, ref int offset)
237 foreach (ResourceDirectoryEntry entry in entries)
239 if (entry.Data != null)
241 linkOffsets.Add(bb.Position);
243 bb.Write(entry.Data.Length);
244 bb.Write(0); // code page
245 bb.Write(0); // reserved
246 offset += (entry.Data.Length + 3) & ~3;
250 entry.WriteResourceDataEntries(bb, linkOffsets, ref offset);
255 private void WriteData(ByteBuffer bb)
257 foreach (ResourceDirectoryEntry entry in entries)
259 if (entry.Data != null)
261 bb.Write(entry.Data);
271 private void Write(ByteBuffer bb, int writeDepth, int currentDepth, ref int offset, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable)
273 if (currentDepth == writeDepth)
276 bb.Write(0); // Characteristics
277 bb.Write(0); // Time/Date Stamp
278 bb.Write(0); // Version (Major / Minor)
279 bb.Write((ushort)namedEntries);
280 bb.Write((ushort)(entries.Count - namedEntries));
282 foreach (ResourceDirectoryEntry entry in entries)
284 if (currentDepth == writeDepth)
286 entry.WriteEntry(bb, ref offset, strings, ref stringTableOffset, stringTable);
290 entry.Write(bb, writeDepth, currentDepth + 1, ref offset, strings, ref stringTableOffset, stringTable);
295 private void WriteEntry(ByteBuffer bb, ref int offset, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable)
297 WriteNameOrOrdinal(bb, OrdinalOrName, strings, ref stringTableOffset, stringTable);
300 bb.Write(0x80000000U | (uint)offset);
306 offset += 16 + entries.Count * 8;
309 private static void WriteNameOrOrdinal(ByteBuffer bb, OrdinalOrName id, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable)
313 bb.Write((int)id.Ordinal);
318 if (!strings.TryGetValue(id.Name, out stringOffset))
320 stringOffset = stringTableOffset;
321 strings.Add(id.Name, stringOffset);
322 stringTableOffset += id.Name.Length * 2 + 2;
323 stringTable.Write((ushort)id.Name.Length);
324 foreach (char c in id.Name)
326 stringTable.Write((short)c);
329 bb.Write(0x80000000U | (uint)stringOffset);
336 internal readonly ushort Ordinal;
337 internal readonly string Name;
339 internal OrdinalOrName(ushort value)
345 internal OrdinalOrName(string value)
351 internal bool IsGreaterThan(OrdinalOrName other)
353 return this.Name == null
354 ? this.Ordinal > other.Ordinal
355 : String.Compare(this.Name, other.Name, StringComparison.OrdinalIgnoreCase) > 0;
358 internal bool IsEqual(OrdinalOrName other)
360 return this.Name == null
361 ? this.Ordinal == other.Ordinal
362 : String.Compare(this.Name, other.Name, StringComparison.OrdinalIgnoreCase) == 0;
366 struct RESOURCEHEADER
368 internal int DataSize;
369 internal int HeaderSize;
370 internal OrdinalOrName TYPE;
371 internal OrdinalOrName NAME;
372 internal int DataVersion;
373 internal ushort MemoryFlags;
374 internal ushort LanguageId;
375 internal int Version;
376 internal int Characteristics;
378 internal RESOURCEHEADER(ByteReader br)
380 DataSize = br.ReadInt32();
381 HeaderSize = br.ReadInt32();
382 TYPE = ReadOrdinalOrName(br);
383 NAME = ReadOrdinalOrName(br);
385 DataVersion = br.ReadInt32();
386 MemoryFlags = br.ReadUInt16();
387 LanguageId = br.ReadUInt16();
388 Version = br.ReadInt32();
389 Characteristics = br.ReadInt32();
392 private static OrdinalOrName ReadOrdinalOrName(ByteReader br)
394 char c = br.ReadChar();
397 return new OrdinalOrName(br.ReadUInt16());
401 StringBuilder sb = new StringBuilder();
407 return new OrdinalOrName(sb.ToString());