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
= {
250 long product_version
;
261 Hashtable properties
;
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
266 signature
= 0xfeef04bd;
267 struct_version
= 1 << 16; /* 1.0 */
268 file_flags_mask
= 63;
270 file_os
= 4; /* VOS_WIN32 */
275 file_lang
= compilercontext
? 0x00 : 0x7f;
276 file_codepage
= 1200;
278 properties
= new Hashtable ();
280 string defaultvalue
= compilercontext
? string.Empty
: " ";
282 // Well known properties
283 foreach (string s
in WellKnownProperties
)
284 // The value of properties can't be empty
285 properties
[s
] = defaultvalue
;
287 LegalCopyright
= " ";
288 FileDescription
= " ";
291 public string Version
{
294 "" + (file_version
>> 48) +
295 "." + ((file_version
>> 32) & 0xffff) +
296 "." + ((file_version
>> 16) & 0xffff) +
297 "." + ((file_version
>> 0) & 0xffff);
301 long[] ver
= new long [4] { 0, 0, 0, 0 }
;
303 string[] parts
= value.Split ('.');
306 for (int i
= 0; i
< parts
.Length
; ++i
) {
308 ver
[i
] = Int32
.Parse (parts
[i
]);
310 } catch (FormatException
) {
314 file_version
= (ver
[0] << 48) | (ver
[1] << 32) | (ver
[2] << 16) + ver
[3];
315 properties
["FileVersion"] = Version
;
319 public virtual string this [string key
] {
321 properties
[key
] = value;
325 // Accessors for well known properties
327 public virtual string Comments
{
329 return (string)properties
["Comments"];
332 properties
["Comments"] = value == String
.Empty
? " " : value;
336 public virtual string CompanyName
{
338 return (string)properties
["CompanyName"];
341 properties
["CompanyName"] = value == String
.Empty
? " " : value;
345 public virtual string LegalCopyright
{
347 return (string)properties
["LegalCopyright"];
350 properties
["LegalCopyright"] = value == String
.Empty
? " " : value;
354 public virtual string LegalTrademarks
{
356 return (string)properties
["LegalTrademarks"];
359 properties
["LegalTrademarks"] = value == String
.Empty
? " " : value;
363 public virtual string OriginalFilename
{
365 return (string)properties
["OriginalFilename"];
368 properties
["OriginalFilename"] = value == String
.Empty
? " " : value;
372 public virtual string ProductName
{
374 return (string)properties
["ProductName"];
377 properties
["ProductName"] = value == String
.Empty
? " " : value;
381 public virtual string ProductVersion
{
383 return (string)properties
["ProductVersion"];
386 if (value == null || value.Length
== 0)
389 long [] ver
= new long [4] { 0, 0, 0, 0 }
;
391 string [] parts
= value.Split ('.');
394 for (int i
= 0; i
< parts
.Length
; ++i
) {
396 ver
[i
] = Int32
.Parse (parts
[i
]);
398 } catch (FormatException
) {
401 properties
["ProductVersion"] = value;
402 product_version
= (ver
[0] << 48) | (ver
[1] << 32) | (ver
[2] << 16) + ver
[3];
406 public virtual string InternalName
{
408 return (string)properties
["InternalName"];
411 properties
["InternalName"] = value == String
.Empty
? " " : value;
415 public virtual string FileDescription
{
417 return (string)properties
["FileDescription"];
420 properties
["FileDescription"] = value == String
.Empty
? " " : value;
424 public virtual int FileLanguage
{
425 get { return file_lang; }
426 set { file_lang = value; }
429 public virtual string FileVersion
{
431 return (string)properties
["FileVersion"];
434 if (value == null || value.Length
== 0)
437 long[] ver
= new long [4] { 0, 0, 0, 0 }
;
438 string[] parts
= value.Split ('.');
441 for (int i
= 0; i
< parts
.Length
; ++i
) {
443 ver
[i
] = Int32
.Parse (parts
[i
]);
445 } catch (FormatException
) {
448 properties
["FileVersion"] = value;
449 file_version
= (ver
[0] << 48) | (ver
[1] << 32) | (ver
[2] << 16) + ver
[3];
453 private void emit_padding (BinaryWriter w
) {
454 Stream ms
= w
.BaseStream
;
456 if ((ms
.Position
% 4) != 0)
460 private void patch_length (BinaryWriter w
, long len_pos
) {
461 Stream ms
= w
.BaseStream
;
463 long pos
= ms
.Position
;
464 ms
.Position
= len_pos
;
465 w
.Write ((short)(pos
- len_pos
));
469 public override void WriteTo (Stream ms
)
471 using (BinaryWriter w
= new BinaryWriter (ms
, Encoding
.Unicode
)) {
473 // See the documentation for the VS_VERSIONINFO structure and
474 // its children on MSDN
479 w
.Write ((short)0x34);
481 w
.Write ("VS_VERSION_INFO".ToCharArray ());
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)));
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));
505 long var_file_info_pos
= ms
.Position
;
509 w
.Write ("VarFileInfo".ToCharArray ());
512 if ((ms
.Position
% 4) != 0)
516 long var_pos
= ms
.Position
;
520 w
.Write ("Translation".ToCharArray ());
523 if ((ms
.Position
% 4) != 0)
526 w
.Write ((short)file_lang
);
527 w
.Write ((short)file_codepage
);
529 patch_length (w
, var_pos
);
531 patch_length (w
, var_file_info_pos
);
534 long string_file_info_pos
= ms
.Position
;
538 w
.Write ("StringFileInfo".ToCharArray ());
543 long string_table_pos
= ms
.Position
;
547 w
.Write (String
.Format ("{0:x4}{1:x4}", file_lang
, file_codepage
).ToCharArray ());
552 foreach (string key
in properties
.Keys
) {
553 string value = (string)properties
[key
];
555 long string_pos
= ms
.Position
;
557 w
.Write ((short)(value.ToCharArray ().Length
+ 1));
559 w
.Write (key
.ToCharArray ());
564 w
.Write (value.ToCharArray ());
569 patch_length (w
, string_pos
);
572 patch_length (w
, string_table_pos
);
574 patch_length (w
, string_file_info_pos
);
581 internal class Win32ResFileReader
{
585 public Win32ResFileReader (Stream s
) {
590 int b1
= res_file
.ReadByte ();
594 int b2
= res_file
.ReadByte ();
598 return b1
| (b2
<< 8);
602 int w1
= read_int16 ();
605 int w2
= read_int16 ();
609 return w1
| (w2
<< 16);
612 private bool read_padding () {
613 while ((res_file
.Position
% 4) != 0){
614 if (read_int16 () == -1)
620 NameOrId
read_ordinal () {
621 int i
= read_int16 ();
622 if ((i
& 0xffff) != 0) {
623 int j
= read_int16 ();
624 return new NameOrId (j
);
627 byte[] chars
= new byte [16];
631 int j
= read_int16 ();
634 if (pos
== chars
.Length
) {
635 byte[] new_chars
= new byte [chars
.Length
* 2];
636 Array
.Copy (chars
, new_chars
, chars
.Length
);
639 chars
[pos
] = (byte)(j
>> 8);
640 chars
[pos
+ 1] = (byte)(j
& 0xff);
644 return new NameOrId (new String (Encoding
.Unicode
.GetChars (chars
, 0, pos
)));
648 public ICollection
ReadResources () {
649 ArrayList resources
= new ArrayList ();
652 * We can't use a BinaryReader since we have to keep track of the
653 * stream position for padding.
658 if (!read_padding ())
661 int data_size
= read_int32 ();
669 NameOrId type
= read_ordinal ();
670 NameOrId name
= read_ordinal ();
672 if (!read_padding ())
679 int language_id
= read_int16 ();
682 //int characteristics =
686 /* Empty resource entry */
689 byte[] data
= new byte [data_size
];
690 if (res_file
.Read (data
, 0, data_size
) != data_size
)
693 resources
.Add (new Win32EncodedResource (type
, name
, language_id
, data
));
701 // This class represents one icon image in an .ico file
703 internal class ICONDIRENTRY
{
705 #pragma warning disable 649
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
717 public override string ToString () {
718 return "ICONDIRENTRY (" + bWidth
+ "x" + bHeight
+ " " + wBitCount
+ " bpp)";
723 // This class represents a Reader for Win32 .ico files
725 internal class Win32IconFileReader
{
729 public Win32IconFileReader (Stream s
) {
733 public ICONDIRENTRY
[] ReadIcons () {
734 ICONDIRENTRY
[] icons
= null;
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 ();
743 icons
= new ICONDIRENTRY
[nitems
];
745 for (int i
= 0; i
< nitems
; ++i
) {
746 ICONDIRENTRY entry
= new ICONDIRENTRY ();
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 ();
758 entry
.image
= new byte [dwBytesInRes
];
760 long pos
= iconFile
.Position
;
761 iconFile
.Position
= dwImageOffset
;
762 iconFile
.Read (entry
.image
, 0, dwBytesInRes
);
763 iconFile
.Position
= pos
;
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
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));