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