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