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));