Update mcs/class/Commons.Xml.Relaxng/Commons.Xml.Relaxng/RelaxngPattern.cs
[mono.git] / mcs / class / IKVM.Reflection / Writer / ResourceSection.cs
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 }