2004-04-01 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / class / corlib / System.Resources / Win32Resources.cs
index b36c2e9e9b286a786c56f35071b76f9d169406db..86ac91517fa5a211bb363a9360714216e9c5a863 100644 (file)
 //
 // (C) 2003 Ximian, Inc.  http://www.ximian.com
 //
+// An incomplete set of classes for manipulating Win32 resources
+//
 
 using System;
 using System.Collections;
 using System.IO;
 using System.Text;
 
+namespace System.Resources {
+
+
+internal enum Win32ResourceType {
+       RT_CURSOR = 1,
+       RT_FONT = 8,
+       RT_BITMAP = 2,
+       RT_ICON = 3,
+       RT_MENU = 4,
+       RT_DIALOG = 5,
+       RT_STRING = 6,
+       RT_FONTDIR = 7,
+       RT_ACCELERATOR = 9,
+       RT_RCDATA = 10,
+       RT_MESSAGETABLE = 11,
+       RT_GROUP_CURSOR = 12,
+       RT_GROUP_ICON = 14,
+       RT_VERSION = 16,
+       RT_DLGINCLUDE = 17,
+       RT_PLUGPLAY = 19,
+       RT_VXD = 20,
+       RT_ANICURSOR = 21,
+       RT_ANIICON = 22,
+       RT_HTML = 23,
+}
+
+internal class NameOrId {
+       string name;
+       int id;
+
+       public NameOrId (string name) {
+               this.name = name;
+       }
+
+       public NameOrId (int id) {
+               this.id = id;
+       }
+
+       public bool IsName {
+               get {
+                       return name != null;
+               }
+       }
+
+       public string Name {
+               get {
+                       return name;
+               }
+       }
+
+       public int Id {
+               get {
+                       return id;
+               }
+       }
+
+       public override string ToString () {
+               if (name != null)
+                       return "Name(" + name + ")";
+               else
+                       return "Id(" + id + ")";
+       }
+}
+
+internal abstract class Win32Resource {
+
+       NameOrId type;
+       NameOrId name;
+       int language;
+
+       internal Win32Resource (NameOrId type, NameOrId name, int language) {
+               this.type = type;
+               this.name = name;
+               this.language = language;
+       }
+
+       internal Win32Resource (Win32ResourceType type, int name, int language) {
+               this.type = new NameOrId ((int)type);
+               this.name = new NameOrId (name);
+               this.language = language;
+       }
+
+       public Win32ResourceType ResourceType {
+               get {
+                       if (type.IsName)
+                               return (Win32ResourceType)(-1);
+                       else
+                               return (Win32ResourceType)type.Id;
+               }
+       }
+
+       public NameOrId Name {
+               get {
+                       return name;
+               }
+       }
+
+       public NameOrId Type {
+               get {
+                       return type;
+               }
+       }
+
+       public int Language {
+               get {
+                       return language;
+               }
+       }
+
+       public abstract void WriteTo (Stream s);
+
+       public override string ToString () {
+               return "Win32Resource (Kind=" + ResourceType + ", Name=" + name + ")";
+       }
+}
+
 //
-// This class represents the contents of a VS_VERSIONINFO structure and its
-// children.
+// This class represents a Win32 resource in encoded format
 //
-internal class Win32VersionResource {
+internal class Win32EncodedResource : Win32Resource {
 
-       long signature;
-       int struct_version;
-       long file_version;
-       long product_version;
-       int file_flags_mask;
-       int file_flags;
-       int file_os;
-       int file_type;
-       int file_subtype;
-       long file_date;
+       byte[] data;
 
-       Hashtable properties;
+       internal Win32EncodedResource (NameOrId type, NameOrId name, int language, byte[] data) : base (type, name, language) {
+               this.data = data;
+       }
+
+       public byte[] Data {
+               get {
+                       return data;
+               }
+       }
+
+       public override void WriteTo (Stream s) {
+               s.Write (data, 0, data.Length);
+       }
+}
+
+//
+// This class represents a Win32 ICON resource
+//
+internal class Win32IconResource : Win32Resource {
+
+       ICONDIRENTRY icon;
+
+       public Win32IconResource (int id, int language, ICONDIRENTRY icon) : base (Win32ResourceType.RT_ICON, id, language) {
+               this.icon = icon;
+       }
+
+       public ICONDIRENTRY Icon {
+               get {
+                       return icon;
+               }
+       }
+
+       public override void WriteTo (Stream s) {
+               s.Write (icon.image, 0, icon.image.Length);
+       }
+}
+
+internal class Win32GroupIconResource : Win32Resource {
+
+       Win32IconResource[] icons;
+
+       public Win32GroupIconResource (int id, int language, Win32IconResource[] icons) : base (Win32ResourceType.RT_GROUP_ICON, id, language) {
+               this.icons = icons;
+       }
+
+       public override void WriteTo (Stream s) {
+               using (BinaryWriter w = new BinaryWriter (s)) {
+                       w.Write ((short)0);
+                       w.Write ((short)1);
+                       w.Write ((short)icons.Length);
+                       for (int i = 0; i < icons.Length; ++i) {
+                               Win32IconResource icon = icons [i];
+                               ICONDIRENTRY entry = icon.Icon;
+
+                               w.Write (entry.bWidth);
+                               w.Write (entry.bHeight);
+                               w.Write (entry.bColorCount);
+                               w.Write ((byte)0);
+                               w.Write (entry.wPlanes);
+                               w.Write (entry.wBitCount);
+                               w.Write ((int)entry.image.Length);
+                               w.Write ((short)icon.Name.Id);
+                       }
+               }
+       }
+}
+
+//
+// This class represents a Win32 VERSION resource
+//
+internal class Win32VersionResource : Win32Resource {
 
        public string[] WellKnownProperties = {
                "Comments",
@@ -44,7 +222,23 @@ internal class Win32VersionResource {
                "ProductVersion"
        };
 
-       public Win32VersionResource () {
+       long signature;
+       int struct_version;
+       long file_version;
+       long product_version;
+       int file_flags_mask;
+       int file_flags;
+       int file_os;
+       int file_type;
+       int file_subtype;
+       long file_date;
+
+       int file_lang;
+       int file_codepage;
+
+       Hashtable properties;
+
+       public Win32VersionResource (int id, int language) : base (Win32ResourceType.RT_VERSION, id, language) {
                // Initialize non-public members to the usual values used in
                // resources
                signature = 0xfeef04bd;
@@ -56,6 +250,9 @@ internal class Win32VersionResource {
                file_subtype = 0;
                file_date = 0;
 
+               file_lang = 0x7f;
+               file_codepage = 1200;
+
                properties = new Hashtable ();
 
                // Well known properties
@@ -74,7 +271,7 @@ internal class Win32VersionResource {
                }
 
                set {
-                       int[] ver = new int [4] { 0, 0, 0, 0 };
+                       long[] ver = new long [4] { 0, 0, 0, 0 };
                        if (value != null) {
                                string[] parts = value.Split ('.');
                                
@@ -98,15 +295,107 @@ internal class Win32VersionResource {
                }
        }
 
+       // Accessors for well known properties
+
+       public virtual string Comments {
+               get {
+                       return (string)properties ["Comments"];
+               }
+               set {
+                       properties ["Comments"] = value == String.Empty ? " " : value;
+               }
+       }
+
+       public virtual string CompanyName {
+               get {
+                       return (string)properties ["CompanyName"];
+               }
+               set {
+                       properties ["CompanyName"] = value == String.Empty ? " " : value;
+               }
+       }
+
+       public virtual string LegalCopyright {
+               get {
+                       return (string)properties ["LegalCopyright"];
+               }
+               set {
+                       properties ["LegalCopyright"] = value == String.Empty ? " " : value;
+               }
+       }
+
+       public virtual string LegalTrademarks {
+               get {
+                       return (string)properties ["LegalTrademarks"];
+               }
+               set {
+                       properties ["LegalTrademarks"] = value == String.Empty ? " " : value;
+               }
+       }
+
+       public virtual string OriginalFilename {
+               get {
+                       return (string)properties ["OriginalFilename"];
+               }
+               set {
+                       properties ["OriginalFilename"] = value == String.Empty ? " " : value;
+               }
+       }
+
+       public virtual string ProductName {
+               get {
+                       return (string)properties ["ProductName"];
+               }
+               set {
+                       properties ["ProductName"] = value == String.Empty ? " " : value;
+               }
+       }
+
+       public virtual string ProductVersion {
+               get {
+                       return (string)properties ["ProductVersion"];
+               }
+               set {
+                       properties ["ProductVersion"] = value == String.Empty ? " " : value;
+               }
+       }
+
+       public virtual string InternalName {
+               get {
+                       return (string)properties ["InternalName"];
+               }
+               set {
+                       properties ["InternalName"] = value == String.Empty ? " " : value;
+               }
+       }
+
+       public virtual string FileDescription {
+               get {
+                       return (string)properties ["FileDescription"];
+               }
+               set {
+                       properties ["FileDescription"] = value == String.Empty ? " " : value;
+               }
+       }
+
+       public virtual int FileLanguage {
+               get {
+                       return file_lang;
+               }
+               set {
+                       file_lang = value;
+               }
+       }
+
        private void emit_padding (BinaryWriter w) {
-               MemoryStream ms = (MemoryStream)w.BaseStream;
+               Stream ms = w.BaseStream;
 
                if ((ms.Position % 4) != 0)
                        w.Write ((short)0);
        }
 
        private void patch_length (BinaryWriter w, long len_pos) {
-               MemoryStream ms = (MemoryStream)w.BaseStream;
+               Stream ms = w.BaseStream;
 
                long pos = ms.Position;
                ms.Position = len_pos;
@@ -114,12 +403,9 @@ internal class Win32VersionResource {
                ms.Position = pos;
        }
 
-       public void WriteTo (MemoryStream ms)
+       public override void WriteTo (Stream ms)
        {
                using (BinaryWriter w = new BinaryWriter (ms, Encoding.Unicode)) {
-                       short len;
-                       long pos;
-
                        //
                        // See the documentation for the VS_VERSIONINFO structure and
                        // its children on MSDN
@@ -174,8 +460,8 @@ internal class Win32VersionResource {
                        if ((ms.Position % 4) != 0)
                                w.Write ((short)0);
 
-                       w.Write ((short)0x7f);
-                       w.Write ((short)1200);
+                       w.Write ((short)file_lang);
+                       w.Write ((short)file_codepage);
 
                        patch_length (w, var_pos);
 
@@ -195,7 +481,7 @@ internal class Win32VersionResource {
                        w.Write ((short)0);
                        w.Write ((short)0);
                        w.Write ((short)1);
-                       w.Write ("007f04b0".ToCharArray ());
+                       w.Write (String.Format ("{0:x4}{1:x4}", file_lang, file_codepage).ToCharArray ());
 
                        emit_padding (w);
 
@@ -215,6 +501,8 @@ internal class Win32VersionResource {
                                w.Write (value.ToCharArray ());
                                w.Write ((short)0);
 
+                               emit_padding (w);
+
                                patch_length (w, string_pos);
                        }
 
@@ -226,3 +514,192 @@ internal class Win32VersionResource {
                }
        }
 }
+
+internal class Win32ResFileReader {
+
+       Stream res_file;
+
+       public Win32ResFileReader (Stream s) {
+               res_file = s;
+       }
+
+       int read_int16 () {
+               int b1 = res_file.ReadByte ();
+               int b2 = res_file.ReadByte ();
+
+               if ((b1 == -1) || (b2 == -1))
+                       return -1;
+               else
+                       return b1 | (b2 << 8);
+       }
+
+       int read_int32 () {
+               int w1 = read_int16 ();
+               int w2 = read_int16 ();
+
+               if ((w1 == -1) || (w2 == -1))
+                       return -1;
+               return w1 | (w2 << 16);
+       }
+
+       private void read_padding () {
+               while ((res_file.Position % 4) != 0)
+                       read_int16 ();
+       }
+
+       NameOrId read_ordinal () {
+               int i = read_int16 ();
+               if ((i & 0xffff) != 0) {
+                       int j = read_int16 ();
+                       return new NameOrId (j);
+               }
+               else {
+                       byte[] chars = new byte [16];
+                       int pos = 0;
+
+                       while (true) {
+                               int j = read_int16 ();
+                               if (j == 0)
+                                       break;
+                               if (pos == chars.Length) {
+                                       byte[] new_chars = new byte [chars.Length * 2];
+                                       Array.Copy (chars, new_chars, chars.Length);
+                                       chars = new_chars;
+                               }
+                               chars [pos] = (byte)(j >> 8);
+                               chars [pos + 1] = (byte)(j & 0xff);
+                               pos += 2;
+                       }
+
+                       return new NameOrId (new String (Encoding.Unicode.GetChars (chars, 0, pos)));
+               }                                       
+       }
+
+       public ICollection ReadResources () {
+               ArrayList resources = new ArrayList ();
+
+               /* 
+                * We can't use a BinaryReader since we have to keep track of the 
+                * stream position for padding.
+                */
+
+               while (true) {
+
+                       read_padding ();
+
+                       int data_size = read_int32 ();
+
+                       if (data_size == -1)
+                               /* EOF */
+                               break;
+
+                       int header_size = read_int32 ();
+                       NameOrId type = read_ordinal ();
+                       NameOrId name = read_ordinal ();
+
+                       read_padding ();
+
+                       int data_version = read_int32 ();
+                       int memory_flags = read_int16 ();
+                       int language_id = read_int16 ();
+                       int version = read_int32 ();
+                       int characteristics = read_int32 ();
+
+                       if (data_size == 0)
+                               /* Empty resource entry */
+                               continue;
+
+                       byte[] data = new byte [data_size];
+                       res_file.Read (data, 0, data_size);
+
+                       resources.Add (new Win32EncodedResource (type, name, language_id, data));
+               }
+
+               return resources;
+       }
+}
+
+//
+// This class represents one icon image in an .ico file
+//
+internal class ICONDIRENTRY {
+
+       public byte bWidth;
+       public byte bHeight;
+       public byte bColorCount;
+       public byte bReserved;
+       public Int16 wPlanes;
+       public Int16 wBitCount;
+       public Int32 dwBytesInRes;
+       public Int32 dwImageOffset;
+
+       public byte[] image;
+
+       public override string ToString () {
+               return "ICONDIRENTRY (" + bWidth + "x" + bHeight + " " + wBitCount + " bpp)";
+       }
+}
+
+//
+// This class represents a Reader for Win32 .ico files
+//
+internal class Win32IconFileReader {
+
+       Stream iconFile;
+
+       public Win32IconFileReader (Stream s) {
+               iconFile = s;
+       }
+
+       public ICONDIRENTRY[] ReadIcons () {
+               ICONDIRENTRY[] icons = null;
+
+               using (BinaryReader r = new BinaryReader (iconFile)) {
+                       int idReserved = r.ReadInt16 ();
+                       int idType = r.ReadInt16 ();
+                       if ((idReserved != 0) || (idType != 1))
+                               throw new Exception ("Invalid .ico file format");
+                       long nitems = r.ReadInt16 ();
+
+                       icons = new ICONDIRENTRY [nitems];
+
+                       for (int i = 0; i < nitems; ++i) {
+                               ICONDIRENTRY entry = new ICONDIRENTRY ();
+
+                               entry.bWidth = r.ReadByte ();
+                               entry.bHeight = r.ReadByte ();
+                               entry.bColorCount = r.ReadByte ();
+                               entry.bReserved = r.ReadByte ();
+                               entry.wPlanes = r.ReadInt16 ();
+                               entry.wBitCount = r.ReadInt16 ();
+                               int dwBytesInRes = r.ReadInt32 ();
+                               int dwImageOffset = r.ReadInt32 ();
+
+                               /* Read image */
+                               entry.image = new byte [dwBytesInRes];
+
+                               long pos = iconFile.Position;
+                               iconFile.Position = dwImageOffset;
+                               iconFile.Read (entry.image, 0, dwBytesInRes);
+                               iconFile.Position = pos;
+
+                               /* 
+                                * The wPlanes and wBitCount members in the ICONDIRENTRY
+                                * structure can be 0, so we set them from the BITMAPINFOHEADER
+                                * structure that follows
+                                */
+
+                               if (entry.wPlanes == 0)
+                                       entry.wPlanes = (short)(entry.image [12] | (entry.image [13] << 8));
+                               if (entry.wBitCount == 0)
+                                       entry.wBitCount = (short)(entry.image [14] | (entry.image [15] << 8));
+
+                               icons [i] = entry;
+                       }
+
+                       return icons;
+               }
+       }
+}      
+
+}