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