1 /*
2   Copyright (C) 2008 Jeroen Frijters
3 
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7 
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely, subject to the following restrictions:
11 
12   1. The origin of this software must not be misrepresented; you must not
13      claim that you wrote the original software. If you use this software
14      in a product, an acknowledgment in the product documentation would be
15      appreciated but is not required.
16   2. Altered source versions must be plainly marked as such, and must not be
17      misrepresented as being the original software.
18   3. This notice may not be removed or altered from any source distribution.
19 
20   Jeroen Frijters
21   jeroen@frijters.net
22 
23 */
24 using System;
25 using System.Globalization;
26 using IKVM.Reflection.Emit;
27 
28 namespace IKVM.Reflection.Writer
29 {
30 	sealed class VersionInfo
31 	{
32 		private AssemblyName name;
33 		private string fileName;
34 		internal string copyright;
35 		internal string trademark;
36 		internal string product;
37 		internal string company;
38 		private string description;
39 		private string title;
40 		internal string informationalVersion;
41 		private string fileVersion;
42 
SetName(AssemblyName name)43 		internal void SetName(AssemblyName name)
44 		{
45 			this.name = name;
46 		}
47 
SetFileName(string assemblyFileName)48 		internal void SetFileName(string assemblyFileName)
49 		{
50 			this.fileName = System.IO.Path.GetFileName(assemblyFileName);
51 		}
52 
SetAttribute(AssemblyBuilder asm, CustomAttributeBuilder cab)53 		internal void SetAttribute(AssemblyBuilder asm, CustomAttributeBuilder cab)
54 		{
55 			Universe u = cab.Constructor.Module.universe;
56 			Type type = cab.Constructor.DeclaringType;
57 			if (copyright == null && type == u.System_Reflection_AssemblyCopyrightAttribute)
58 			{
59 				copyright = (string)cab.DecodeBlob(asm).GetConstructorArgument(0);
60 			}
61 			else if (trademark == null && type == u.System_Reflection_AssemblyTrademarkAttribute)
62 			{
63 				trademark = (string)cab.DecodeBlob(asm).GetConstructorArgument(0);
64 			}
65 			else if (product == null && type == u.System_Reflection_AssemblyProductAttribute)
66 			{
67 				product = (string)cab.DecodeBlob(asm).GetConstructorArgument(0);
68 			}
69 			else if (company == null && type == u.System_Reflection_AssemblyCompanyAttribute)
70 			{
71 				company = (string)cab.DecodeBlob(asm).GetConstructorArgument(0);
72 			}
73 			else if (description == null && type == u.System_Reflection_AssemblyDescriptionAttribute)
74 			{
75 				description = (string)cab.DecodeBlob(asm).GetConstructorArgument(0);
76 			}
77 			else if (title == null && type == u.System_Reflection_AssemblyTitleAttribute)
78 			{
79 				title = (string)cab.DecodeBlob(asm).GetConstructorArgument(0);
80 			}
81 			else if (informationalVersion == null && type == u.System_Reflection_AssemblyInformationalVersionAttribute)
82 			{
83 				informationalVersion = (string)cab.DecodeBlob(asm).GetConstructorArgument(0);
84 			}
85 			else if (fileVersion == null && type == u.System_Reflection_AssemblyFileVersionAttribute)
86 			{
87 				fileVersion = (string)cab.DecodeBlob(asm).GetConstructorArgument(0);
88 			}
89 		}
90 
Write(ByteBuffer bb)91 		internal void Write(ByteBuffer bb)
92 		{
93 			if (fileVersion == null)
94 			{
95 				if (name.Version != null)
96 				{
97 					fileVersion = name.Version.ToString();
98 				}
99 				else
100 				{
101 					fileVersion = "0.0.0.0";
102 				}
103 			}
104 
105 			int codepage = 1200;	// Unicode codepage
106 			int lcid = 0x7f;
107 			try
108 			{
109 				if (name.CultureInfo != null)
110 				{
111 #if CORECLR
112 					throw new NotImplementedException();
113 #else
114 					lcid = name.CultureInfo.LCID;
115 #endif
116 				}
117 			}
118 			catch (ArgumentException)
119 			{
120 				// AssemblyName.CultureInfo throws an ArgumentException if AssemblyBuilder.__SetAssemblyCulture() was used to specify a non-existing culture
121 			}
122 
123 			Version filever = ParseVersionRobust(fileVersion);
124 			int fileVersionMajor = filever.Major;
125 			int fileVersionMinor = filever.Minor;
126 			int fileVersionBuild = filever.Build;
127 			int fileVersionRevision = filever.Revision;
128 
129 			int productVersionMajor = fileVersionMajor;
130 			int productVersionMinor = fileVersionMinor;
131 			int productVersionBuild = fileVersionBuild;
132 			int productVersionRevision = fileVersionRevision;
133 			if (informationalVersion != null)
134 			{
135 				Version productver = ParseVersionRobust(informationalVersion);
136 				productVersionMajor = productver.Major;
137 				productVersionMinor = productver.Minor;
138 				productVersionBuild = productver.Build;
139 				productVersionRevision = productver.Revision;
140 			}
141 
142 			ByteBuffer stringTable = new ByteBuffer(512);
143 			stringTable.Write((short)0);	// wLength (placeholder)
144 			stringTable.Write((short)0);	// wValueLength
145 			stringTable.Write((short)1);	// wType
146 			WriteUTF16Z(stringTable, string.Format("{0:x4}{1:x4}", lcid, codepage));
147 			stringTable.Align(4);
148 
149 			WriteString(stringTable, "Comments", description);
150 			WriteString(stringTable, "CompanyName", company);
151 			WriteString(stringTable, "FileDescription", title);
152 			WriteString(stringTable, "FileVersion", fileVersion);
153 			WriteString(stringTable, "InternalName", name.Name);
154 			WriteString(stringTable, "LegalCopyright", copyright);
155 			WriteString(stringTable, "LegalTrademarks", trademark);
156 			WriteString(stringTable, "OriginalFilename", fileName);
157 			WriteString(stringTable, "ProductName", product);
158 			WriteString(stringTable, "ProductVersion", informationalVersion);
159 
160 			stringTable.Position = 0;
161 			stringTable.Write((short)stringTable.Length);
162 
163 			ByteBuffer stringFileInfo = new ByteBuffer(512);
164 			stringFileInfo.Write((short)0);	// wLength (placeholder)
165 			stringFileInfo.Write((short)0);	// wValueLength
166 			stringFileInfo.Write((short)1);	// wType
167 			WriteUTF16Z(stringFileInfo, "StringFileInfo");
168 			stringFileInfo.Align(4);
169 			stringFileInfo.Write(stringTable);
170 			stringFileInfo.Position = 0;
171 			stringFileInfo.Write((short)stringFileInfo.Length);
172 
173 			byte[] preamble1 = new byte[] {
174 			  // VS_VERSIONINFO (platform SDK)
175 			  0x34, 0x00,				// wValueLength
176 			  0x00, 0x00,				// wType
177 			  0x56, 0x00, 0x53, 0x00, 0x5F, 0x00, 0x56, 0x00, 0x45, 0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x5F, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, 0x00, 0x00,  // "VS_VERSION_INFO\0"
178 			  0x00, 0x00,				// Padding1 (32 bit alignment)
179 			  // VS_FIXEDFILEINFO starts
180 			  0xBD, 0x04, 0xEF, 0xFE,	// dwSignature (0xFEEF04BD)
181 			  0x00, 0x00, 0x01, 0x00,	// dwStrucVersion
182 			};
183 			byte[] preamble2 = new byte[] {
184 			  0x3F, 0x00, 0x00, 0x00,	// dwFileFlagsMask (??)
185 			  0x00, 0x00, 0x00, 0x00,	// dwFileFlags (??)
186 			  0x04, 0x00, 0x00, 0x00,	// dwFileOS
187 			  0x02, 0x00, 0x00, 0x00,	// dwFileType
188 			  0x00, 0x00, 0x00, 0x00,	// dwFileSubtype
189 			  0x00, 0x00, 0x00, 0x00,	// dwFileDateMS
190 			  0x00, 0x00, 0x00, 0x00,	// dwFileDateLS
191 										// Padding2 (32 bit alignment)
192 			  // VarFileInfo
193 			  0x44, 0x00,				// wLength
194 			  0x00, 0x00,				// wValueLength
195 			  0x01, 0x00,				// wType
196 			  0x56, 0x00, 0x61, 0x00, 0x72, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x00, 0x00,	// "VarFileInfo\0"
197 			  0x00, 0x00,				// Padding
198 			  // Var
199 			  0x24, 0x00,				// wLength
200 			  0x04, 0x00,				// wValueLength
201 			  0x00, 0x00,				// wType
202 			  0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x73, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00,	// "Translation\0"
203 			  0x00, 0x00,				// Padding (32 bit alignment)
204 			};
205 			bb.Write((short)(2 + preamble1.Length + 8 + 8 + preamble2.Length + 4 + stringFileInfo.Length));
206 			bb.Write(preamble1);
207 			bb.Write((short)fileVersionMinor);
208 			bb.Write((short)fileVersionMajor);
209 			bb.Write((short)fileVersionRevision);
210 			bb.Write((short)fileVersionBuild);
211 			bb.Write((short)productVersionMinor);
212 			bb.Write((short)productVersionMajor);
213 			bb.Write((short)productVersionRevision);
214 			bb.Write((short)productVersionBuild);
215 			bb.Write(preamble2);
216 			bb.Write((short)lcid);
217 			bb.Write((short)codepage);
218 			bb.Write(stringFileInfo);
219 		}
220 
WriteUTF16Z(ByteBuffer bb, string str)221 		private static void WriteUTF16Z(ByteBuffer bb, string str)
222 		{
223 			foreach (char c in str)
224 			{
225 				bb.Write((short)c);
226 			}
227 			bb.Write((short)0);
228 		}
229 
WriteString(ByteBuffer bb, string name, string value)230 		private static void WriteString(ByteBuffer bb, string name, string value)
231 		{
232 			value = value ?? " ";
233 			int pos = bb.Position;
234 			bb.Write((short)0);					// wLength (placeholder)
235 			bb.Write((short)(value.Length + 1));// wValueLength
236 			bb.Write((short)1);					// wType
237 			WriteUTF16Z(bb, name);
238 			bb.Align(4);
239 			WriteUTF16Z(bb, value);
240 			bb.Align(4);
241 			int savedPos = bb.Position;
242 			bb.Position = pos;
243 			bb.Write((short)(savedPos - pos));
244 			bb.Position = savedPos;
245 		}
246 
ParseVersionRobust(string ver)247 		private static Version ParseVersionRobust(string ver)
248 		{
249 			int index = 0;
250 			ushort major = ParseVersionPart(ver, ref index);
251 			ushort minor = ParseVersionPart(ver, ref index);
252 			ushort build = ParseVersionPart(ver, ref index);
253 			ushort revision = ParseVersionPart(ver, ref index);
254 			return new Version(major, minor, build, revision);
255 		}
256 
ParseVersionPart(string str, ref int pos)257 		private static ushort ParseVersionPart(string str, ref int pos)
258 		{
259 			ushort value = 0;
260 			while (pos < str.Length)
261 			{
262 				char c = str[pos];
263 				if (c == '.')
264 				{
265 					pos++;
266 					break;
267 				}
268 				else if (c >= '0' && c <= '9')
269 				{
270 					value *= 10;
271 					value += (ushort)(c - '0');
272 					pos++;
273 				}
274 				else
275 				{
276 					break;
277 				}
278 			}
279 			return value;
280 		}
281 	}
282 }
283