~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/ikvm/reflect/Writer/ResourceSection.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  Copyright (C) 2010-2012 Jeroen Frijters
 
3
 
 
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.
 
7
 
 
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:
 
11
 
 
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.
 
19
 
 
20
  Jeroen Frijters
 
21
  jeroen@frijters.net
 
22
  
 
23
*/
 
24
using System;
 
25
using System.Collections.Generic;
 
26
using System.IO;
 
27
using System.Text;
 
28
using IKVM.Reflection.Reader;
 
29
 
 
30
namespace IKVM.Reflection.Writer
 
31
{
 
32
        sealed class ResourceSection
 
33
        {
 
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;
 
41
 
 
42
                internal void AddVersionInfo(ByteBuffer versionInfo)
 
43
                {
 
44
                        root[new OrdinalOrName(RT_VERSION)][new OrdinalOrName(1)][new OrdinalOrName(0)].Data = versionInfo;
 
45
                }
 
46
 
 
47
                internal void AddIcon(byte[] iconFile)
 
48
                {
 
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)
 
54
                        {
 
55
                                throw new ArgumentException("The supplied byte array is not a valid .ico file.");
 
56
                        }
 
57
                        ByteBuffer group = new ByteBuffer(6 + 14 * idCount);
 
58
                        group.Write(idReserved);
 
59
                        group.Write(idType);
 
60
                        group.Write(idCount);
 
61
                        for (int i = 0; i < idCount; i++)
 
62
                        {
 
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();
 
71
 
 
72
                                // we start the icon IDs at 2
 
73
                                ushort id = (ushort)(2 + i);
 
74
 
 
75
                                group.Write(bWidth);
 
76
                                group.Write(bHeight);
 
77
                                group.Write(bColorCount);
 
78
                                group.Write(bReserved);
 
79
                                group.Write(wPlanes);
 
80
                                group.Write(wBitCount);
 
81
                                group.Write(dwBytesInRes);
 
82
                                group.Write(id);
 
83
 
 
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);
 
87
                        }
 
88
                        root[new OrdinalOrName(RT_GROUP_ICON)][new OrdinalOrName(32512)][new OrdinalOrName(0)].Data = group;
 
89
                }
 
90
 
 
91
                internal void AddManifest(byte[] manifest, ushort resourceID)
 
92
                {
 
93
                        root[new OrdinalOrName(RT_MANIFEST)][new OrdinalOrName(resourceID)][new OrdinalOrName(0)].Data = ByteBuffer.Wrap(manifest);
 
94
                }
 
95
 
 
96
                internal void ExtractResources(byte[] buf)
 
97
                {
 
98
                        ByteReader br = new ByteReader(buf, 0, buf.Length);
 
99
                        while (br.Length >= 32)
 
100
                        {
 
101
                                br.Align(4);
 
102
                                RESOURCEHEADER hdr = new RESOURCEHEADER(br);
 
103
                                if (hdr.DataSize != 0)
 
104
                                {
 
105
                                        root[hdr.TYPE][hdr.NAME][new OrdinalOrName(hdr.LanguageId)].Data = ByteBuffer.Wrap(br.ReadBytes(hdr.DataSize));
 
106
                                }
 
107
                        }
 
108
                }
 
109
 
 
110
                internal void Finish()
 
111
                {
 
112
                        if (bb != null)
 
113
                        {
 
114
                                throw new InvalidOperationException();
 
115
                        }
 
116
                        bb = new ByteBuffer(1024);
 
117
                        linkOffsets = new List<int>();
 
118
                        root.Write(bb, linkOffsets);
 
119
                        root = null;
 
120
                }
 
121
 
 
122
                internal int Length
 
123
                {
 
124
                        get { return bb.Length; }
 
125
                }
 
126
 
 
127
                internal void Write(MetadataWriter mw, uint rva)
 
128
                {
 
129
                        foreach (int offset in linkOffsets)
 
130
                        {
 
131
                                bb.Position = offset;
 
132
                                bb.Write(bb.GetInt32AtCurrentPosition() + (int)rva);
 
133
                        }
 
134
                        mw.Write(bb);
 
135
                }
 
136
        }
 
137
 
 
138
        sealed class ResourceDirectoryEntry
 
139
        {
 
140
                internal readonly OrdinalOrName OrdinalOrName;
 
141
                internal ByteBuffer Data;
 
142
                private int namedEntries;
 
143
                private readonly List<ResourceDirectoryEntry> entries = new List<ResourceDirectoryEntry>();
 
144
 
 
145
                internal ResourceDirectoryEntry(OrdinalOrName id)
 
146
                {
 
147
                        this.OrdinalOrName = id;
 
148
                }
 
149
 
 
150
                internal ResourceDirectoryEntry this[OrdinalOrName id]
 
151
                {
 
152
                        get
 
153
                        {
 
154
                                foreach (ResourceDirectoryEntry entry in entries)
 
155
                                {
 
156
                                        if (entry.OrdinalOrName.IsEqual(id))
 
157
                                        {
 
158
                                                return entry;
 
159
                                        }
 
160
                                }
 
161
                                // the entries must be sorted
 
162
                                ResourceDirectoryEntry newEntry = new ResourceDirectoryEntry(id);
 
163
                                if (id.Name == null)
 
164
                                {
 
165
                                        for (int i = namedEntries; i < entries.Count; i++)
 
166
                                        {
 
167
                                                if (entries[i].OrdinalOrName.IsGreaterThan(id))
 
168
                                                {
 
169
                                                        entries.Insert(i, newEntry);
 
170
                                                        return newEntry;
 
171
                                                }
 
172
                                        }
 
173
                                        entries.Add(newEntry);
 
174
                                        return newEntry;
 
175
                                }
 
176
                                else
 
177
                                {
 
178
                                        for (int i = 0; i < namedEntries; i++)
 
179
                                        {
 
180
                                                if (entries[i].OrdinalOrName.IsGreaterThan(id))
 
181
                                                {
 
182
                                                        entries.Insert(i, newEntry);
 
183
                                                        namedEntries++;
 
184
                                                        return newEntry;
 
185
                                                }
 
186
                                        }
 
187
                                        entries.Insert(namedEntries++, newEntry);
 
188
                                        return newEntry;
 
189
                                }
 
190
                        }
 
191
                }
 
192
 
 
193
                private int DirectoryLength
 
194
                {
 
195
                        get
 
196
                        {
 
197
                                if (Data != null)
 
198
                                {
 
199
                                        return 16;
 
200
                                }
 
201
                                else
 
202
                                {
 
203
                                        int length = 16 + entries.Count * 8;
 
204
                                        foreach (ResourceDirectoryEntry entry in entries)
 
205
                                        {
 
206
                                                length += entry.DirectoryLength;
 
207
                                        }
 
208
                                        return length;
 
209
                                }
 
210
                        }
 
211
                }
 
212
 
 
213
                internal void Write(ByteBuffer bb, List<int> linkOffsets)
 
214
                {
 
215
                        if (entries.Count != 0)
 
216
                        {
 
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++)
 
222
                                {
 
223
                                        Write(bb, pass, 0, ref offset, strings, ref stringTableOffset, stringTable);
 
224
                                }
 
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);
 
231
                                WriteData(bb);
 
232
                        }
 
233
                }
 
234
 
 
235
                private void WriteResourceDataEntries(ByteBuffer bb, List<int> linkOffsets, ref int offset)
 
236
                {
 
237
                        foreach (ResourceDirectoryEntry entry in entries)
 
238
                        {
 
239
                                if (entry.Data != null)
 
240
                                {
 
241
                                        linkOffsets.Add(bb.Position);
 
242
                                        bb.Write(offset);
 
243
                                        bb.Write(entry.Data.Length);
 
244
                                        bb.Write(0);    // code page
 
245
                                        bb.Write(0);    // reserved
 
246
                                        offset += (entry.Data.Length + 3) & ~3;
 
247
                                }
 
248
                                else
 
249
                                {
 
250
                                        entry.WriteResourceDataEntries(bb, linkOffsets, ref offset);
 
251
                                }
 
252
                        }
 
253
                }
 
254
 
 
255
                private void WriteData(ByteBuffer bb)
 
256
                {
 
257
                        foreach (ResourceDirectoryEntry entry in entries)
 
258
                        {
 
259
                                if (entry.Data != null)
 
260
                                {
 
261
                                        bb.Write(entry.Data);
 
262
                                        bb.Align(4);
 
263
                                }
 
264
                                else
 
265
                                {
 
266
                                        entry.WriteData(bb);
 
267
                                }
 
268
                        }
 
269
                }
 
270
 
 
271
                private void Write(ByteBuffer bb, int writeDepth, int currentDepth, ref int offset, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable)
 
272
                {
 
273
                        if (currentDepth == writeDepth)
 
274
                        {
 
275
                                // directory header
 
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));
 
281
                        }
 
282
                        foreach (ResourceDirectoryEntry entry in entries)
 
283
                        {
 
284
                                if (currentDepth == writeDepth)
 
285
                                {
 
286
                                        entry.WriteEntry(bb, ref offset, strings, ref stringTableOffset, stringTable);
 
287
                                }
 
288
                                else
 
289
                                {
 
290
                                        entry.Write(bb, writeDepth, currentDepth + 1, ref offset, strings, ref stringTableOffset, stringTable);
 
291
                                }
 
292
                        }
 
293
                }
 
294
 
 
295
                private void WriteEntry(ByteBuffer bb, ref int offset, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable)
 
296
                {
 
297
                        WriteNameOrOrdinal(bb, OrdinalOrName, strings, ref stringTableOffset, stringTable);
 
298
                        if (Data == null)
 
299
                        {
 
300
                                bb.Write(0x80000000U | (uint)offset);
 
301
                        }
 
302
                        else
 
303
                        {
 
304
                                bb.Write(offset);
 
305
                        }
 
306
                        offset += 16 + entries.Count * 8;
 
307
                }
 
308
 
 
309
                private static void WriteNameOrOrdinal(ByteBuffer bb, OrdinalOrName id, Dictionary<string, int> strings, ref int stringTableOffset, ByteBuffer stringTable)
 
310
                {
 
311
                        if (id.Name == null)
 
312
                        {
 
313
                                bb.Write((int)id.Ordinal);
 
314
                        }
 
315
                        else
 
316
                        {
 
317
                                int stringOffset;
 
318
                                if (!strings.TryGetValue(id.Name, out stringOffset))
 
319
                                {
 
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)
 
325
                                        {
 
326
                                                stringTable.Write((short)c);
 
327
                                        }
 
328
                                }
 
329
                                bb.Write(0x80000000U | (uint)stringOffset);
 
330
                        }
 
331
                }
 
332
        }
 
333
 
 
334
        struct OrdinalOrName
 
335
        {
 
336
                internal readonly ushort Ordinal;
 
337
                internal readonly string Name;
 
338
 
 
339
                internal OrdinalOrName(ushort value)
 
340
                {
 
341
                        Ordinal = value;
 
342
                        Name = null;
 
343
                }
 
344
 
 
345
                internal OrdinalOrName(string value)
 
346
                {
 
347
                        Ordinal = 0xFFFF;
 
348
                        Name = value;
 
349
                }
 
350
 
 
351
                internal bool IsGreaterThan(OrdinalOrName other)
 
352
                {
 
353
                        return this.Name == null
 
354
                                ? this.Ordinal > other.Ordinal
 
355
                                : String.Compare(this.Name, other.Name, StringComparison.OrdinalIgnoreCase) > 0;
 
356
                }
 
357
 
 
358
                internal bool IsEqual(OrdinalOrName other)
 
359
                {
 
360
                        return this.Name == null
 
361
                                ? this.Ordinal == other.Ordinal
 
362
                                : String.Compare(this.Name, other.Name, StringComparison.OrdinalIgnoreCase) == 0;
 
363
                }
 
364
        }
 
365
 
 
366
        struct RESOURCEHEADER
 
367
        {
 
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;
 
377
 
 
378
                internal RESOURCEHEADER(ByteReader br)
 
379
                {
 
380
                        DataSize = br.ReadInt32();
 
381
                        HeaderSize = br.ReadInt32();
 
382
                        TYPE = ReadOrdinalOrName(br);
 
383
                        NAME = ReadOrdinalOrName(br);
 
384
                        br.Align(4);
 
385
                        DataVersion = br.ReadInt32();
 
386
                        MemoryFlags = br.ReadUInt16();
 
387
                        LanguageId = br.ReadUInt16();
 
388
                        Version = br.ReadInt32();
 
389
                        Characteristics = br.ReadInt32();
 
390
                }
 
391
 
 
392
                private static OrdinalOrName ReadOrdinalOrName(ByteReader br)
 
393
                {
 
394
                        char c = br.ReadChar();
 
395
                        if (c == 0xFFFF)
 
396
                        {
 
397
                                return new OrdinalOrName(br.ReadUInt16());
 
398
                        }
 
399
                        else
 
400
                        {
 
401
                                StringBuilder sb = new StringBuilder();
 
402
                                while (c != 0)
 
403
                                {
 
404
                                        sb.Append(c);
 
405
                                        c = br.ReadChar();
 
406
                                }
 
407
                                return new OrdinalOrName(sb.ToString());
 
408
                        }
 
409
                }
 
410
        }
 
411
}