1#[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#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
61#[repr(C)]
62pub struct Language {
63 pub lang_id: u16,
65 pub charset_id: u16,
67}
68unsafe impl Pod for Language {}
69impl Language {
70 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#[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 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 pub fn fixed(self) -> Option<&'a VS_FIXEDFILEINFO> {
124 let mut this = QueryFixed(None);
125 self.visit(&mut this);
126 this.0
127 }
128 pub fn translation(self) -> &'a [Language] {
132 let mut this = QueryTranslation(&[]);
133 self.visit(&mut this);
134 this.0
135 }
136 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 pub fn strings<F: FnMut(&str, &str)>(self, lang: Language, f: F) {
148 self.visit(&mut QueryStrings { lang, f });
149 }
150 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 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 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 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 visit.enter_scope(1);
194 if file_info.key == &self::strings::StringFileInfo {
195 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 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 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 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#[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 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#[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#[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
436mod strings {
439 #![allow(non_upper_case_globals)]
440 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 }
457
458#[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
468pub 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#[derive(Copy, Clone, Debug, Eq, PartialEq)]
520struct TLV<'a> {
521 pub key: &'a [u16],
522 pub value: &'a [u16], pub children: &'a [u16], }
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 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 if words.len() < 4 {
562 return Err(Error::Invalid);
563 }
564 let length = cmp::max(4, words[0] as usize / 2);
568 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 if length > words.len() {
579 return Err(Error::Invalid);
580 }
581 state.words = &words[cmp::min(length.align_to(2), words.len())..];
583 words = &words[..length];
584
585 let key = wstrn(&words[3..]);
587 if words[3..].len() == key.len() {
588 return Err(Error::Invalid);
589 }
590
591 words = &words[key.len().align_to(2) + 4..];
593
594 if value_length > words.len() {
596 return Err(Error::Invalid);
597 }
598 let value = &words[..value_length];
599 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 parser = Parser::new_zero(&[0, 0]);
612 assert_eq!(parser.next(), Some(Err(Error::Invalid)));
613 assert_eq!(parser.next(), None);
614
615 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 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 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 }