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