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