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 private:
54 	Node readNode()
55 	{
56 		auto size = read!ushort() - ushort.sizeof;
57 		auto remainingData = data[size..$];
58 		scope(success) data = remainingData;
59 		data = data[0..size];
60 
61 		auto valueLength = read!ushort();
62 		auto type = read!ushort();
63 		if (type)
64 			valueLength *= 2;
65 
66 		wchar[] key;
67 		ubyte[] value;
68 		debug (verparse) scope(failure) stderr.writefln("wLength=%d wValueLength=%d remainder=%d wType=%d key=%(%s%) [error]", size, valueLength, data.length, type, [key]);
69 		if (valueLength < data.length && (cast(wchar[])data[0..$-valueLength]).indexOf('\0') < 0)
70 		{
71 			// Work around resource compiler bug
72 			debug (verparse) stderr.writeln("Resource compiler bug detected");
73 			valueLength += 2; // Make up for the lost null terminator
74 			auto wdata = cast(wchar[])data;
75 			while (wdata.length > 1 && wdata[$-1] == 0 && wdata[$-2] == 0)
76 				wdata = wdata[0..$-1];
77 			auto point = wdata.length - valueLength/wchar.sizeof;
78 			key = wdata[0..point];
79 			value = cast(ubyte[])wdata[point..$];
80 			data = null;
81 		}
82 		else
83 		{
84 			key = readWStringz();
85 			readAlign();
86 			if (valueLength > data.length)
87 				valueLength = data.length.to!ushort; // Work around Borland linker bug (madCHook.dll)
88 			value = readBytes(valueLength);
89 			readAlign();
90 		}
91 
92 		debug (verparse)
93 		{
94 			stderr.writefln("wLength=%d wValueLength=%d remainder=%d wType=%d key=%(%s%)", size, valueLength, data.length, type, [key]);
95 			if (value.length)
96 				stderr.writeln(hexDump(value));
97 		}
98 
99 		Node node;
100 		node.key = to!string(key);
101 		node.isText = type > 0;
102 		node.value = value;
103 
104 		while (data.length)
105 		{
106 			node.children ~= readNode();
107 			readAlign();
108 		}
109 
110 		return node;
111 	}
112 
113 	T read(T)()
114 	{
115 		T value = *cast(T*)data.ptr;
116 		data = data[T.sizeof..$];
117 		return value;
118 	}
119 
120 	wchar[] readWStringz()
121 	{
122 		auto start = cast(wchar*)data.ptr;
123 		size_t count = 0;
124 		while (read!wchar())
125 			count++;
126 		return start[0..count];
127 	}
128 
129 	ubyte[] readBytes(size_t count)
130 	{
131 		scope(success) data = data[count..$];
132 		return data[0..count];
133 	}
134 
135 	void readAlign()
136 	{
137 		while (data.length && (cast(size_t)data.ptr) % 4 != 0)
138 			read!ubyte();
139 	}
140 }
141