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 <vladimir@thecybershadow.net>
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 struct VersionInfoParser
22 {
23 	ubyte[] data;
24 
25 	this(void[] data)
26 	{
27 		enforce((cast(size_t)data.ptr) % 4 == 0, "Data must be DWORD-aligned");
28 		this.data = cast(ubyte[])data;
29 		root = readNode();
30 	}
31 
32 	struct Node
33 	{
34 		string key;
35 		bool isText;
36 		void[] value;
37 		Node[] children;
38 
39 		@property string valueText()
40 		{
41 			if (!value.length)
42 				return null;
43 			auto str = cast(wchar[])value;
44 			enforce(str.endsWith("\0"w), "Not null-terminated");
45 			return str[0..$-1].to!string;
46 		}
47 	}
48 	Node root;
49 
50 	Node readNode()
51 	{
52 		auto size = read!ushort() - ushort.sizeof;
53 		auto remainingData = data[size..$];
54 		scope(success) data = remainingData;
55 		data = data[0..size];
56 
57 		auto valueLength = read!ushort();
58 		auto type = read!ushort();
59 		if (type)
60 			valueLength *= 2;
61 
62 		wchar[] key;
63 		ubyte[] value;
64 		debug (verparse) scope(failure) stderr.writefln("wLength=%d wValueLength=%d remainder=%d wType=%d key=%(%s%) [error]", size, valueLength, data.length, type, [key]);
65 		if (valueLength < data.length && (cast(wchar[])data[0..$-valueLength]).indexOf('\0') < 0)
66 		{
67 			// Work around resource compiler bug
68 			debug (verparse) stderr.writeln("Resource compiler bug detected");
69 			valueLength += 2; // Make up for the lost null terminator
70 			auto wdata = cast(wchar[])data;
71 			while (wdata.length > 1 && wdata[$-1] == 0 && wdata[$-2] == 0)
72 				wdata = wdata[0..$-1];
73 			auto point = wdata.length - valueLength/wchar.sizeof;
74 			key = wdata[0..point];
75 			value = cast(ubyte[])wdata[point..$];
76 			data = null;
77 		}
78 		else
79 		{
80 			key = readWStringz();
81 			readAlign();
82 			if (valueLength > data.length)
83 				valueLength = data.length.to!ushort; // Work around Borland linker bug (madCHook.dll)
84 			value = readBytes(valueLength);
85 			readAlign();
86 		}
87 
88 		debug (verparse)
89 		{
90 			stderr.writefln("wLength=%d wValueLength=%d remainder=%d wType=%d key=%(%s%)", size, valueLength, data.length, type, [key]);
91 			if (value.length)
92 				stderr.writeln(hexDump(value));
93 		}
94 
95 		Node node;
96 		node.key = to!string(key);
97 		node.isText = type > 0;
98 		node.value = value;
99 
100 		while (data.length)
101 		{
102 			node.children ~= readNode();
103 			readAlign();
104 		}
105 
106 		return node;
107 	}
108 
109 	T read(T)()
110 	{
111 		T value = *cast(T*)data.ptr;
112 		data = data[T.sizeof..$];
113 		return value;
114 	}
115 
116 	wchar[] readWStringz()
117 	{
118 		auto start = cast(wchar*)data.ptr;
119 		size_t count = 0;
120 		while (read!wchar())
121 			count++;
122 		return start[0..count];
123 	}
124 
125 	ubyte[] readBytes(size_t count)
126 	{
127 		scope(success) data = data[count..$];
128 		return data[0..count];
129 	}
130 
131 	void readAlign()
132 	{
133 		while (data.length && (cast(size_t)data.ptr) % 4 != 0)
134 			read!ubyte();
135 	}
136 }
137