1 /**
2  * Support for Windows PE VersionInfo resources.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 module ae.sys.windows.pe.versioninfo;
15 
16 import std.algorithm.searching;
17 import std.conv;
18 import std.exception;
19 import std..string;
20 
21 /// Parses PE VersionInfo resources.
22 struct VersionInfoParser
23 {
24 	ubyte[] data; /// All data.
25 
26 	this(void[] data)
27 	{
28 		enforce((cast(size_t)data.ptr) % 4 == 0, "Data must be DWORD-aligned");
29 		this.data = cast(ubyte[])data;
30 		root = readNode();
31 	} ///
32 
33 	/// A `VersionInfo` node.
34 	struct Node
35 	{
36 		string key; ///
37 		bool isText; ///
38 		void[] value; ///
39 		Node[] children; ///
40 
41 		/// Return the string contents of a text node.
42 		@property string valueText()
43 		{
44 			if (!value.length)
45 				return null;
46 			auto str = cast(wchar[])value;
47 			enforce(str.endsWith("\0"w), "Not null-terminated");
48 			return str[0..$-1].to!string;
49 		}
50 	}
51 	Node root; /// The root node.
52 
53 	Node readNode()
54 	{
55 		auto size = read!ushort() - ushort.sizeof;
56 		auto remainingData = data[size..$];
57 		scope(success) data = remainingData;
58 		data = data[0..size];
59 
60 		auto valueLength = read!ushort();
61 		auto type = read!ushort();
62 		if (type)
63 			valueLength *= 2;
64 
65 		wchar[] key;
66 		ubyte[] value;
67 		debug (verparse) scope(failure) stderr.writefln("wLength=%d wValueLength=%d remainder=%d wType=%d key=%(%s%) [error]", size, valueLength, data.length, type, [key]);
68 		if (valueLength < data.length && (cast(wchar[])data[0..$-valueLength]).indexOf('\0') < 0)
69 		{
70 			// Work around resource compiler bug
71 			debug (verparse) stderr.writeln("Resource compiler bug detected");
72 			valueLength += 2; // Make up for the lost null terminator
73 			auto wdata = cast(wchar[])data;
74 			while (wdata.length > 1 && wdata[$-1] == 0 && wdata[$-2] == 0)
75 				wdata = wdata[0..$-1];
76 			auto point = wdata.length - valueLength/wchar.sizeof;
77 			key = wdata[0..point];
78 			value = cast(ubyte[])wdata[point..$];
79 			data = null;
80 		}
81 		else
82 		{
83 			key = readWStringz();
84 			readAlign();
85 			if (valueLength > data.length)
86 				valueLength = data.length.to!ushort; // Work around Borland linker bug (madCHook.dll)
87 			value = readBytes(valueLength);
88 			readAlign();
89 		}
90 
91 		debug (verparse)
92 		{
93 			stderr.writefln("wLength=%d wValueLength=%d remainder=%d wType=%d key=%(%s%)", size, valueLength, data.length, type, [key]);
94 			if (value.length)
95 				stderr.writeln(hexDump(value));
96 		}
97 
98 		Node node;
99 		node.key = to!string(key);
100 		node.isText = type > 0;
101 		node.value = value;
102 
103 		while (data.length)
104 		{
105 			node.children ~= readNode();
106 			readAlign();
107 		}
108 
109 		return node;
110 	}
111 
112 	T read(T)()
113 	{
114 		T value = *cast(T*)data.ptr;
115 		data = data[T.sizeof..$];
116 		return value;
117 	}
118 
119 	wchar[] readWStringz()
120 	{
121 		auto start = cast(wchar*)data.ptr;
122 		size_t count = 0;
123 		while (read!wchar())
124 			count++;
125 		return start[0..count];
126 	}
127 
128 	ubyte[] readBytes(size_t count)
129 	{
130 		scope(success) data = data[count..$];
131 		return data[0..count];
132 	}
133 
134 	void readAlign()
135 	{
136 		while (data.length && (cast(size_t)data.ptr) % 4 != 0)
137 			read!ubyte();
138 	}
139 }
140