2 // System.Resources/Win32Resources.cs
5 // Zoltan Varga (vargaz@freemail.hu)
7 // (C) 2003 Ximian, Inc. http://www.ximian.com
9 // An incomplete set of classes for manipulating Win32 resources
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
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.
36 using System
.Collections
;
37 using System
.Globalization
;
41 namespace System
.Resources
{
44 internal enum Win32ResourceType
{
67 internal class NameOrId
{
71 public NameOrId (string name
) {
75 public NameOrId (int id
) {
97 public override string ToString () {
99 return "Name(" + name
+ ")";
101 return "Id(" + id
+ ")";
105 internal abstract class Win32Resource
{
111 internal Win32Resource (NameOrId type
, NameOrId name
, int language
) {
114 this.language
= language
;
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
;
123 public Win32ResourceType ResourceType
{
126 return (Win32ResourceType
)(-1);
128 return (Win32ResourceType
)type
.Id
;
132 public NameOrId Name
{
138 public NameOrId Type
{
144 public int Language
{
150 public abstract void WriteTo (Stream s
);
152 public override string ToString () {
153 return "Win32Resource (Kind=" + ResourceType
+ ", Name=" + name
+ ")";
158 // This class represents a Win32 resource in encoded format
160 internal class Win32EncodedResource
: Win32Resource
{
164 internal Win32EncodedResource (NameOrId type
, NameOrId name
, int language
, byte[] data
) : base (type
, name
, language
) {
174 public override void WriteTo (Stream s
) {
175 s
.Write (data
, 0, data
.Length
);
180 // This class represents a Win32 ICON resource
182 internal class Win32IconResource
: Win32Resource
{
186 public Win32IconResource (int id
, int language
, ICONDIRENTRY icon
) : base (Win32ResourceType
.RT_ICON
, id
, language
) {
190 public ICONDIRENTRY Icon
{
196 public override void WriteTo (Stream s
) {
197 s
.Write (icon
.image
, 0, icon
.image
.Length
);
201 internal class Win32GroupIconResource
: Win32Resource
{
203 Win32IconResource
[] icons
;
205 public Win32GroupIconResource (int id
, int language
, Win32IconResource
[] icons
) : base (Win32ResourceType
.RT_GROUP_ICON
, id
, language
) {
209 public override void WriteTo (Stream s
) {
210 using (BinaryWriter w
= new BinaryWriter (s
)) {
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
;
218 w
.Write (entry
.bWidth
);
219 w
.Write (entry
.bHeight
);
220 w
.Write (entry
.bColorCount
);
222 w
.Write (entry
.wPlanes
);
223 w
.Write (entry
.wBitCount
);
224 w
.Write ((int)entry
.image
.Length
);
225 w
.Write ((short)icon
.Name
.Id
);
232 // This class represents a Win32 VERSION resource
234 internal class Win32VersionResource
: Win32Resource
{
236 public string[] WellKnownProperties
= {
256 long product_version
;
267 Hashtable properties
;
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
272 signature
= 0xfeef04bd;
273 struct_version
= 1 << 16; /* 1.0 */
274 file_flags_mask
= 63;
276 file_os
= 4; /* VOS_WIN32 */
281 file_lang
= compilercontext
? 0x00 : 0x7f;
282 file_codepage
= 1200;
284 properties
= new Hashtable ();
287 string defaultvalue
= compilercontext
? string.Empty
: " ";
289 string defaultvalue
= " ";
292 // Well known properties
293 foreach (string s
in WellKnownProperties
)
294 // The value of properties can't be empty
295 properties
[s
] = defaultvalue
;
298 LegalCopyright
= " ";
299 FileDescription
= " ";
303 public string Version
{
306 "" + (file_version
>> 48) +
307 "." + ((file_version
>> 32) & 0xffff) +
308 "." + ((file_version
>> 16) & 0xffff) +
309 "." + ((file_version
>> 0) & 0xffff);
314 long[] ver
= new long [4] { 0, 0, 0, 0 }
;
316 long [] ver
= new long [4] { 0, 0xffff, 0xffff, 0xffff }
;
319 string[] parts
= value.Split ('.');
322 for (int i
= 0; i
< parts
.Length
; ++i
) {
324 ver
[i
] = Int32
.Parse (parts
[i
]);
326 } catch (FormatException
) {
330 file_version
= (ver
[0] << 48) | (ver
[1] << 32) | (ver
[2] << 16) + ver
[3];
331 properties
["FileVersion"] = Version
;
335 public virtual string this [string key
] {
337 properties
[key
] = value;
341 // Accessors for well known properties
343 public virtual string Comments
{
345 return (string)properties
["Comments"];
348 properties
["Comments"] = value == String
.Empty
? " " : value;
352 public virtual string CompanyName
{
354 return (string)properties
["CompanyName"];
357 properties
["CompanyName"] = value == String
.Empty
? " " : value;
361 public virtual string LegalCopyright
{
363 return (string)properties
["LegalCopyright"];
366 properties
["LegalCopyright"] = value == String
.Empty
? " " : value;
370 public virtual string LegalTrademarks
{
372 return (string)properties
["LegalTrademarks"];
375 properties
["LegalTrademarks"] = value == String
.Empty
? " " : value;
379 public virtual string OriginalFilename
{
381 return (string)properties
["OriginalFilename"];
384 properties
["OriginalFilename"] = value == String
.Empty
? " " : value;
388 public virtual string ProductName
{
390 return (string)properties
["ProductName"];
393 properties
["ProductName"] = value == String
.Empty
? " " : value;
397 public virtual string ProductVersion
{
399 return (string)properties
["ProductVersion"];
402 if (value == null || value.Length
== 0)
405 long [] ver
= new long [4] { 0, 0, 0, 0 }
;
407 string [] parts
= value.Split ('.');
410 for (int i
= 0; i
< parts
.Length
; ++i
) {
412 ver
[i
] = Int32
.Parse (parts
[i
]);
414 } catch (FormatException
) {
417 properties
["ProductVersion"] = value;
418 product_version
= (ver
[0] << 48) | (ver
[1] << 32) | (ver
[2] << 16) + ver
[3];
422 public virtual string InternalName
{
424 return (string)properties
["InternalName"];
427 properties
["InternalName"] = value == String
.Empty
? " " : value;
431 public virtual string FileDescription
{
433 return (string)properties
["FileDescription"];
436 properties
["FileDescription"] = value == String
.Empty
? " " : value;
440 public virtual int FileLanguage
{
441 get { return file_lang; }
442 set { file_lang = value; }
445 public virtual string FileVersion
{
447 return (string)properties
["FileVersion"];
450 if (value == null || value.Length
== 0)
453 long[] ver
= new long [4] { 0, 0, 0, 0 }
;
454 string[] parts
= value.Split ('.');
457 for (int i
= 0; i
< parts
.Length
; ++i
) {
459 ver
[i
] = Int32
.Parse (parts
[i
]);
461 } catch (FormatException
) {
464 properties
["FileVersion"] = value;
465 file_version
= (ver
[0] << 48) | (ver
[1] << 32) | (ver
[2] << 16) + ver
[3];
469 private void emit_padding (BinaryWriter w
) {
470 Stream ms
= w
.BaseStream
;
472 if ((ms
.Position
% 4) != 0)
476 private void patch_length (BinaryWriter w
, long len_pos
) {
477 Stream ms
= w
.BaseStream
;
479 long pos
= ms
.Position
;
480 ms
.Position
= len_pos
;
481 w
.Write ((short)(pos
- len_pos
));
485 public override void WriteTo (Stream ms
)
487 using (BinaryWriter w
= new BinaryWriter (ms
, Encoding
.Unicode
)) {
489 // See the documentation for the VS_VERSIONINFO structure and
490 // its children on MSDN
495 w
.Write ((short)0x34);
497 w
.Write ("VS_VERSION_INFO".ToCharArray ());
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)));
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));
521 long var_file_info_pos
= ms
.Position
;
525 w
.Write ("VarFileInfo".ToCharArray ());
528 if ((ms
.Position
% 4) != 0)
532 long var_pos
= ms
.Position
;
536 w
.Write ("Translation".ToCharArray ());
539 if ((ms
.Position
% 4) != 0)
542 w
.Write ((short)file_lang
);
543 w
.Write ((short)file_codepage
);
545 patch_length (w
, var_pos
);
547 patch_length (w
, var_file_info_pos
);
550 long string_file_info_pos
= ms
.Position
;
554 w
.Write ("StringFileInfo".ToCharArray ());
559 long string_table_pos
= ms
.Position
;
563 w
.Write (String
.Format ("{0:x4}{1:x4}", file_lang
, file_codepage
).ToCharArray ());
568 foreach (string key
in properties
.Keys
) {
569 string value = (string)properties
[key
];
571 long string_pos
= ms
.Position
;
573 w
.Write ((short)(value.ToCharArray ().Length
+ 1));
575 w
.Write (key
.ToCharArray ());
580 w
.Write (value.ToCharArray ());
585 patch_length (w
, string_pos
);
588 patch_length (w
, string_table_pos
);
590 patch_length (w
, string_file_info_pos
);
597 internal class Win32ResFileReader
{
601 public Win32ResFileReader (Stream s
) {
606 int b1
= res_file
.ReadByte ();
610 int b2
= res_file
.ReadByte ();
614 return b1
| (b2
<< 8);
618 int w1
= read_int16 ();
621 int w2
= read_int16 ();
625 return w1
| (w2
<< 16);
628 private void read_padding () {
629 while ((res_file
.Position
% 4) != 0)
633 NameOrId
read_ordinal () {
634 int i
= read_int16 ();
635 if ((i
& 0xffff) != 0) {
636 int j
= read_int16 ();
637 return new NameOrId (j
);
640 byte[] chars
= new byte [16];
644 int j
= read_int16 ();
647 if (pos
== chars
.Length
) {
648 byte[] new_chars
= new byte [chars
.Length
* 2];
649 Array
.Copy (chars
, new_chars
, chars
.Length
);
652 chars
[pos
] = (byte)(j
>> 8);
653 chars
[pos
+ 1] = (byte)(j
& 0xff);
657 return new NameOrId (new String (Encoding
.Unicode
.GetChars (chars
, 0, pos
)));
661 public ICollection
ReadResources () {
662 ArrayList resources
= new ArrayList ();
665 * We can't use a BinaryReader since we have to keep track of the
666 * stream position for padding.
673 int data_size
= read_int32 ();
681 NameOrId type
= read_ordinal ();
682 NameOrId name
= read_ordinal ();
690 int language_id
= read_int16 ();
693 //int characteristics =
697 /* Empty resource entry */
700 byte[] data
= new byte [data_size
];
701 res_file
.Read (data
, 0, data_size
);
703 resources
.Add (new Win32EncodedResource (type
, name
, language_id
, data
));
711 // This class represents one icon image in an .ico file
713 internal class ICONDIRENTRY
{
715 #pragma warning disable 649
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
727 public override string ToString () {
728 return "ICONDIRENTRY (" + bWidth
+ "x" + bHeight
+ " " + wBitCount
+ " bpp)";
733 // This class represents a Reader for Win32 .ico files
735 internal class Win32IconFileReader
{
739 public Win32IconFileReader (Stream s
) {
743 public ICONDIRENTRY
[] ReadIcons () {
744 ICONDIRENTRY
[] icons
= null;
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 ();
753 icons
= new ICONDIRENTRY
[nitems
];
755 for (int i
= 0; i
< nitems
; ++i
) {
756 ICONDIRENTRY entry
= new ICONDIRENTRY ();
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 ();
768 entry
.image
= new byte [dwBytesInRes
];
770 long pos
= iconFile
.Position
;
771 iconFile
.Position
= dwImageOffset
;
772 iconFile
.Read (entry
.image
, 0, dwBytesInRes
);
773 iconFile
.Position
= pos
;
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
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));