pelite\resources/
version_info.rs

1/*!
2Version Information.
3
4See [Microsoft's documentation](https://docs.microsoft.com/en-us/windows/desktop/menurc/version-information) for more information.
5
6# Examples
7
8See also the `examples/version_info.rs` example which reads and prints the version info of the given file.
9
10```
11use pelite::PeFile;
12
13fn example(bin: PeFile<'_>) -> Result<(), pelite::resources::FindError> {
14	let resources = bin.resources()?;
15	let version_info = resources.version_info()?;
16
17	// Get and print the fixed file info
18	println!("FixedFileInfo: {:?}", version_info.fixed());
19
20	// Get the first available language
21	let lang = version_info.translation()[0];
22
23	// Query some properties
24	let company_name = version_info.value(lang, "CompanyName");
25
26	// Print all the properties for this language
27	version_info.strings(lang, |key, value| {
28		println!("{}: {:?}", key, value);
29	});
30
31	// Dump the version info into hashmaps for later consumption or serialization
32	let file_info = version_info.file_info();
33
34	// Transform the version info back into source code
35	let source_code = version_info.source_code();
36
37	Ok(())
38}
39```
40
41 */
42
43#[cfg(not(feature = "std"))]
44use hashbrown::HashMap;
45#[cfg(feature = "std")]
46use std::collections::HashMap;
47
48use std::prelude::v1::*;
49use std::{char, cmp, fmt, mem, slice};
50
51use std::fmt::Write;
52
53use crate::image::VS_FIXEDFILEINFO;
54use crate::util::{wstrn, AlignTo, FmtUtf16};
55use crate::{Error, Pod, Result};
56
57//----------------------------------------------------------------
58
59/// Language and charset pair.
60#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
61#[repr(C)]
62pub struct Language {
63	/// References [langID](https://docs.microsoft.com/en-us/windows/desktop/menurc/versioninfo-resource#langID) constants.
64	pub lang_id: u16,
65	/// References [charsetID](https://docs.microsoft.com/en-us/windows/desktop/menurc/versioninfo-resource#charsetID) constants.
66	pub charset_id: u16,
67}
68unsafe impl Pod for Language {}
69impl Language {
70	/// Parse language hex strings.
71	pub fn parse(lang: &[u16]) -> std::result::Result<Language, &[u16]> {
72		if lang.len() != 8 {
73			return Err(lang);
74		}
75		fn digit(word: u16) -> u16 {
76			let num = word.wrapping_sub('0' as u16);
77			let upper = word.wrapping_sub('A' as u16).wrapping_add(10);
78			let lower = word.wrapping_sub('a' as u16).wrapping_add(10);
79			if word >= 'a' as u16 { lower }
80			else if word >= 'A' as u16 { upper }
81			else { num }
82		}
83		let mut digits = [0u16; 8];
84		for i in 0..8 {
85			digits[i] = digit(lang[i]);
86		}
87		let lang_id = (digits[0] << 12) | (digits[1] << 8) | (digits[2] << 4) | digits[3];
88		let charset_id = (digits[4] << 12) | (digits[5] << 8) | (digits[6] << 4) | digits[7];
89		Ok(Language { lang_id, charset_id })
90	}
91	fn from_slice<'a>(words: &'a [u16]) -> &'a [Language] {
92		let len = words.len() / 2;
93		unsafe { slice::from_raw_parts(words.as_ptr() as *const Language, len) }
94	}
95}
96impl fmt::Display for Language {
97	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98		write!(f, "{:04X}{:04X}", self.lang_id, self.charset_id)
99	}
100}
101
102//----------------------------------------------------------------
103
104/// Version Information.
105#[derive(Copy, Clone)]
106pub struct VersionInfo<'a> {
107	words: &'a [u16],
108}
109impl<'a> VersionInfo<'a> {
110	pub fn try_from(bytes: &'a [u8]) -> Result<VersionInfo<'a>> {
111		// Alignment of 4 bytes is assumed everywhere,
112		// unsafe code in this module relies on this
113		if !bytes.as_ptr().aligned_to(4) {
114			return Err(Error::Misaligned);
115		}
116		let words = unsafe { slice::from_raw_parts(bytes.as_ptr() as *const u16, bytes.len() / 2) };
117		Ok(VersionInfo { words })
118	}
119
120	/// Gets the fixed file information if available.
121	///
122	/// Queries `\`.
123	pub fn fixed(self) -> Option<&'a VS_FIXEDFILEINFO> {
124		let mut this = QueryFixed(None);
125		self.visit(&mut this);
126		this.0
127	}
128	/// Gets the available languages.
129	///
130	/// Queries `\VarFileInfo\Translation`.
131	pub fn translation(self) -> &'a [Language] {
132		let mut this = QueryTranslation(&[]);
133		self.visit(&mut this);
134		this.0
135	}
136	/// Gets a string value by name.
137	///
138	/// Queries `\StringFileInfo\{lang}\{key}`
139	pub fn value(self, lang: Language, key: &str) -> Option<String> {
140		let mut this = QueryValue { lang, key, value: None };
141		self.visit(&mut this);
142		this.value
143	}
144	/// Iterates over all the strings' keys and values of a given language.
145	///
146	/// Queries `\StringFileInfo\{lang}\*`
147	pub fn strings<F: FnMut(&str, &str)>(self, lang: Language, f: F) {
148		self.visit(&mut QueryStrings { lang, f });
149	}
150	/// Parse the version info into HashMaps.
151	pub fn file_info(self) -> FileInfo<'a> {
152		let mut file_info = FileInfo::default();
153		self.visit(&mut file_info);
154		file_info
155	}
156	/// Renders the version info back into its source code form.
157	pub fn source_code(self) -> String {
158		let mut source_code = String::new();
159		self.visit(&mut source_code);
160		source_code
161	}
162
163	/// Parse the version information.
164	///
165	/// Because of the super convoluted format, the visitor pattern is used.
166	/// Implement the [`Visit` trait](trait.Visit.html) to get the desired information.
167	///
168	/// To keep the API simple all errors are ignored, any invalid or corrupted data is skipped.
169	pub fn visit(self, visit: &mut dyn Visit<'a>) {
170		for version_info in Parser::new_bytes(self.words).filter_map(Result::ok) {
171			const VS_FIXEDFILEINFO_SIZEOF: usize = mem::size_of::<VS_FIXEDFILEINFO>();
172			let fixed = match mem::size_of_val(version_info.value) {
173				0 => None,
174				VS_FIXEDFILEINFO_SIZEOF => {
175					let value = unsafe { &*(version_info.value.as_ptr() as *const VS_FIXEDFILEINFO) };
176					Some(value)
177				},
178				_ => None,
179			};
180
181			if !visit.version_info(version_info.key, fixed) {
182				continue;
183			}
184
185			// MS docs: This member is always equal to zero.
186			visit.enter_scope(0);
187			for file_info in Parser::new_zero(version_info.children).filter_map(Result::ok) {
188				if !visit.file_info(file_info.key) {
189					continue;
190				}
191
192				// MS docs: L"StringFileInfo"
193				visit.enter_scope(1);
194				if file_info.key == &self::strings::StringFileInfo {
195					// MS docs: This member is always equal to zero.
196					for string_table in Parser::new_zero(file_info.children).filter_map(Result::ok) {
197						if !visit.string_table(string_table.key) {
198							continue;
199						}
200
201						visit.enter_scope(2);
202						for string in Parser::new_words(string_table.children).filter_map(Result::ok) {
203							// Strip the nul terminator...
204							let value = if string.value.last() != Some(&0) { string.value }
205							else { &string.value[..string.value.len() - 1] };
206							visit.string(string.key, value);
207						}
208						visit.exit_scope(2);
209					}
210				}
211				// MS docs: L"VarFileInfo"
212				else if file_info.key == &self::strings::VarFileInfo {
213					for var in Parser::new_bytes(file_info.children).filter_map(Result::ok) {
214						visit.var(var.key, var.value);
215					}
216				}
217				visit.exit_scope(1);
218			}
219			visit.exit_scope(0);
220
221			// Ignore any additional version infos...
222			return;
223		}
224	}
225}
226impl fmt::Debug for VersionInfo<'_> {
227	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
228		let file_info = self.file_info();
229		f.debug_struct("VersionInfo")
230			.field("fixed", &file_info.fixed)
231			.field("strings", &file_info.strings)
232			.field("langs", &file_info.langs)
233			.finish()
234	}
235}
236
237//----------------------------------------------------------------
238
239/// Visitor pattern to view the version information details.
240#[allow(unused_variables)]
241pub trait Visit<'a> {
242	fn version_info(&mut self, key: &'a [u16], fixed: Option<&'a VS_FIXEDFILEINFO>) -> bool { true }
243	fn file_info(&mut self, key: &'a [u16]) -> bool { true }
244	fn string_table(&mut self, lang: &'a [u16]) -> bool { true }
245	fn string(&mut self, key: &'a [u16], value: &'a [u16]) {}
246	fn var(&mut self, key: &'a [u16], value: &'a [u16]) {}
247	fn enter_scope(&mut self, depth: usize) {}
248	fn exit_scope(&mut self, depth: usize) {}
249}
250
251struct QueryFixed<'a>(Option<&'a VS_FIXEDFILEINFO>);
252impl<'a> Visit<'a> for QueryFixed<'a> {
253	fn version_info(&mut self, _key: &'a [u16], fixed: Option<&'a VS_FIXEDFILEINFO>) -> bool {
254		self.0 = fixed;
255		true
256	}
257	fn file_info(&mut self, _key: &'a [u16]) -> bool {
258		false
259	}
260}
261
262struct QueryTranslation<'a>(&'a [Language]);
263impl<'a> Visit<'a> for QueryTranslation<'a> {
264	fn file_info(&mut self, key: &'a [u16]) -> bool {
265		key == strings::VarFileInfo
266	}
267	fn var(&mut self, key: &'a [u16], value: &'a [u16]) {
268		if key == strings::Translation {
269			self.0 = Language::from_slice(value);
270		}
271	}
272}
273
274struct QueryValue<'z> {
275	lang: Language,
276	key: &'z str,
277	value: Option<String>,
278}
279impl<'a, 'z> Visit<'a> for QueryValue<'z> {
280	fn file_info(&mut self, key: &'a [u16]) -> bool {
281		key == strings::StringFileInfo
282	}
283	fn string_table(&mut self, lang: &'a [u16]) -> bool {
284		match Language::parse(lang) {
285			Ok(lang) => lang == self.lang,
286			Err(_) => false,
287		}
288	}
289	fn string(&mut self, key: &'a [u16], value: &'a [u16]) {
290		if Iterator::eq(self.key.chars().map(Ok), char::decode_utf16(key.iter().cloned())) {
291			let value = String::from_utf16_lossy(value);
292			self.value = Some(value);
293		}
294	}
295}
296
297struct QueryStrings<F> {
298	lang: Language,
299	f: F,
300}
301impl<'a, F: FnMut(&str, &str)> Visit<'a> for QueryStrings<F> {
302	fn string_table(&mut self, lang: &'a [u16]) -> bool {
303		match Language::parse(lang) {
304			Ok(lang) => lang == self.lang,
305			Err(_) => false,
306		}
307	}
308	fn string(&mut self, key: &'a [u16], value: &'a [u16]) {
309		let key = String::from_utf16_lossy(key);
310		let value = String::from_utf16_lossy(value);
311		(self.f)(&key, &value);
312	}
313}
314
315impl<'a> Visit<'a> for String {
316	fn version_info(&mut self, _key: &'a [u16], fixed: Option<&'a VS_FIXEDFILEINFO>) -> bool {
317		if let Some(fixed) = fixed {
318			let _ = writeln!(self, "\
3191 VERSIONINFO
320FILEVERSION {}, {}, {}, {}
321PRODUCTVERSION {}, {}, {}, {}
322FILEFLAGSMASK {:#x}
323FILEFLAGS {:#x}
324FILEOS ({} << 16) | {}
325FILETYPE {}
326FILESUBTYPE {}",
327				fixed.dwFileVersion.Major, fixed.dwFileVersion.Minor, fixed.dwFileVersion.Patch, fixed.dwFileVersion.Build,
328				fixed.dwProductVersion.Major, fixed.dwProductVersion.Minor, fixed.dwProductVersion.Patch, fixed.dwProductVersion.Build,
329				fixed.dwFileFlagsMask, fixed.dwFileFlags, fixed.dwFileOS >> 16, fixed.dwFileOS & 0xffff, fixed.dwFileType, fixed.dwFileSubtype);
330		}
331		true
332	}
333	fn file_info(&mut self, key: &'a [u16]) -> bool {
334		let _ = writeln!(self, "  BLOCK {:?}", FmtUtf16(key));
335		true
336	}
337	fn string_table(&mut self, lang: &'a [u16]) -> bool {
338		let _ = writeln!(self, "    BLOCK {:?}", FmtUtf16(lang));
339		true
340	}
341	fn string(&mut self, key: &'a [u16], value: &'a [u16]) {
342		let _ = writeln!(self, "      VALUE {:?}, {:?}", FmtUtf16(key), FmtUtf16(value));
343	}
344	fn var(&mut self, key: &'a [u16], value: &'a [u16]) {
345		// Don't know how to interpret any other Var key...
346		if key != strings::Translation {
347			return;
348		}
349		struct PrintLangs<'a>(&'a [Language]);
350		impl fmt::Display for PrintLangs<'_> {
351			fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
352				for lang in self.0 {
353					write!(f, ", {}, {}", lang.lang_id, lang.charset_id)?;
354				}
355				Ok(())
356			}
357		}
358		let langs = Language::from_slice(value);
359		let _ = writeln!(self, "    VALUE {:?}{}", FmtUtf16(key), PrintLangs(langs));
360	}
361	fn enter_scope(&mut self, depth: usize) {
362		let _ = writeln!(self, "{}{{", &"        "[..depth * 2]);
363	}
364	fn exit_scope(&mut self, depth: usize) {
365		let _ = writeln!(self, "{}}}", &"        "[..depth * 2]);
366	}
367}
368
369/// VersionInfo parsed into HashMaps.
370#[derive(Clone, Debug, Default)]
371#[cfg_attr(all(feature = "std", feature = "serde"), derive(::serde::Serialize))]
372pub struct FileInfo<'a> {
373	pub fixed: Option<&'a VS_FIXEDFILEINFO>,
374	pub strings: HashMap<Language, HashMap<String, String>>,
375	pub langs: &'a [Language],
376	#[cfg_attr(feature = "serde", serde(skip))]
377	lang: Language,
378}
379impl<'a> Visit<'a> for FileInfo<'a> {
380	fn version_info(&mut self, _key: &'a [u16], fixed: Option<&'a VS_FIXEDFILEINFO>) -> bool {
381		self.fixed = fixed;
382		true
383	}
384	fn string_table(&mut self, lang: &'a [u16]) -> bool {
385		if let Ok(lang) = Language::parse(lang) {
386			self.lang = lang;
387			self.strings.insert(lang, HashMap::new());
388			return true;
389		}
390		false
391	}
392	fn string(&mut self, key: &'a [u16], value: &'a [u16]) {
393		if let Some(entry) = self.strings.get_mut(&self.lang) {
394			let key = String::from_utf16_lossy(key);
395			let value = String::from_utf16_lossy(value);
396			entry.insert(key, value);
397		}
398	}
399	fn var(&mut self, key: &'a [u16], value: &'a [u16]) {
400		if key == strings::Translation {
401			self.langs = Language::from_slice(value);
402		}
403	}
404}
405
406//----------------------------------------------------------------
407
408/*
409	"version_info": {
410		"fixed": { .. },
411		"strings": {
412			"040904B0": { ... }
413		},
414		"langs": ["040904B0"],
415	},
416*/
417
418#[cfg(all(feature = "std", feature = "serde"))]
419mod serde {
420	use crate::util::serde_helper::*;
421	use super::{Language, VersionInfo};
422
423	impl Serialize for Language {
424		fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
425			serializer.collect_str(self)
426		}
427	}
428
429	impl<'a> Serialize for VersionInfo<'a> {
430		fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
431			self.file_info().serialize(serializer)
432		}
433	}
434}
435
436//----------------------------------------------------------------
437
438mod strings {
439	#![allow(non_upper_case_globals)]
440	// static VS_VERSION_INFO: [u16; 15] = [86u16, 83, 95, 86, 69, 82, 83, 73, 79, 78, 95, 73, 78, 70, 79];
441	pub(super) static StringFileInfo: [u16; 14] = [83u16, 116, 114, 105, 110, 103, 70, 105, 108, 101, 73, 110, 102, 111];
442	pub(super) static VarFileInfo: [u16; 11] = [86u16, 97, 114, 70, 105, 108, 101, 73, 110, 102, 111];
443	pub(super) static Translation: [u16; 11] = [84u16, 114, 97, 110, 115, 108, 97, 116, 105, 111, 110];
444	// static Comments: [u16; 8] = [67u16, 111, 109, 109, 101, 110, 116, 115];
445	// static CompanyName: [u16; 11] = [67u16, 111, 109, 112, 97, 110, 121, 78, 97, 109, 101];
446	// static FileDescription: [u16; 15] = [70u16, 105, 108, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110];
447	// static FileVersion: [u16; 11] = [70u16, 105, 108, 101, 86, 101, 114, 115, 105, 111, 110];
448	// static InternalName: [u16; 12] = [73u16, 110, 116, 101, 114, 110, 97, 108, 78, 97, 109, 101];
449	// static LegalCopyright: [u16; 14] = [76u16, 101, 103, 97, 108, 67, 111, 112, 121, 114, 105, 103, 104, 116];
450	// static LegalTrademarks: [u16; 15] = [76u16, 101, 103, 97, 108, 84, 114, 97, 100, 101, 109, 97, 114, 107, 115];
451	// static OriginalFilename: [u16; 16] = [79u16, 114, 105, 103, 105, 110, 97, 108, 70, 105, 108, 101, 110, 97, 109, 101];
452	// static PrivateBuild: [u16; 12] = [80u16, 114, 105, 118, 97, 116, 101, 66, 117, 105, 108, 100];
453	// static ProductName: [u16; 11] = [80u16, 114, 111, 100, 117, 99, 116, 78, 97, 109, 101];
454	// static ProductVersion: [u16; 14] = [80u16, 114, 111, 100, 117, 99, 116, 86, 101, 114, 115, 105, 111, 110];
455	// static SpecialBuild: [u16; 12] = [83u16, 112, 101, 99, 105, 97, 108, 66, 117, 105, 108, 100];
456}
457
458//----------------------------------------------------------------
459
460#[cfg(test)]
461pub(crate) fn test(version_info: VersionInfo<'_>) {
462	let _fixed = version_info.fixed();
463	let _langs = version_info.translation();
464	let _file_info = version_info.file_info();
465	let _source_code = version_info.source_code();
466}
467
468//----------------------------------------------------------------
469
470/// Fixed file info constants.
471pub mod image {
472pub const VS_FF_DEBUG: u32        = 0x01;
473pub const VS_FF_PRERELEASE: u32   = 0x02;
474pub const VS_FF_PATCHED: u32      = 0x04;
475pub const VS_FF_PRIVATEBUILD: u32 = 0x08;
476pub const VS_FF_INFOINFERRED: u32 = 0x10;
477pub const VS_FF_SPECIALBUILD: u32 = 0x20;
478
479pub const VOS_UNKNOWN: u32    = 0x00000000;
480pub const VOS_DOS: u32        = 0x00010000;
481pub const VOS_OS216: u32      = 0x00020000;
482pub const VOS_OS232: u32      = 0x00030000;
483pub const VOS_NT: u32         = 0x00040000;
484pub const VOS__WINDOWS16: u32 = 0x00000001;
485pub const VOS__PM16: u32      = 0x00000002;
486pub const VOS__PM32: u32      = 0x00000003;
487pub const VOS__WINDOWS32: u32 = 0x00000004;
488
489pub const VFT_UNKNOWN: u32    = 0x00000000;
490pub const VFT_APP: u32        = 0x00000001;
491pub const VFT_DLL: u32        = 0x00000002;
492pub const VFT_DRV: u32        = 0x00000003;
493pub const VFT_FONT: u32       = 0x00000004;
494pub const VFT_VXD: u32        = 0x00000005;
495pub const VFT_STATIC_LIB: u32 = 0x00000007;
496
497pub const VFT2_UNKNOWN: u32               = 0x00000000;
498
499pub const VFT2_DRV_PRINTER: u32           = 0x00000001;
500pub const VFT2_DRV_KEYBOARD: u32          = 0x00000002;
501pub const VFT2_DRV_LANGUAGE: u32          = 0x00000003;
502pub const VFT2_DRV_DISPLAY: u32           = 0x00000004;
503pub const VFT2_DRV_MOUSE: u32             = 0x00000005;
504pub const VFT2_DRV_NETWORK: u32           = 0x00000006;
505pub const VFT2_DRV_SYSTEM: u32            = 0x00000007;
506pub const VFT2_DRV_INSTALLABLE: u32       = 0x00000008;
507pub const VFT2_DRV_SOUND: u32             = 0x00000009;
508pub const VFT2_DRV_COMM: u32              = 0x0000000A;
509pub const VFT2_DRV_VERSIONED_PRINTER: u32 = 0x0000000C;
510
511pub const VFT2_FONT_RASTER: u32           = 0x00000001;
512pub const VFT2_FONT_VECTOR: u32           = 0x00000002;
513pub const VFT2_FONT_TRUETYPE: u32         = 0x00000003;
514}
515
516//----------------------------------------------------------------
517// This is an absolutely god awful format...
518
519#[derive(Copy, Clone, Debug, Eq, PartialEq)]
520struct TLV<'a> {
521	pub key: &'a [u16],
522	pub value: &'a [u16], // DWORD aligned
523	pub children: &'a [u16], // DWORD aligned
524}
525#[derive(Copy, Clone, Debug, Eq, PartialEq)]
526enum ValueLengthType { Zero, Bytes, Words }
527#[derive(Clone)]
528struct Parser<'a> {
529	words: &'a [u16],
530	vlt: ValueLengthType,
531}
532impl<'a> Iterator for Parser<'a> {
533	type Item = Result<TLV<'a>>;
534	fn next(&mut self) -> Option<Result<TLV<'a>>> {
535		if self.words.len() == 0 {
536			return None;
537		}
538		let result = parse_tlv(self);
539		// If the parser errors, ensure the Iterator stops
540		if result.is_err() {
541			self.words = &self.words[self.words.len()..];
542		}
543		Some(result)
544	}
545}
546impl<'a> Parser<'a> {
547	pub(crate) fn new_zero(words: &'a [u16]) -> Parser<'a> {
548		Parser { words, vlt: ValueLengthType::Zero }
549	}
550	pub(crate) fn new_bytes(words: &'a [u16]) -> Parser<'a> {
551		Parser { words, vlt: ValueLengthType::Bytes }
552	}
553	pub(crate) fn new_words(words: &'a [u16]) -> Parser<'a> {
554		Parser { words, vlt: ValueLengthType::Words }
555	}
556}
557fn parse_tlv<'a>(state: &mut Parser<'a>) -> Result<TLV<'a>> {
558	let mut words = state.words;
559	// Parse the first three words from the TLV structure:
560	// wLength, wValueLength and wType (plus at least zero terminator of szKey)
561	if words.len() < 4 {
562		return Err(Error::Invalid);
563	}
564	// This is tricky, the struct contains a fixed and variable length parts
565	// However the length field includes the size of the fixed part
566	// Further complicating things, if the variable length part is absent the total length is set to zero (?!)
567	let length = cmp::max(4, words[0] as usize / 2);
568	// Oh god why, interpret the value_length
569	let value_length = match state.vlt {
570		ValueLengthType::Zero if words[1] == 0 => 0,
571		ValueLengthType::Zero => return Err(Error::Invalid),
572		ValueLengthType::Bytes => words[1] as usize / 2,
573		ValueLengthType::Words => words[1] as usize,
574	};
575	// let wType = words[2];
576
577	// Split the input where this structure ends and the next sibling begins
578	if length > words.len() {
579		return Err(Error::Invalid);
580	}
581	// The length does not contain padding to align to a 32-bit boundary
582	state.words = &words[cmp::min(length.align_to(2), words.len())..];
583	words = &words[..length];
584
585	// Parse the nul terminated szKey
586	let key = wstrn(&words[3..]);
587	if words[3..].len() == key.len() {
588		return Err(Error::Invalid);
589	}
590
591	// Padding for the Value
592	words = &words[key.len().align_to(2) + 4..];
593
594	// Split the remaining words between the Value and Children
595	if value_length > words.len() {
596		return Err(Error::Invalid);
597	}
598	let value = &words[..value_length];
599	// The length does not contain padding to align to a 32-bit boundary
600	let children = &words[cmp::min(value.len().align_to(2), words.len())..];
601
602	Ok(TLV { key, value, children })
603}
604
605#[test]
606fn test_parse_tlv_oob()
607{
608	let mut parser;
609
610	// TLV header too short
611	parser = Parser::new_zero(&[0, 0]);
612	assert_eq!(parser.next(), Some(Err(Error::Invalid)));
613	assert_eq!(parser.next(), None);
614
615	// TLV length field larger than the data
616	parser = Parser::new_zero(&[12, 0, 0, 0]);
617	assert_eq!(parser.next(), Some(Err(Error::Invalid)));
618	assert_eq!(parser.next(), None);
619
620	// TLV key not nul terminated
621	parser = Parser::new_zero(&[16, 0, 1, 20, 20, 20, 20, 20]);
622	assert_eq!(parser.next(), Some(Err(Error::Invalid)));
623	assert_eq!(parser.next(), None);
624
625	// TLV value field larger than the data
626	parser = Parser::new_zero(&[8, 10, 0, 0, 0, 0]);
627	assert_eq!(parser.next(), Some(Err(Error::Invalid)));
628	assert_eq!(parser.next(), None);
629}
630
631#[test]
632fn test_parse_254() {
633	static WORDS: [u16; 397] = [
634		794, 52, 0, 86, 83, 95, 86, 69, 82, 83, 73, 79, 78, 95, 73, 78,
635		70, 79, 0, 0, 1213, 65263, 0, 1, 607, 22, 25, 2013, 607, 22, 25, 2013,
636		63, 0, 0, 0, 4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 68, 0,
637		1, 86, 97, 114, 70, 105, 108, 101, 73, 110, 102, 111, 0, 0, 36, 4,
638		0, 84, 114, 97, 110, 115, 108, 97, 116, 105, 111, 110, 0, 0, 0, 1200,
639		634, 0, 1, 83, 116, 114, 105, 110, 103, 70, 105, 108, 101, 73, 110, 102,
640		111, 0, 598, 0, 1, 48, 48, 48, 48, 48, 52, 98, 48, 0, 58, 13,
641		1, 67, 111, 109, 112, 97, 110, 121, 78, 97, 109, 101, 0, 0, 66, 69,
642		46, 69, 115, 115, 101, 110, 116, 105, 97, 108, 0, 0, 66, 13, 1, 70,
643		105, 108, 101, 68, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 0, 0,
644		66, 69, 46, 69, 115, 115, 101, 110, 116, 105, 97, 108, 0, 0, 62, 15,
645		1, 70, 105, 108, 101, 86, 101, 114, 115, 105, 111, 110, 0, 0, 50, 50,
646		46, 54, 48, 55, 46, 50, 48, 49, 51, 46, 50, 53, 0, 0, 66, 17,
647		1, 73, 110, 116, 101, 114, 110, 97, 108, 78, 97, 109, 101, 0, 66, 69,
648		46, 69, 115, 115, 101, 110, 116, 105, 97, 108, 46, 100, 108, 108, 0, 0,
649		40, 2, 1, 76, 101, 103, 97, 108, 67, 111, 112, 121, 114, 105, 103, 104,
650		116, 0, 32, 0, 74, 17, 1, 79, 114, 105, 103, 105, 110, 97, 108, 70,
651		105, 108, 101, 110, 97, 109, 101, 0, 66, 69, 46, 69, 115, 115, 101, 110,
652		116, 105, 97, 108, 46, 100, 108, 108, 0, 0, 58, 13, 1, 80, 114, 111,
653		100, 117, 99, 116, 78, 97, 109, 101, 0, 0, 66, 69, 46, 69, 115, 115,
654		101, 110, 116, 105, 97, 108, 0, 0, 66, 15, 1, 80, 114, 111, 100, 117,
655		99, 116, 86, 101, 114, 115, 105, 111, 110, 0, 50, 50, 46, 54, 48, 55,
656		46, 50, 48, 49, 51, 46, 50, 53, 0, 0, 70, 15, 1, 65, 115, 115,
657		101, 109, 98, 108, 121, 32, 86, 101, 114, 115, 105, 111, 110, 0, 50, 50,
658		46, 54, 48, 55, 46, 50, 48, 49, 51, 46, 50, 53, 0];
659
660	let vi = VersionInfo { words: &WORDS };
661	let fi = vi.file_info();
662	assert!(fi.fixed.is_some());
663
664	let mut strings = HashMap::new();
665	strings.insert(Language { lang_id: 0, charset_id: 1200 }, {
666		let mut strings = HashMap::new();
667		strings.insert(String::from("FileDescription"), String::from("BE.Essential"));
668		strings.insert(String::from("Assembly Version"), String::from("22.607.2013.25"));
669		strings.insert(String::from("ProductVersion"), String::from("22.607.2013.25"));
670		strings.insert(String::from("CompanyName"), String::from("BE.Essential"));
671		strings.insert(String::from("OriginalFilename"), String::from("BE.Essential.dll"));
672		strings.insert(String::from("FileVersion"), String::from("22.607.2013.25"));
673		strings.insert(String::from("InternalName"), String::from("BE.Essential.dll"));
674		strings.insert(String::from("LegalCopyright"), String::from(" "));
675		strings.insert(String::from("ProductName"), String::from("BE.Essential"));
676		strings
677	});
678
679	assert_eq!(fi.strings, strings);
680	assert_eq!(fi.langs, &[Language { lang_id: 0, charset_id: 1200 }]);
681	// panic!("{:#?}", fi);
682}