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