Add a more functional (i.e. fewer-stubs) implementation of System.Data.Linq.
[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 #if !NET_2_0
240                 "FileDescription",
241 #endif
242                 "FileVersion",
243                 "InternalName",
244 #if !NET_2_0
245                 "LegalCopyright",
246 #endif
247                 "LegalTrademarks",
248                 "OriginalFilename",
249                 "ProductName",
250                 "ProductVersion"
251         };
252
253         long signature;
254         int struct_version;
255         long file_version;
256         long product_version;
257         int file_flags_mask;
258         int file_flags;
259         int file_os;
260         int file_type;
261         int file_subtype;
262         long file_date;
263
264         int file_lang;
265         int file_codepage;
266
267         Hashtable properties;
268
269         public Win32VersionResource (int id, int language, bool compilercontext) : base (Win32ResourceType.RT_VERSION, id, language) {
270                 // Initialize non-public members to the usual values used in
271                 // resources
272                 signature = 0xfeef04bd;
273                 struct_version = 1 << 16; /* 1.0 */
274                 file_flags_mask = 63;
275                 file_flags = 0;
276                 file_os = 4; /* VOS_WIN32 */
277                 file_type = 2;
278                 file_subtype = 0;
279                 file_date = 0;
280
281                 file_lang = compilercontext ? 0x00 : 0x7f;
282                 file_codepage = 1200;
283
284                 properties = new Hashtable ();
285
286 #if NET_2_0
287                 string defaultvalue = compilercontext ? string.Empty : " ";
288 #else
289                 string defaultvalue = " ";
290 #endif
291
292                 // Well known properties
293                 foreach (string s in WellKnownProperties)
294                         // The value of properties can't be empty
295                         properties [s] = defaultvalue;
296
297 #if NET_2_0
298                 LegalCopyright = " ";
299                 FileDescription = " ";
300 #endif
301         }
302
303         public string Version {
304                 get {
305                         return 
306                                 "" + (file_version >> 48) + 
307                                 "." + ((file_version >> 32) & 0xffff) + 
308                                 "." + ((file_version >> 16) & 0xffff) +
309                                 "." + ((file_version >> 0) & 0xffff);
310                 }
311
312                 set {
313 #if NET_2_0
314                         long[] ver = new long [4] { 0, 0, 0, 0 };
315 #else
316                         long [] ver = new long [4] { 0, 0xffff, 0xffff, 0xffff };
317 #endif
318                         if (value != null) {
319                                 string[] parts = value.Split ('.');
320
321                                 try {
322                                         for (int i = 0; i < parts.Length; ++i) {
323                                                 if (i < ver.Length)
324                                                         ver [i] = Int32.Parse (parts [i]);
325                                         }
326                                 } catch (FormatException) {
327                                 }
328                         }
329
330                         file_version = (ver [0] << 48) | (ver [1] << 32) | (ver [2] << 16) + ver [3];
331                         properties ["FileVersion"] = Version;
332                 }
333         }
334
335         public virtual string this [string key] {
336                 set {
337                         properties [key] = value;
338                 }
339         }
340
341         // Accessors for well known properties
342
343         public virtual string Comments {
344                 get {
345                         return (string)properties ["Comments"];
346                 }
347                 set {
348                         properties ["Comments"] = value == String.Empty ? " " : value;
349                 }
350         }
351
352         public virtual string CompanyName {
353                 get {
354                         return (string)properties ["CompanyName"];
355                 }
356                 set {
357                         properties ["CompanyName"] = value == String.Empty ? " " : value;
358                 }
359         }
360
361         public virtual string LegalCopyright {
362                 get {
363                         return (string)properties ["LegalCopyright"];
364                 }
365                 set {
366                         properties ["LegalCopyright"] = value == String.Empty ? " " : value;
367                 }
368         }
369
370         public virtual string LegalTrademarks {
371                 get {
372                         return (string)properties ["LegalTrademarks"];
373                 }
374                 set {
375                         properties ["LegalTrademarks"] = value == String.Empty ? " " : value;
376                 }
377         }
378
379         public virtual string OriginalFilename {
380                 get {
381                         return (string)properties ["OriginalFilename"];
382                 }
383                 set {
384                         properties ["OriginalFilename"] = value == String.Empty ? " " : value;
385                 }
386         }
387
388         public virtual string ProductName {
389                 get {
390                         return (string)properties ["ProductName"];
391                 }
392                 set {
393                         properties ["ProductName"] = value == String.Empty ? " " : value;
394                 }
395         }
396
397         public virtual string ProductVersion {
398                 get {
399                         return (string)properties ["ProductVersion"];
400                 }
401                 set {
402                         if (value == null || value.Length == 0)
403                                 value = " ";
404
405                         long [] ver = new long [4] { 0, 0, 0, 0 };
406
407                         string [] parts = value.Split ('.');
408
409                         try {
410                                 for (int i = 0; i < parts.Length; ++i) {
411                                         if (i < ver.Length)
412                                                 ver [i] = Int32.Parse (parts [i]);
413                                 }
414                         } catch (FormatException) {
415                         }
416
417                         properties ["ProductVersion"] = value;
418                         product_version = (ver [0] << 48) | (ver [1] << 32) | (ver [2] << 16) + ver [3];
419                 }
420         }
421
422         public virtual string InternalName {
423                 get {
424                         return (string)properties ["InternalName"];
425                 }
426                 set {
427                         properties ["InternalName"] = value == String.Empty ? " " : value;
428                 }
429         }
430
431         public virtual string FileDescription {
432                 get {
433                         return (string)properties ["FileDescription"];
434                 }
435                 set {
436                         properties ["FileDescription"] = value == String.Empty ? " " : value;
437                 }
438         }
439
440         public virtual int FileLanguage {
441                 get { return file_lang; }
442                 set { file_lang = value; }
443         }
444
445         public virtual string FileVersion {
446                 get {
447                         return (string)properties ["FileVersion"];
448                 }
449                 set {
450                         if (value == null || value.Length == 0)
451                                 value = " ";
452
453                         long[] ver = new long [4] { 0, 0, 0, 0 };
454                         string[] parts = value.Split ('.');
455
456                         try {
457                                 for (int i = 0; i < parts.Length; ++i) {
458                                         if (i < ver.Length)
459                                                 ver [i] = Int32.Parse (parts [i]);
460                                 }
461                         } catch (FormatException) {
462                         }
463
464                         properties ["FileVersion"] = value;
465                         file_version = (ver [0] << 48) | (ver [1] << 32) | (ver [2] << 16) + ver [3];
466                 }
467         }
468
469         private void emit_padding (BinaryWriter w) {
470                 Stream ms = w.BaseStream;
471
472                 if ((ms.Position % 4) != 0)
473                         w.Write ((short)0);
474         }
475
476         private void patch_length (BinaryWriter w, long len_pos) {
477                 Stream ms = w.BaseStream;
478
479                 long pos = ms.Position;
480                 ms.Position = len_pos;
481                 w.Write ((short)(pos - len_pos));
482                 ms.Position = pos;
483         }
484
485         public override void WriteTo (Stream ms)
486         {
487                 using (BinaryWriter w = new BinaryWriter (ms, Encoding.Unicode)) {
488                         //
489                         // See the documentation for the VS_VERSIONINFO structure and
490                         // its children on MSDN
491                         //
492
493                         // VS_VERSIONINFO
494                         w.Write ((short)0);
495                         w.Write ((short)0x34);
496                         w.Write ((short)0);
497                         w.Write ("VS_VERSION_INFO".ToCharArray ());
498                         w.Write ((short)0);
499
500                         emit_padding (w);
501
502                         // VS_FIXEDFILEINFO
503                         w.Write ((uint)signature);
504                         w.Write ((int)struct_version);
505                         w.Write ((int)(file_version >> 32));
506                         w.Write ((int)((file_version & 0xffffffff)));
507
508                         w.Write ((int)(product_version >> 32));
509                         w.Write ((int)(product_version & 0xffffffff));
510                         w.Write ((int)file_flags_mask);
511                         w.Write ((int)file_flags);
512                         w.Write ((int)file_os);
513                         w.Write ((int)file_type);
514                         w.Write ((int)file_subtype);
515                         w.Write ((int)(file_date >> 32));
516                         w.Write ((int)(file_date & 0xffffffff));
517
518                         emit_padding (w);
519
520                         // VarFileInfo
521                         long var_file_info_pos = ms.Position;
522                         w.Write ((short)0);
523                         w.Write ((short)0);
524                         w.Write ((short)1);
525                         w.Write ("VarFileInfo".ToCharArray ());
526                         w.Write ((short)0);
527
528                         if ((ms.Position % 4) != 0)
529                                 w.Write ((short)0);
530
531                         // Var
532                         long var_pos = ms.Position;
533                         w.Write ((short)0);
534                         w.Write ((short)4);
535                         w.Write ((short)0);
536                         w.Write ("Translation".ToCharArray ());
537                         w.Write ((short)0);
538
539                         if ((ms.Position % 4) != 0)
540                                 w.Write ((short)0);
541
542                         w.Write ((short)file_lang);
543                         w.Write ((short)file_codepage);
544
545                         patch_length (w, var_pos);
546
547                         patch_length (w, var_file_info_pos);
548
549                         // StringFileInfo
550                         long string_file_info_pos = ms.Position;
551                         w.Write ((short)0);
552                         w.Write ((short)0);
553                         w.Write ((short)1);
554                         w.Write ("StringFileInfo".ToCharArray ());
555
556                         emit_padding (w);
557
558                         // StringTable
559                         long string_table_pos = ms.Position;
560                         w.Write ((short)0);
561                         w.Write ((short)0);
562                         w.Write ((short)1);
563                         w.Write (String.Format ("{0:x4}{1:x4}", file_lang, file_codepage).ToCharArray ());
564
565                         emit_padding (w);
566
567                         // Strings
568                         foreach (string key in properties.Keys) {
569                                 string value = (string)properties [key];
570
571                                 long string_pos = ms.Position;
572                                 w.Write ((short)0);
573                                 w.Write ((short)(value.ToCharArray ().Length + 1));
574                                 w.Write ((short)1);
575                                 w.Write (key.ToCharArray ());
576                                 w.Write ((short)0);
577
578                                 emit_padding (w);
579
580                                 w.Write (value.ToCharArray ());
581                                 w.Write ((short)0);
582
583                                 emit_padding (w);
584
585                                 patch_length (w, string_pos);
586                         }
587
588                         patch_length (w, string_table_pos);
589
590                         patch_length (w, string_file_info_pos);
591
592                         patch_length (w, 0);
593                 }
594         }
595 }
596
597 internal class Win32ResFileReader {
598
599         Stream res_file;
600
601         public Win32ResFileReader (Stream s) {
602                 res_file = s;
603         }
604
605         int read_int16 () {
606                 int b1 = res_file.ReadByte ();
607                 if (b1 == -1)
608                         return -1;
609
610                 int b2 = res_file.ReadByte ();
611                 if (b2 == -1)
612                         return -1;
613
614                 return b1 | (b2 << 8);
615         }
616
617         int read_int32 () {
618                 int w1 = read_int16 ();
619                 if (w1 == -1)
620                         return -1;
621                 int w2 = read_int16 ();
622                 if (w2 == -1)
623                         return -1;
624
625                 return w1 | (w2 << 16);
626         }
627
628         private void read_padding () {
629                 while ((res_file.Position % 4) != 0)
630                         read_int16 ();
631         }
632
633         NameOrId read_ordinal () {
634                 int i = read_int16 ();
635                 if ((i & 0xffff) != 0) {
636                         int j = read_int16 ();
637                         return new NameOrId (j);
638                 }
639                 else {
640                         byte[] chars = new byte [16];
641                         int pos = 0;
642
643                         while (true) {
644                                 int j = read_int16 ();
645                                 if (j == 0)
646                                         break;
647                                 if (pos == chars.Length) {
648                                         byte[] new_chars = new byte [chars.Length * 2];
649                                         Array.Copy (chars, new_chars, chars.Length);
650                                         chars = new_chars;
651                                 }
652                                 chars [pos] = (byte)(j >> 8);
653                                 chars [pos + 1] = (byte)(j & 0xff);
654                                 pos += 2;
655                         }
656
657                         return new NameOrId (new String (Encoding.Unicode.GetChars (chars, 0, pos)));
658                 }                                       
659         }
660
661         public ICollection ReadResources () {
662                 ArrayList resources = new ArrayList ();
663
664                 /* 
665                  * We can't use a BinaryReader since we have to keep track of the 
666                  * stream position for padding.
667                  */
668
669                 while (true) {
670
671                         read_padding ();
672
673                         int data_size = read_int32 ();
674
675                         if (data_size == -1)
676                                 /* EOF */
677                                 break;
678
679                         //int header_size = 
680                         read_int32 ();
681                         NameOrId type = read_ordinal ();
682                         NameOrId name = read_ordinal ();
683
684                         read_padding ();
685
686                         //int data_version = 
687                         read_int32 ();
688                         //int memory_flags =
689                         read_int16 ();
690                         int language_id = read_int16 ();
691                         //int version =
692                         read_int32 ();
693                         //int characteristics =
694                         read_int32 ();
695
696                         if (data_size == 0)
697                                 /* Empty resource entry */
698                                 continue;
699
700                         byte[] data = new byte [data_size];
701                         res_file.Read (data, 0, data_size);
702
703                         resources.Add (new Win32EncodedResource (type, name, language_id, data));
704                 }
705
706                 return resources;
707         }
708 }
709
710 //
711 // This class represents one icon image in an .ico file
712 //
713 internal class ICONDIRENTRY {
714
715 #pragma warning disable 649
716         public byte bWidth;
717         public byte bHeight;
718         public byte bColorCount;
719         public byte bReserved;
720         public Int16 wPlanes;
721         public Int16 wBitCount;
722         public Int32 dwBytesInRes;
723         public Int32 dwImageOffset;
724 #pragma warning restore 649
725         public byte[] image;
726
727         public override string ToString () {
728                 return "ICONDIRENTRY (" + bWidth + "x" + bHeight + " " + wBitCount + " bpp)";
729         }
730 }
731
732 //
733 // This class represents a Reader for Win32 .ico files
734 //
735 internal class Win32IconFileReader {
736
737         Stream iconFile;
738
739         public Win32IconFileReader (Stream s) {
740                 iconFile = s;
741         }
742
743         public ICONDIRENTRY[] ReadIcons () {
744                 ICONDIRENTRY[] icons = null;
745
746                 using (BinaryReader r = new BinaryReader (iconFile)) {
747                         int idReserved = r.ReadInt16 ();
748                         int idType = r.ReadInt16 ();
749                         if ((idReserved != 0) || (idType != 1))
750                                 throw new Exception ("Invalid .ico file format");
751                         long nitems = r.ReadInt16 ();
752
753                         icons = new ICONDIRENTRY [nitems];
754
755                         for (int i = 0; i < nitems; ++i) {
756                                 ICONDIRENTRY entry = new ICONDIRENTRY ();
757
758                                 entry.bWidth = r.ReadByte ();
759                                 entry.bHeight = r.ReadByte ();
760                                 entry.bColorCount = r.ReadByte ();
761                                 entry.bReserved = r.ReadByte ();
762                                 entry.wPlanes = r.ReadInt16 ();
763                                 entry.wBitCount = r.ReadInt16 ();
764                                 int dwBytesInRes = r.ReadInt32 ();
765                                 int dwImageOffset = r.ReadInt32 ();
766
767                                 /* Read image */
768                                 entry.image = new byte [dwBytesInRes];
769
770                                 long pos = iconFile.Position;
771                                 iconFile.Position = dwImageOffset;
772                                 iconFile.Read (entry.image, 0, dwBytesInRes);
773                                 iconFile.Position = pos;
774
775                                 /* 
776                                  * The wPlanes and wBitCount members in the ICONDIRENTRY
777                                  * structure can be 0, so we set them from the BITMAPINFOHEADER
778                                  * structure that follows
779                                  */
780
781                                 if (entry.wPlanes == 0)
782                                         entry.wPlanes = (short)(entry.image [12] | (entry.image [13] << 8));
783                                 if (entry.wBitCount == 0)
784                                         entry.wBitCount = (short)(entry.image [14] | (entry.image [15] << 8));
785
786                                 icons [i] = entry;
787                         }
788
789                         return icons;
790                 }
791         }
792 }       
793
794 }