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