2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / corlib / System.Resources / Win32Resources.cs
1 //
2 // System.Resources/Win32Resources.cs
3 //
4 // Author:
5 //   Zoltan Varga (vargaz@freemail.hu)
6 //
7 // (C) 2003 Ximian, Inc.  http://www.ximian.com
8 //
9 // An incomplete set of classes for manipulating Win32 resources
10 //
11
12 //
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System;
36 using System.Collections;
37 using System.IO;
38 using System.Text;
39
40 namespace System.Resources {
41
42
43 internal enum Win32ResourceType {
44         RT_CURSOR = 1,
45         RT_FONT = 8,
46         RT_BITMAP = 2,
47         RT_ICON = 3,
48         RT_MENU = 4,
49         RT_DIALOG = 5,
50         RT_STRING = 6,
51         RT_FONTDIR = 7,
52         RT_ACCELERATOR = 9,
53         RT_RCDATA = 10,
54         RT_MESSAGETABLE = 11,
55         RT_GROUP_CURSOR = 12,
56         RT_GROUP_ICON = 14,
57         RT_VERSION = 16,
58         RT_DLGINCLUDE = 17,
59         RT_PLUGPLAY = 19,
60         RT_VXD = 20,
61         RT_ANICURSOR = 21,
62         RT_ANIICON = 22,
63         RT_HTML = 23,
64 }
65
66 internal class NameOrId {
67         string name;
68         int id;
69
70         public NameOrId (string name) {
71                 this.name = name;
72         }
73
74         public NameOrId (int id) {
75                 this.id = id;
76         }
77
78         public bool IsName {
79                 get {
80                         return name != null;
81                 }
82         }
83
84         public string Name {
85                 get {
86                         return name;
87                 }
88         }
89
90         public int Id {
91                 get {
92                         return id;
93                 }
94         }
95
96         public override string ToString () {
97                 if (name != null)
98                         return "Name(" + name + ")";
99                 else
100                         return "Id(" + id + ")";
101         }
102 }
103
104 internal abstract class Win32Resource {
105
106         NameOrId type;
107         NameOrId name;
108         int language;
109
110         internal Win32Resource (NameOrId type, NameOrId name, int language) {
111                 this.type = type;
112                 this.name = name;
113                 this.language = language;
114         }
115
116         internal Win32Resource (Win32ResourceType type, int name, int language) {
117                 this.type = new NameOrId ((int)type);
118                 this.name = new NameOrId (name);
119                 this.language = language;
120         }
121
122         public Win32ResourceType ResourceType {
123                 get {
124                         if (type.IsName)
125                                 return (Win32ResourceType)(-1);
126                         else
127                                 return (Win32ResourceType)type.Id;
128                 }
129         }
130
131         public NameOrId Name {
132                 get {
133                         return name;
134                 }
135         }
136
137         public NameOrId Type {
138                 get {
139                         return type;
140                 }
141         }
142
143         public int Language {
144                 get {
145                         return language;
146                 }
147         }
148
149         public abstract void WriteTo (Stream s);
150
151         public override string ToString () {
152                 return "Win32Resource (Kind=" + ResourceType + ", Name=" + name + ")";
153         }
154 }
155
156 //
157 // This class represents a Win32 resource in encoded format
158 //
159 internal class Win32EncodedResource : Win32Resource {
160
161         byte[] data;
162
163         internal Win32EncodedResource (NameOrId type, NameOrId name, int language, byte[] data) : base (type, name, language) {
164                 this.data = data;
165         }
166
167         public byte[] Data {
168                 get {
169                         return data;
170                 }
171         }
172
173         public override void WriteTo (Stream s) {
174                 s.Write (data, 0, data.Length);
175         }
176 }
177
178 //
179 // This class represents a Win32 ICON resource
180 //
181 internal class Win32IconResource : Win32Resource {
182
183         ICONDIRENTRY icon;
184
185         public Win32IconResource (int id, int language, ICONDIRENTRY icon) : base (Win32ResourceType.RT_ICON, id, language) {
186                 this.icon = icon;
187         }
188
189         public ICONDIRENTRY Icon {
190                 get {
191                         return icon;
192                 }
193         }
194
195         public override void WriteTo (Stream s) {
196                 s.Write (icon.image, 0, icon.image.Length);
197         }
198 }
199
200 internal class Win32GroupIconResource : Win32Resource {
201
202         Win32IconResource[] icons;
203
204         public Win32GroupIconResource (int id, int language, Win32IconResource[] icons) : base (Win32ResourceType.RT_GROUP_ICON, id, language) {
205                 this.icons = icons;
206         }
207
208         public override void WriteTo (Stream s) {
209                 using (BinaryWriter w = new BinaryWriter (s)) {
210                         w.Write ((short)0);
211                         w.Write ((short)1);
212                         w.Write ((short)icons.Length);
213                         for (int i = 0; i < icons.Length; ++i) {
214                                 Win32IconResource icon = icons [i];
215                                 ICONDIRENTRY entry = icon.Icon;
216
217                                 w.Write (entry.bWidth);
218                                 w.Write (entry.bHeight);
219                                 w.Write (entry.bColorCount);
220                                 w.Write ((byte)0);
221                                 w.Write (entry.wPlanes);
222                                 w.Write (entry.wBitCount);
223                                 w.Write ((int)entry.image.Length);
224                                 w.Write ((short)icon.Name.Id);
225                         }
226                 }
227         }
228 }
229
230 //
231 // This class represents a Win32 VERSION resource
232 //
233 internal class Win32VersionResource : Win32Resource {
234
235         public string[] WellKnownProperties = {
236                 "Comments",
237                 "CompanyName",
238                 "FileDescription",
239                 "FileVersion",
240                 "InternalName",
241                 "LegalCopyright",
242                 "LegalTrademarks",
243                 "OriginalFilename",
244                 "ProductName",
245                 "ProductVersion"
246         };
247
248         long signature;
249         int struct_version;
250         long file_version;
251         long product_version;
252         int file_flags_mask;
253         int file_flags;
254         int file_os;
255         int file_type;
256         int file_subtype;
257         long file_date;
258
259         int file_lang;
260         int file_codepage;
261
262         Hashtable properties;
263
264         public Win32VersionResource (int id, int language) : base (Win32ResourceType.RT_VERSION, id, language) {
265                 // Initialize non-public members to the usual values used in
266                 // resources
267                 signature = 0xfeef04bd;
268                 struct_version = 1 << 16; /* 1.0 */
269                 file_flags_mask = 63;
270                 file_flags = 0;
271                 file_os = 4; /* VOS_WIN32 */
272                 file_type = 2;
273                 file_subtype = 0;
274                 file_date = 0;
275
276                 file_lang = 0x7f;
277                 file_codepage = 1200;
278
279                 properties = new Hashtable ();
280
281                 // Well known properties
282                 foreach (string s in WellKnownProperties)
283                         // The value of properties can't be empty
284                         properties [s] = " ";
285         }
286
287         public string FileVersion {
288                 get {
289                         return 
290                                 "" + (file_version >> 48) + 
291                                 "." + ((file_version >> 32) & 0xffff) + 
292                                 "." + ((file_version >> 16) & 0xffff) +
293                                 "." + ((file_version >> 0) & 0xffff);
294                 }
295
296                 set {
297                         long[] ver = new long [4] { 0, 0, 0, 0 };
298                         if (value != null) {
299                                 string[] parts = value.Split ('.');
300                                 
301                                 for (int i = 0; i < parts.Length; ++i) {
302                                         try {
303                                                 if (i < ver.Length)
304                                                         ver [i] = Int32.Parse (parts [i]);
305                                         }
306                                         catch (FormatException) {
307                                         }
308                                 }
309                         }
310
311                         file_version = (ver [0] << 48) | (ver [1] << 32) | (ver [2] << 16) + ver [3];
312                 }
313         }
314
315         public virtual string this [string key] {
316                 set {
317                         properties [key] = value;
318                 }
319         }
320
321         // Accessors for well known properties
322
323         public virtual string Comments {
324                 get {
325                         return (string)properties ["Comments"];
326                 }
327                 set {
328                         properties ["Comments"] = value == String.Empty ? " " : value;
329                 }
330         }
331
332         public virtual string CompanyName {
333                 get {
334                         return (string)properties ["CompanyName"];
335                 }
336                 set {
337                         properties ["CompanyName"] = value == String.Empty ? " " : value;
338                 }
339         }
340
341         public virtual string LegalCopyright {
342                 get {
343                         return (string)properties ["LegalCopyright"];
344                 }
345                 set {
346                         properties ["LegalCopyright"] = value == String.Empty ? " " : value;
347                 }
348         }
349
350         public virtual string LegalTrademarks {
351                 get {
352                         return (string)properties ["LegalTrademarks"];
353                 }
354                 set {
355                         properties ["LegalTrademarks"] = value == String.Empty ? " " : value;
356                 }
357         }
358
359         public virtual string OriginalFilename {
360                 get {
361                         return (string)properties ["OriginalFilename"];
362                 }
363                 set {
364                         properties ["OriginalFilename"] = value == String.Empty ? " " : value;
365                 }
366         }
367
368         public virtual string ProductName {
369                 get {
370                         return (string)properties ["ProductName"];
371                 }
372                 set {
373                         properties ["ProductName"] = value == String.Empty ? " " : value;
374                 }
375         }
376
377         public virtual string ProductVersion {
378                 get {
379                         return (string)properties ["ProductVersion"];
380                 }
381                 set {
382                         properties ["ProductVersion"] = value == String.Empty ? " " : value;
383                 }
384         }
385
386         public virtual string InternalName {
387                 get {
388                         return (string)properties ["InternalName"];
389                 }
390                 set {
391                         properties ["InternalName"] = value == String.Empty ? " " : value;
392                 }
393         }
394
395         public virtual string FileDescription {
396                 get {
397                         return (string)properties ["FileDescription"];
398                 }
399                 set {
400                         properties ["FileDescription"] = value == String.Empty ? " " : value;
401                 }
402         }
403
404         public virtual int FileLanguage {
405                 get {
406                         return file_lang;
407                 }
408                 set {
409                         file_lang = value;
410                 }
411         }
412
413         private void emit_padding (BinaryWriter w) {
414                 Stream ms = w.BaseStream;
415
416                 if ((ms.Position % 4) != 0)
417                         w.Write ((short)0);
418         }
419
420         private void patch_length (BinaryWriter w, long len_pos) {
421                 Stream ms = w.BaseStream;
422
423                 long pos = ms.Position;
424                 ms.Position = len_pos;
425                 w.Write ((short)(pos - len_pos));
426                 ms.Position = pos;
427         }
428
429         public override void WriteTo (Stream ms)
430         {
431                 using (BinaryWriter w = new BinaryWriter (ms, Encoding.Unicode)) {
432                         //
433                         // See the documentation for the VS_VERSIONINFO structure and
434                         // its children on MSDN
435                         //
436
437                         // VS_VERSIONINFO
438                         w.Write ((short)0);
439                         w.Write ((short)0x34);
440                         w.Write ((short)0);
441                         w.Write ("VS_VERSION_INFO".ToCharArray ());
442                         w.Write ((short)0);
443
444                         emit_padding (w);
445
446                         // VS_FIXEDFILEINFO
447                         w.Write ((uint)signature);
448                         w.Write ((int)struct_version);
449                         w.Write ((int)(file_version >> 32));
450                         w.Write ((int)((file_version & 0xffffffff)));
451
452                         w.Write ((int)(product_version >> 32));
453                         w.Write ((int)(product_version & 0xffffffff));
454                         w.Write ((int)file_flags_mask);
455                         w.Write ((int)file_flags);
456                         w.Write ((int)file_os);
457                         w.Write ((int)file_type);
458                         w.Write ((int)file_subtype);
459                         w.Write ((int)(file_date >> 32));
460                         w.Write ((int)(file_date & 0xffffffff));
461
462                         emit_padding (w);
463
464                         // VarFileInfo
465                         long var_file_info_pos = ms.Position;
466                         w.Write ((short)0);
467                         w.Write ((short)0);
468                         w.Write ((short)1);
469                         w.Write ("VarFileInfo".ToCharArray ());
470                         w.Write ((short)0);
471
472                         if ((ms.Position % 4) != 0)
473                                 w.Write ((short)0);
474
475                         // Var
476                         long var_pos = ms.Position;
477                         w.Write ((short)0);
478                         w.Write ((short)4);
479                         w.Write ((short)0);
480                         w.Write ("Translation".ToCharArray ());
481                         w.Write ((short)0);
482
483                         if ((ms.Position % 4) != 0)
484                                 w.Write ((short)0);
485
486                         w.Write ((short)file_lang);
487                         w.Write ((short)file_codepage);
488
489                         patch_length (w, var_pos);
490
491                         patch_length (w, var_file_info_pos);
492
493                         // StringFileInfo
494                         long string_file_info_pos = ms.Position;
495                         w.Write ((short)0);
496                         w.Write ((short)0);
497                         w.Write ((short)1);
498                         w.Write ("StringFileInfo".ToCharArray ());
499
500                         emit_padding (w);
501
502                         // StringTable
503                         long string_table_pos = ms.Position;
504                         w.Write ((short)0);
505                         w.Write ((short)0);
506                         w.Write ((short)1);
507                         w.Write (String.Format ("{0:x4}{1:x4}", file_lang, file_codepage).ToCharArray ());
508
509                         emit_padding (w);
510
511                         // Strings
512                         foreach (string key in properties.Keys) {
513                                 string value = (string)properties [key];
514
515                                 long string_pos = ms.Position;
516                                 w.Write ((short)0);
517                                 w.Write ((short)(value.ToCharArray ().Length + 1));
518                                 w.Write ((short)1);
519                                 w.Write (key.ToCharArray ());
520                                 w.Write ((short)0);
521
522                                 emit_padding (w);
523
524                                 w.Write (value.ToCharArray ());
525                                 w.Write ((short)0);
526
527                                 emit_padding (w);
528
529                                 patch_length (w, string_pos);
530                         }
531
532                         patch_length (w, string_table_pos);
533
534                         patch_length (w, string_file_info_pos);
535
536                         patch_length (w, 0);
537                 }
538         }
539 }
540
541 internal class Win32ResFileReader {
542
543         Stream res_file;
544
545         public Win32ResFileReader (Stream s) {
546                 res_file = s;
547         }
548
549         int read_int16 () {
550                 int b1 = res_file.ReadByte ();
551                 int b2 = res_file.ReadByte ();
552
553                 if ((b1 == -1) || (b2 == -1))
554                         return -1;
555                 else
556                         return b1 | (b2 << 8);
557         }
558
559         int read_int32 () {
560                 int w1 = read_int16 ();
561                 int w2 = read_int16 ();
562
563                 if ((w1 == -1) || (w2 == -1))
564                         return -1;
565                 return w1 | (w2 << 16);
566         }
567
568         private void read_padding () {
569                 while ((res_file.Position % 4) != 0)
570                         read_int16 ();
571         }
572
573         NameOrId read_ordinal () {
574                 int i = read_int16 ();
575                 if ((i & 0xffff) != 0) {
576                         int j = read_int16 ();
577                         return new NameOrId (j);
578                 }
579                 else {
580                         byte[] chars = new byte [16];
581                         int pos = 0;
582
583                         while (true) {
584                                 int j = read_int16 ();
585                                 if (j == 0)
586                                         break;
587                                 if (pos == chars.Length) {
588                                         byte[] new_chars = new byte [chars.Length * 2];
589                                         Array.Copy (chars, new_chars, chars.Length);
590                                         chars = new_chars;
591                                 }
592                                 chars [pos] = (byte)(j >> 8);
593                                 chars [pos + 1] = (byte)(j & 0xff);
594                                 pos += 2;
595                         }
596
597                         return new NameOrId (new String (Encoding.Unicode.GetChars (chars, 0, pos)));
598                 }                                       
599         }
600
601         public ICollection ReadResources () {
602                 ArrayList resources = new ArrayList ();
603
604                 /* 
605                  * We can't use a BinaryReader since we have to keep track of the 
606                  * stream position for padding.
607                  */
608
609                 while (true) {
610
611                         read_padding ();
612
613                         int data_size = read_int32 ();
614
615                         if (data_size == -1)
616                                 /* EOF */
617                                 break;
618
619                         int header_size = read_int32 ();
620                         NameOrId type = read_ordinal ();
621                         NameOrId name = read_ordinal ();
622
623                         read_padding ();
624
625                         //int data_version = 
626                         read_int32 ();
627                         //int memory_flags =
628                         read_int16 ();
629                         int language_id = read_int16 ();
630                         //int version =
631                         read_int32 ();
632                         //int characteristics =
633                         read_int32 ();
634
635                         if (data_size == 0)
636                                 /* Empty resource entry */
637                                 continue;
638
639                         byte[] data = new byte [data_size];
640                         res_file.Read (data, 0, data_size);
641
642                         resources.Add (new Win32EncodedResource (type, name, language_id, data));
643                 }
644
645                 return resources;
646         }
647 }
648
649 //
650 // This class represents one icon image in an .ico file
651 //
652 internal class ICONDIRENTRY {
653
654         public byte bWidth;
655         public byte bHeight;
656         public byte bColorCount;
657         public byte bReserved;
658         public Int16 wPlanes;
659         public Int16 wBitCount;
660         public Int32 dwBytesInRes;
661         public Int32 dwImageOffset;
662
663         public byte[] image;
664
665         public override string ToString () {
666                 return "ICONDIRENTRY (" + bWidth + "x" + bHeight + " " + wBitCount + " bpp)";
667         }
668 }
669
670 //
671 // This class represents a Reader for Win32 .ico files
672 //
673 internal class Win32IconFileReader {
674
675         Stream iconFile;
676
677         public Win32IconFileReader (Stream s) {
678                 iconFile = s;
679         }
680
681         public ICONDIRENTRY[] ReadIcons () {
682                 ICONDIRENTRY[] icons = null;
683
684                 using (BinaryReader r = new BinaryReader (iconFile)) {
685                         int idReserved = r.ReadInt16 ();
686                         int idType = r.ReadInt16 ();
687                         if ((idReserved != 0) || (idType != 1))
688                                 throw new Exception ("Invalid .ico file format");
689                         long nitems = r.ReadInt16 ();
690
691                         icons = new ICONDIRENTRY [nitems];
692
693                         for (int i = 0; i < nitems; ++i) {
694                                 ICONDIRENTRY entry = new ICONDIRENTRY ();
695
696                                 entry.bWidth = r.ReadByte ();
697                                 entry.bHeight = r.ReadByte ();
698                                 entry.bColorCount = r.ReadByte ();
699                                 entry.bReserved = r.ReadByte ();
700                                 entry.wPlanes = r.ReadInt16 ();
701                                 entry.wBitCount = r.ReadInt16 ();
702                                 int dwBytesInRes = r.ReadInt32 ();
703                                 int dwImageOffset = r.ReadInt32 ();
704
705                                 /* Read image */
706                                 entry.image = new byte [dwBytesInRes];
707
708                                 long pos = iconFile.Position;
709                                 iconFile.Position = dwImageOffset;
710                                 iconFile.Read (entry.image, 0, dwBytesInRes);
711                                 iconFile.Position = pos;
712
713                                 /* 
714                                  * The wPlanes and wBitCount members in the ICONDIRENTRY
715                                  * structure can be 0, so we set them from the BITMAPINFOHEADER
716                                  * structure that follows
717                                  */
718
719                                 if (entry.wPlanes == 0)
720                                         entry.wPlanes = (short)(entry.image [12] | (entry.image [13] << 8));
721                                 if (entry.wBitCount == 0)
722                                         entry.wBitCount = (short)(entry.image [14] | (entry.image [15] << 8));
723
724                                 icons [i] = entry;
725                         }
726
727                         return icons;
728                 }
729         }
730 }       
731
732 }