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());