Skip to main content

goblin_experimental/pe/
resource.rs

1use crate::error;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::fmt;
5use core::ops::Not;
6use log::debug;
7use scroll::{Pread, Pwrite, SizeWith};
8
9use crate::pe::data_directories;
10use crate::pe::options;
11use crate::pe::section_table;
12use crate::pe::utils;
13
14/// Size of `wchar_t` in C (aka [`u16`] in Rust)
15pub(super) const SIZE_OF_WCHAR: usize = core::mem::size_of::<u16>();
16/// Converts [`u8`] slice into a vector of [`u16`] and then utf-16 [`String`].
17///
18/// This function assumes that input bytes are multiple of `2`.
19pub(super) fn to_utf16_string(bytes: &[u8]) -> String {
20    let u16_slice = bytes
21        .chunks(2)
22        .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
23        .take_while(|&wchar| wchar != 0)
24        .collect::<Vec<_>>();
25    String::from_utf16_lossy(&u16_slice)
26}
27/// Performs arbitrary alignment of values based on homogeneous numerical types.
28#[inline]
29pub(super) fn align_up<N>(value: N, align: N) -> N
30where
31    N: core::ops::Add<Output = N>
32        + core::ops::Not<Output = N>
33        + core::ops::BitAnd<Output = N>
34        + core::ops::Sub<Output = N>
35        + core::cmp::PartialEq
36        + core::marker::Copy,
37    u8: Into<N>,
38{
39    debug_assert!(align != 0u8.into(), "Align must be non-zero");
40    (value + align - 1u8.into()) & !(align - 1u8.into())
41}
42
43/// Windows resource type identifier for cursors.
44pub const RT_CURSOR: u16 = 1;
45/// Windows resource type identifier for bitmaps.
46pub const RT_BITMAP: u16 = 2;
47/// Windows resource type identifier for icons.
48pub const RT_ICON: u16 = 3;
49/// Windows resource type identifier for menus.
50pub const RT_MENU: u16 = 4;
51/// Windows resource type identifier for dialog boxes.
52pub const RT_DIALOG: u16 = 5;
53/// Windows resource type identifier for string tables.
54pub const RT_STRING: u16 = 6;
55/// Windows resource type identifier for font directories.
56pub const RT_FONTDIR: u16 = 7;
57/// Windows resource type identifier for fonts.
58pub const RT_FONT: u16 = 8;
59/// Windows resource type identifier for accelerators.
60pub const RT_ACCELERATOR: u16 = 9;
61/// Windows resource type identifier for raw data.
62pub const RT_RCDATA: u16 = 10;
63/// Windows resource type identifier for message tables.
64pub const RT_MESSAGETABLE: u16 = 11;
65/// Windows resource type identifier for group cursors.
66pub const RT_GROUP_CURSOR: u16 = 12;
67/// Windows resource type identifier for group icons.
68pub const RT_GROUP_ICON: u16 = 14;
69/// Windows resource type identifier for version information.
70pub const RT_VERSION: u16 = 16;
71/// Windows resource type identifier for dialog includes.
72pub const RT_DLGINCLUDE: u16 = 17;
73/// Windows resource type identifier for Plug and Play resources.
74pub const RT_PLUGPLAY: u16 = 19;
75/// Windows resource type identifier for VxD resources.
76pub const RT_VXD: u16 = 20;
77/// Windows resource type identifier for animated cursors.
78pub const RT_ANICURSOR: u16 = 21;
79/// Windows resource type identifier for animated icons.
80pub const RT_ANIICON: u16 = 22;
81/// Windows resource type identifier for HTML resources.
82pub const RT_HTML: u16 = 23;
83/// Windows resource type identifier for manifests.
84pub const RT_MANIFEST: u16 = 24;
85
86/// Represents an image resource directory in the PE (Portable Executable) format.
87#[repr(C)]
88#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
89pub struct ImageResourceDirectory {
90    /// The characteristics of the resource directory.
91    pub characteristics: u32,
92    /// The timestamp of when the resource directory was created.
93    pub time_date_stamp: u32,
94    /// The major version of the resource directory.
95    pub major_version: u16,
96    /// The minor version of the resource directory.
97    pub minor_version: u16,
98    /// The number of named entries in the resource directory.
99    pub number_of_named_entries: u16,
100    /// The number of ID entries in the resource directory.
101    pub number_of_id_entries: u16,
102}
103
104/// [`ResourceEntry::name_or_id`]: Indicates that the resource name is a string.
105pub const IMAGE_RESOURCE_NAME_IS_STRING: u32 = 0x80000000;
106/// [`ResourceEntry::offset_to_data_or_directory`]: Indicates that the resource data is a directory.
107pub const IMAGE_RESOURCE_DATA_IS_DIRECTORY: u32 = 0x80000000;
108/// A mask used to extract the union field from [`ResourceEntry`].
109pub const IMAGE_RESOURCE_MASK: u32 = 0x7FFFFFFF;
110
111impl<'a> ImageResourceDirectory {
112    pub fn parse(
113        bytes: &'a [u8],
114        dd: data_directories::DataDirectory,
115        sections: &[section_table::SectionTable],
116        file_alignment: u32,
117    ) -> error::Result<Self> {
118        Self::parse_with_opts(
119            bytes,
120            dd,
121            sections,
122            file_alignment,
123            &options::ParseOptions::default(),
124        )
125    }
126
127    pub fn parse_with_opts(
128        bytes: &'a [u8],
129        dd: data_directories::DataDirectory,
130        sections: &[section_table::SectionTable],
131        file_alignment: u32,
132        opts: &options::ParseOptions,
133    ) -> error::Result<Self> {
134        let rva = dd.virtual_address as usize;
135        let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
136            error::Error::Malformed(format!(
137                "Cannot map ImageResourceDirectory rva {:#x} into offset",
138                rva
139            ))
140        })?;
141        let resource_dir = bytes.pread_with(offset, scroll::LE)?;
142        Ok(resource_dir)
143    }
144
145    /// Counts the total number of resource entries (both named and ID entries).
146    ///
147    /// Returns the sum of [`ImageResourceDirectory::number_of_id_entries`] and [`ImageResourceDirectory::number_of_named_entries`]
148    /// from the [`ImageResourceDirectory`].
149    pub fn count(&self) -> u16 {
150        self.number_of_id_entries + self.number_of_named_entries
151    }
152
153    /// Returns the total size of entries in bytes
154    pub fn entries_size(&self) -> usize {
155        self.count() as usize * RESOURCE_ENTRY_SIZE
156    }
157
158    /// Returns the next resource entry iterator
159    pub fn next_iter(&self, offset: usize, bytes: &'a [u8]) -> ResourceEntryIterator<'a> {
160        ResourceEntryIterator {
161            num_resources: self.count() as usize,
162            data: &bytes[offset..offset + self.entries_size()],
163        }
164    }
165}
166
167/// Iterator over [`ResourceData`]
168#[derive(Debug, Copy, Clone)]
169pub struct ResourceEntryIterator<'a> {
170    /// Total number of ID entries and named entries
171    ///
172    /// Must be equals to [`ImageResourceDirectory::number_of_named_entries`] + [`ImageResourceDirectory::number_of_id_entries`]
173    num_resources: usize,
174    /// Raw data of resource direcrory without [`ImageResourceDirectory`] and scoped to [`RESOURCE_ENTRY_SIZE`] * [`Self::num_resources`]
175    data: &'a [u8],
176}
177
178impl Iterator for ResourceEntryIterator<'_> {
179    type Item = error::Result<ResourceEntry>;
180
181    fn next(&mut self) -> Option<Self::Item> {
182        if self.data.is_empty() {
183            return None;
184        }
185
186        Some(match self.data.pread_with(0, scroll::LE) {
187            Ok(func) => {
188                self.data = &self.data[RESOURCE_ENTRY_SIZE..];
189                Ok(func)
190            }
191            Err(error) => {
192                self.data = &[];
193                Err(error.into())
194            }
195        })
196    }
197
198    fn size_hint(&self) -> (usize, Option<usize>) {
199        let len = self.data.len() / RESOURCE_ENTRY_SIZE;
200        (len, Some(len))
201    }
202}
203
204impl<'a> ResourceEntryIterator<'a> {
205    /// Find the resource entry by its resource ID.
206    pub fn find_by_id(&self, id: u16) -> error::Result<Option<ResourceEntry>> {
207        self.map(|x| {
208            x.and_then(|x| {
209                if x.id() == Some(id) {
210                    Ok(Some(x))
211                } else {
212                    Ok(None)
213                }
214            })
215        })
216        .find_map(Result::transpose)
217        .transpose()
218    }
219}
220
221/// Represents an entry in a resource data entry structure.
222///
223/// This struct contains information about a specific resource, including
224/// the offset to the resource data, the size of the resource, the code page,
225/// and any reserved fields for future use.
226#[repr(C)]
227#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
228pub struct ResourceDataEntry {
229    /// The offset from the beginning of the resource data directory to the actual
230    /// resource data in memory.
231    ///
232    /// The name of this field is confusing, but this is really a RVA.
233    pub offset_to_data: u32,
234    /// The size of the resource data in bytes.
235    pub size: u32,
236    /// The code page used for the resource data, which specifies the character
237    /// encoding for strings within the resource.
238    pub code_page: u32,
239    /// Reserved field for future use.
240    pub reserved: u32,
241}
242
243/// Represents a resource entry in the PE (Portable Executable) format.
244#[repr(C)]
245#[derive(PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
246pub struct ResourceEntry {
247    /// The name or identifier of the resource entry.
248    pub name_or_id: u32,
249    /// The offset to the resource data or directory.
250    pub offset_to_data_or_directory: u32,
251}
252
253/// Size of [`ResourceEntry`]
254pub const RESOURCE_ENTRY_SIZE: usize = core::mem::size_of::<u64>();
255
256impl fmt::Debug for ResourceEntry {
257    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258        f.debug_struct("ResourceEntry")
259            .field("value", &format_args!("{:#x}", self.value()))
260            .field("name_is_string", &self.name_is_string())
261            .field("name_offset", &format_args!("{:#x}", self.name_offset()))
262            .field("id", &self.id())
263            .field("data_is_directory", &self.data_is_directory())
264            .field(
265                "offset_to_directory",
266                &format_args!("{:#x}", self.offset_to_directory()),
267            )
268            .field(
269                "offset_to_data",
270                &format_args!("{:#x?}", self.offset_to_data()),
271            )
272            .finish()
273    }
274}
275
276impl ResourceEntry {
277    /// Reinterprets the struct as [`u64`]
278    pub fn value(&self) -> u64 {
279        ((self.name_or_id) as u64) << 32 | self.offset_to_data_or_directory as u64
280    }
281
282    /// Checks if the resource name is a string.
283    ///
284    /// Returns `true` if the name is a string, otherwise `false`.
285    pub fn name_is_string(&self) -> bool {
286        self.name_or_id & IMAGE_RESOURCE_NAME_IS_STRING != 0
287    }
288
289    /// Retrieves the offset of the resource name or ID.
290    ///
291    /// If the name is a string, it returns the offset to the string.
292    /// If it is an ID, it returns the ID masked to remove the string flag.
293    pub fn name_offset(&self) -> u32 {
294        self.name_or_id & IMAGE_RESOURCE_MASK
295    }
296
297    /// Retrieves the ID of the resource if the name is not a string.
298    ///
299    /// Returns `Some(u16)` if the name is an ID, otherwise `None`.
300    ///
301    /// One of:
302    /// - [`RT_CURSOR`]
303    /// - [`RT_BITMAP`]
304    /// - [`RT_ICON`]
305    /// - [`RT_MENU`]
306    /// - [`RT_DIALOG`]
307    /// - [`RT_STRING`]
308    /// - [`RT_FONTDIR`]
309    /// - [`RT_FONT`]
310    /// - [`RT_ACCELERATOR`]
311    /// - [`RT_RCDATA`]
312    /// - [`RT_MESSAGETABLE`]
313    /// - [`RT_GROUP_CURSOR`]
314    /// - [`RT_GROUP_ICON`]
315    /// - [`RT_VERSION`]
316    /// - [`RT_DLGINCLUDE`]
317    /// - [`RT_PLUGPLAY`]
318    /// - [`RT_VXD`]
319    /// - [`RT_ANICURSOR`]
320    /// - [`RT_ANIICON`]
321    /// - [`RT_HTML`]
322    /// - [`RT_MANIFEST`]
323    pub fn id(&self) -> Option<u16> {
324        self.name_is_string().not().then(|| self.name_or_id as u16)
325    }
326
327    /// Checks if the resource entry points to a directory.
328    ///
329    /// Returns `true` if the resource data is a directory, otherwise `false`.
330    pub fn data_is_directory(&self) -> bool {
331        self.offset_to_data_or_directory & IMAGE_RESOURCE_DATA_IS_DIRECTORY != 0
332    }
333
334    /// Retrieves the offset to the resource directory.
335    ///
336    /// If the resource entry points to a directory, it returns the offset masked to remove the directory flag.
337    pub fn offset_to_directory(&self) -> u32 {
338        self.offset_to_data_or_directory & IMAGE_RESOURCE_MASK
339    }
340
341    /// Retrieves the offset to the resource data if the entry does not point to a directory.
342    ///
343    /// Returns `Some(u32)` if the resource entry points to data, otherwise `None`.
344    pub fn offset_to_data(&self) -> Option<u32> {
345        self.data_is_directory()
346            .not()
347            .then(|| self.offset_to_data_or_directory)
348    }
349
350    /// Returns the next depth entry of [`ResourceEntry`] if present
351    pub fn next_depth<'a>(&self, bytes: &'a [u8]) -> error::Result<Option<ResourceEntry>> {
352        let mut offset = self.offset_to_directory() as usize;
353
354        let dir = bytes.gread_with::<ImageResourceDirectory>(&mut offset, scroll::LE)?;
355        let iterator = dir.next_iter(offset, bytes);
356        let entries = iterator.collect::<Result<Vec<_>, _>>()?;
357
358        Ok(entries.first().map(|x| *x))
359    }
360
361    /// Returns next depth entry of [`ResourceEntry`] recursively while either `predicate` returns `true` or reach the final depth
362    pub fn recursive_next_depth<'a, P>(
363        &self,
364        bytes: &'a [u8],
365        predicate: P,
366    ) -> error::Result<Option<ResourceEntry>>
367    where
368        P: Fn(&Self) -> bool,
369    {
370        if let Some(next) = self.next_depth(bytes)? {
371            if !predicate(&next) {
372                Ok(Some(next))
373            } else {
374                next.recursive_next_depth(bytes, predicate)
375            }
376        } else {
377            Ok(Some(*self))
378        }
379    }
380}
381
382impl From<u64> for ResourceEntry {
383    fn from(value: u64) -> Self {
384        Self {
385            name_or_id: (value >> 32) as u32,
386            offset_to_data_or_directory: value as u32,
387        }
388    }
389}
390
391/// Represents the resource data associated with a PE (Portable Executable) image.
392#[derive(Debug, Copy, Clone, Default)]
393pub struct ResourceData<'a> {
394    /// The image resource directory containing metadata about the resources.
395    pub image_resource_directory: ImageResourceDirectory,
396    /// The raw data of the resources.
397    data: &'a [u8],
398    /// Version information if present
399    pub version_info: Option<VersionInfo<'a>>,
400    /// Manifest data if present
401    pub manifest_data: Option<ManifestData<'a>>,
402}
403
404impl<'a> ResourceData<'a> {
405    pub fn parse(
406        bytes: &'a [u8],
407        dd: data_directories::DataDirectory,
408        sections: &[section_table::SectionTable],
409        file_alignment: u32,
410    ) -> error::Result<Self> {
411        Self::parse_with_opts(
412            bytes,
413            dd,
414            sections,
415            file_alignment,
416            &options::ParseOptions::default(),
417        )
418    }
419
420    pub fn parse_with_opts(
421        bytes: &'a [u8],
422        dd: data_directories::DataDirectory,
423        sections: &[section_table::SectionTable],
424        file_alignment: u32,
425        opts: &options::ParseOptions,
426    ) -> error::Result<Self> {
427        let image_resource_directory =
428            ImageResourceDirectory::parse_with_opts(bytes, dd, sections, file_alignment, opts)?;
429
430        let rva = dd.virtual_address as usize;
431        let offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
432            error::Error::Malformed(format!(
433                "Cannot map ImageResourceDirectory rva {:#x} into offset",
434                rva
435            ))
436        })?;
437
438        if offset + dd.size as usize > bytes.len() {
439            return Err(error::Error::Malformed(format!(
440                "Resource directory offset ({:#x}) and size ({:#x}) exceeds bytes slice ({:#x})",
441                offset,
442                dd.size,
443                bytes.len()
444            )));
445        }
446        let data = &bytes[offset..offset + dd.size as usize];
447
448        let count = image_resource_directory.count() as usize;
449        let offset = core::mem::size_of::<ImageResourceDirectory>();
450        let size = image_resource_directory.entries_size();
451        if offset + size as usize > data.len() {
452            return Err(error::Error::Malformed(format!(
453                "Resource entry offset ({:#x}) and size ({:#x}) exceeds data slice ({:#x})",
454                offset,
455                size,
456                data.len()
457            )));
458        }
459        let iterator_data = &data[offset..offset + size];
460        let iterator = ResourceEntryIterator {
461            num_resources: count,
462            data: iterator_data,
463        };
464        let version_info =
465            VersionInfo::parse(bytes, data, iterator, sections, file_alignment, opts)?;
466        let manifest_data =
467            ManifestData::parse(bytes, data, iterator, sections, file_alignment, opts)?;
468
469        Ok(ResourceData {
470            image_resource_directory,
471            data,
472            version_info,
473            manifest_data,
474        })
475    }
476
477    /// Counts the total number of resource entries (both named and ID entries).
478    ///
479    /// Returns the sum of [`ImageResourceDirectory::number_of_id_entries`] and [`ImageResourceDirectory::number_of_named_entries`]
480    /// from the [`Self::image_resource_directory`].
481    pub fn count(&self) -> u16 {
482        self.image_resource_directory.count()
483    }
484
485    /// Creates an iterator over the [`ResourceEntry`].
486    ///
487    /// Returns a [`ResourceEntryIterator`] that can be used to iterate over
488    /// the resource entries contained within this resource data.
489    pub fn entries(&self) -> ResourceEntryIterator<'a> {
490        let offset = core::mem::size_of::<ImageResourceDirectory>();
491        let size = self.image_resource_directory.entries_size();
492        // Safety: Panic-free is guaranteed here by Self::parse_with_opts
493        ResourceEntryIterator {
494            num_resources: self.count() as usize,
495            data: &self.data[offset..offset + size],
496        }
497    }
498}
499
500/// [`VsFixedFileInfo::signature`]: The signature for the fixed file information structure in the version resource.
501pub const VS_FFI_SIGNATURE: u32 = 0xFEEF04BD;
502/// [`VsFixedFileInfo::struct_version`]: The structure version for the fixed file information.
503///
504/// NOTE: Typo is inherited from Windows SDK (perhaps typo by Microsoft employee).
505pub const VS_FFI_STRUCVERSION: u32 = 0x00010000;
506/// [`VsFixedFileInfo::file_flags_mask`]: A mask to extract the file flags from the fixed file information.
507pub const VS_FFI_FILEFLAGSMASK: u32 = 0x0000003F;
508
509/// [`VsFixedFileInfo::file_flags`]: Indicates that the file is a debug build.
510pub const VS_FF_DEBUG: u32 = 0x00000001;
511/// [`VsFixedFileInfo::file_flags`]: Indicates that the file is a pre-release version.
512pub const VS_FF_PRERELEASE: u32 = 0x00000002;
513/// [`VsFixedFileInfo::file_flags`]: Indicates that the file has been patched.
514pub const VS_FF_PATCHED: u32 = 0x00000004;
515/// [`VsFixedFileInfo::file_flags`]: Indicates that the file is a private build.
516pub const VS_FF_PRIVATEBUILD: u32 = 0x00000008;
517/// [`VsFixedFileInfo::file_flags`]: Indicates that information about the file is inferred.
518pub const VS_FF_INFOINFERRED: u32 = 0x00000010;
519/// [`VsFixedFileInfo::file_flags`]: Indicates that the file is a special build.
520pub const VS_FF_SPECIALBUILD: u32 = 0x00000020;
521
522// VS_VERSION.dwFileFlags
523
524/// [`VsFixedFileInfo::file_os`]: Indicates an unknown operating system.
525pub const VOS_UNKNOWN: u32 = 0x00000000;
526/// [`VsFixedFileInfo::file_os`]: Indicates the DOS operating system.
527pub const VOS_DOS: u32 = 0x00010000;
528/// [`VsFixedFileInfo::file_os`]: Indicates OS/2 version 1.6 (16-bit).
529pub const VOS_OS216: u32 = 0x00020000;
530/// [`VsFixedFileInfo::file_os`]: Indicates OS/2 version 2.0 (32-bit).
531pub const VOS_OS232: u32 = 0x00030000;
532/// [`VsFixedFileInfo::file_os`]: Indicates the Windows NT operating system.
533pub const VOS_NT: u32 = 0x00040000;
534/// [`VsFixedFileInfo::file_os`]: Indicates the Windows CE operating system.
535pub const VOS_WINCE: u32 = 0x00050000;
536
537// VS_VERSION.dwFileFlags
538
539/// [`VsFixedFileInfo::file_flags`]: Indicates the base operating system type.
540#[doc(alias("VOS__BASE"))]
541pub const VOS_BASE: u32 = 0x00000000;
542/// [`VsFixedFileInfo::file_flags`]: Indicates the Windows 16-bit operating system.
543#[doc(alias("VOS__WINDOWS16"))]
544pub const VOS_WINDOWS16: u32 = 0x00000001;
545/// [`VsFixedFileInfo::file_flags`]: Indicates the Presentation Manager (PM) 16-bit operating system.
546#[doc(alias("VOS__PM16"))]
547pub const VOS_PM16: u32 = 0x00000002;
548/// [`VsFixedFileInfo::file_flags`]: Indicates the Presentation Manager (PM) 32-bit operating system.
549#[doc(alias("VOS__PM32"))]
550pub const VOS_PM32: u32 = 0x00000003;
551/// [`VsFixedFileInfo::file_flags`]: Indicates the Windows 32-bit operating system.
552#[doc(alias("VOS__WINDOWS32"))]
553pub const VOS_WINDOWS32: u32 = 0x00000004;
554
555// VS_VERSION.dwFileOS
556
557/// [`VsFixedFileInfo::file_os`]: Indicates DOS with Windows 16-bit compatibility.
558pub const VOS_DOS_WINDOWS16: u32 = 0x00010001;
559/// [`VsFixedFileInfo::file_os`]: Indicates DOS with Windows 32-bit compatibility.
560pub const VOS_DOS_WINDOWS32: u32 = 0x00010004;
561/// [`VsFixedFileInfo::file_os`]: Indicates OS/2 1.6 with Presentation Manager 16-bit.
562pub const VOS_OS216_PM16: u32 = 0x00020002;
563/// [`VsFixedFileInfo::file_os`]: Indicates OS/2 1.6 with Presentation Manager 32-bit.
564pub const VOS_OS216_PM32: u32 = 0x00030003;
565/// [`VsFixedFileInfo::file_os`]: Indicates Windows NT with Windows 32-bit compatibility.
566pub const VOS_NT_WINDOWS32: u32 = 0x00040004;
567
568// VS_VERSION.dwFileType
569
570/// [`VsFixedFileInfo::file_type`]: Indicates an unknown file type.
571pub const VFT_UNKNOWN: u32 = 0x00000000;
572/// [`VsFixedFileInfo::file_type`]: Indicates an application file type.
573pub const VFT_APP: u32 = 0x00000001;
574/// [`VsFixedFileInfo::file_type`]: Indicates a dynamic link library (DLL) file type.
575pub const VFT_DLL: u32 = 0x00000002;
576/// [`VsFixedFileInfo::file_type`]: Indicates a device driver file type.
577pub const VFT_DRV: u32 = 0x00000003;
578/// [`VsFixedFileInfo::file_type`]: Indicates a font file type.
579pub const VFT_FONT: u32 = 0x00000004;
580/// [`VsFixedFileInfo::file_type`]: Indicates a virtual device driver (VXD) file type.
581pub const VFT_VXD: u32 = 0x00000005;
582/// [`VsFixedFileInfo::file_type`]: Indicates a static library file type.
583pub const VFT_STATIC_LIB: u32 = 0x00000007;
584
585// VS_VERSION.dwFileSubtype for VFT_WINDOWS_DRV
586
587/// [`VsFixedFileInfo::file_subtype`]: Indicates an unknown driver subtype.
588pub const VFT2_UNKNOWN: u32 = 0x00000000;
589/// [`VsFixedFileInfo::file_subtype`]: Indicates a printer driver subtype.
590pub const VFT2_DRV_PRINTER: u32 = 0x00000001;
591/// [`VsFixedFileInfo::file_subtype`]: Indicates a keyboard driver subtype.
592pub const VFT2_DRV_KEYBOARD: u32 = 0x00000002;
593/// [`VsFixedFileInfo::file_subtype`]: Indicates a language driver subtype.
594pub const VFT2_DRV_LANGUAGE: u32 = 0x00000003;
595/// [`VsFixedFileInfo::file_subtype`]: Indicates a display driver subtype.
596pub const VFT2_DRV_DISPLAY: u32 = 0x00000004;
597/// [`VsFixedFileInfo::file_subtype`]: Indicates a mouse driver subtype.
598pub const VFT2_DRV_MOUSE: u32 = 0x00000005;
599/// [`VsFixedFileInfo::file_subtype`]: Indicates a network driver subtype.
600pub const VFT2_DRV_NETWORK: u32 = 0x00000006;
601/// [`VsFixedFileInfo::file_subtype`]: Indicates a system driver subtype.
602pub const VFT2_DRV_SYSTEM: u32 = 0x00000007;
603/// [`VsFixedFileInfo::file_subtype`]: Indicates an installable driver subtype.
604pub const VFT2_DRV_INSTALLABLE: u32 = 0x00000008;
605/// [`VsFixedFileInfo::file_subtype`]: Indicates a sound driver subtype.
606pub const VFT2_DRV_SOUND: u32 = 0x00000009;
607/// [`VsFixedFileInfo::file_subtype`]: Indicates a communication driver subtype.
608pub const VFT2_DRV_COMM: u32 = 0x0000000A;
609/// [`VsFixedFileInfo::file_subtype`]: Indicates an input method driver subtype.
610pub const VFT2_DRV_INPUTMETHOD: u32 = 0x0000000B;
611/// [`VsFixedFileInfo::file_subtype`]: Indicates a versioned printer driver subtype.
612pub const VFT2_DRV_VERSIONED_PRINTER: u32 = 0x0000000C;
613
614// VS_VERSION.dwFileSubtype for VFT_WINDOWS_FONT
615
616/// [`VsFixedFileInfo::file_subtype`]: Indicates a raster font subtype.
617pub const VFT2_FONT_RASTER: u32 = 0x00000001;
618/// [`VsFixedFileInfo::file_subtype`]: Indicates a vector font subtype.
619pub const VFT2_FONT_VECTOR: u32 = 0x00000002;
620/// [`VsFixedFileInfo::file_subtype`]: Indicates a TrueType font subtype.
621pub const VFT2_FONT_TRUETYPE: u32 = 0x00000003;
622
623/// Iterator over [`ResourceString`]
624#[derive(Debug, Copy, Clone)]
625pub struct ResourceStringIterator<'a> {
626    /// The raw data must be scoped to the [`ResourceDataEntry`]
627    pub data: &'a [u8],
628}
629
630impl<'a> Iterator for ResourceStringIterator<'a> {
631    type Item = error::Result<ResourceString<'a>>;
632
633    fn next(&mut self) -> Option<Self::Item> {
634        if self.data.is_empty() {
635            return None;
636        }
637
638        let mut offset = 0;
639        Some(match ResourceString::parse(self.data, &mut offset) {
640            Ok(next) => {
641                debug!(
642                    "Parsed next resource string as size {:#x}: {:#x?}",
643                    offset, next?
644                );
645                self.data = &self.data[offset..];
646                Ok(next?)
647            }
648            Err(error) => {
649                self.data = &[];
650                Err(error.into())
651            }
652        })
653    }
654}
655
656/// Represents a resource string entry.
657#[derive(Copy, Clone, PartialEq)]
658pub struct ResourceString<'a> {
659    /// The length, in bytes, of this [`ResourceString`] structure.
660    pub len: u16,
661    /// The size, in words, of the [`ResourceString::value`].
662    ///
663    /// - When [`ResourceString::type`] indicates string data: multiply this field with [`SIZE_OF_WCHAR`] that should be the actual size of [`ResourceString::value`] with null-terminator.
664    /// - Othereise, treat as-is.
665    pub value_len: u16,
666    /// The type of [`ResourceString::value`] in the version resource.
667    ///
668    /// This member is `1` if the version resource contains text data;
669    /// and `0` if the version resource contains binary data, otherwise sometimes an invalid value.
670    pub r#type: u16,
671    /// An arbitrary null-terminated utf-16 unicode string.
672    pub key: &'a [u8],
673    /// An arbitrary null-terminated utf-16 unicode string or binary data depends on [`ResourceString::type`].
674    pub value: &'a [u8],
675}
676
677/// Fields in [`ResourceString`] must be aligned with size of [`u32`] while parsing
678pub const RESOURCE_STRING_FIELD_ALIGNMENT: usize = core::mem::size_of::<u32>();
679
680impl fmt::Debug for ResourceString<'_> {
681    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
682        let mut debug_struct = f.debug_struct("ResourceString");
683
684        debug_struct.field("len", &format_args!("{:#x}", self.len));
685
686        if self.is_text_data() {
687            // If this is string data, acrual size is multiple of 2 (sizeof wchar_t)
688            debug_struct.field(
689                "value_len",
690                &format_args!(
691                    "{:#x} ({} bytes)",
692                    self.value_len,
693                    self.value_len * SIZE_OF_WCHAR as u16
694                ),
695            );
696        } else {
697            debug_struct.field("value_len", &format_args!("{:#x}", self.value_len));
698        }
699
700        debug_struct
701            .field(
702                "type",
703                &format_args!(
704                    "{} ({})",
705                    self.r#type,
706                    match self.r#type {
707                        0 => "Binary Data",
708                        1 => "String Data",
709                        _ => "Unknown",
710                    }
711                ),
712            )
713            .field("key", &self.key_string())
714            .field(
715                "key_slice",
716                &format_args!("{:02x?} ({} bytes)", self.key, self.key.len()),
717            );
718
719        if self.is_text_data() && self.value_len > 0 {
720            debug_struct.field("value", &self.value_string());
721        }
722
723        debug_struct
724            .field(
725                "value_slice",
726                &format_args!(
727                    "{:02x?} ({} bytes, {})",
728                    self.value,
729                    self.value.len(),
730                    if self.value.len() == self.value_len as usize {
731                        "Correct"
732                    } else {
733                        "Incorrect"
734                    }
735                ),
736            )
737            .finish()
738    }
739}
740
741impl<'a> ResourceString<'a> {
742    pub fn parse(bytes: &'a [u8], offset: &mut usize) -> error::Result<Option<Self>> {
743        let len = bytes.gread_with::<u16>(offset, scroll::LE)?;
744        if len == 0 {
745            return Ok(None);
746        }
747        let value_len = bytes.gread_with::<u16>(offset, scroll::LE)?;
748        let r#type = bytes.gread_with::<u16>(offset, scroll::LE)?;
749        *offset = align_up(*offset, RESOURCE_STRING_FIELD_ALIGNMENT);
750        let key_size = &bytes[*offset..]
751            .chunks(2)
752            .take_while(|x| u16::from_le_bytes([x[0], x[1]]) != 0u16)
753            .count()
754            * SIZE_OF_WCHAR;
755        if (*offset - SIZE_OF_WCHAR) + key_size + SIZE_OF_WCHAR > bytes.len() {
756            return Err(error::Error::Malformed(format!(
757                "offset ({:#x}) and key_size ({:#x}) is greater than bytes len {:#x}",
758                offset,
759                key_size,
760                bytes.len()
761            )));
762        }
763        let key =
764            &bytes[*offset - SIZE_OF_WCHAR..*offset - SIZE_OF_WCHAR + key_size + SIZE_OF_WCHAR];
765        *offset += align_up(key.len(), RESOURCE_STRING_FIELD_ALIGNMENT);
766        let real_value_len = align_up(
767            if r#type == 1 {
768                value_len as usize * SIZE_OF_WCHAR
769            } else {
770                value_len as usize
771            },
772            4,
773        );
774        if *offset + (real_value_len as usize) > bytes.len() {
775            return Err(error::Error::Malformed(format!(
776                "offset ({:#x}) and real_value_len ({:#x}) is greater than bytes len {:#x}",
777                offset,
778                real_value_len,
779                bytes.len()
780            )));
781        }
782        let value = &bytes[*offset..*offset + real_value_len as usize];
783        *offset += value.len();
784        Ok(Some(Self {
785            len,
786            value_len,
787            r#type,
788            key,
789            value,
790        }))
791    }
792
793    /// Returns `true` if [`ResourceString::value`] is expected to an null-terminated unicode string
794    pub fn is_text_data(&self) -> bool {
795        self.r#type == 1
796    }
797
798    /// Returns `true` if [`ResourceString::value`] is expected to a binary data
799    pub fn is_binary_data(&self) -> bool {
800        self.r#type == 0
801    }
802
803    /// Converts [`ResourceString::key`] into a [`String`]
804    pub fn key_string(&self) -> String {
805        to_utf16_string(&self.key)
806    }
807
808    /// Converts [`ResourceString::value`] into a [`String`]
809    pub fn value_string(&self) -> String {
810        to_utf16_string(&self.value)
811    }
812}
813
814/// Represents a generic version format, commonly used for file or product versioning within Windows SDK.
815///
816/// The version information is stored in four parts:
817/// - `major`: The major version, typically indicating significant updates or changes.
818/// - `minor`: The minor version, for less impactful updates or feature additions.
819/// - `build`: The build number, often used for tracking internal builds or revisions.
820/// - `revision`: The revision number, generally indicating small fixes or patches.
821#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Default)]
822pub struct VersionField {
823    /// The major version, indicating significant updates or releases.
824    pub major: u16,
825    /// The minor version, indicating smaller feature additions or changes.
826    pub minor: u16,
827    /// The build number, often used to distinguish between internal builds.
828    pub build: u16,
829    /// The revision number, typically used for small fixes or patches.
830    pub revision: u16,
831}
832
833impl VersionField {
834    /// Creates a new [`VersionField`] from the combination of [`u32`] fields.
835    ///
836    /// # Parameters
837    /// - `ms`: The [`u32`] representation of a most significant part, which contains the major and minor version.
838    /// - `ls`: The [`u32`] representation of a least significant part, which contains the build and revision.
839    pub fn from_ms_ls(ms: u32, ls: u32) -> Self {
840        let major = (ms >> 16) as u16;
841        let minor = (ms & 0xFFFF) as u16;
842        let build = (ls >> 16) as u16;
843        let revision = (ls & 0xFFFF) as u16;
844        Self {
845            major,
846            minor,
847            build,
848            revision,
849        }
850    }
851
852    /// Converts [`VersionField`] back to the [`u32`] most significant field.
853    ///
854    /// - Upper 16-bits: Major version (`HIWORD`)
855    /// - Lower 16-bits: Minor version (`LOWORD`)
856    pub fn to_ms(&self) -> u32 {
857        ((self.major as u32) << 16) | (self.minor as u32)
858    }
859
860    /// Converts [`VersionField`] back to the [`u32`] least significant field.
861    ///
862    /// - Upper 16-bits: Build number (`HIWORD`)
863    /// - Lower 16-bits: Revision number (`LOWORD`)
864    pub fn to_ls(&self) -> u32 {
865        ((self.build as u32) << 16) | (self.revision as u32)
866    }
867
868    /// Formats the version as a [`String`] in the format "major.minor.build.revision".
869    pub fn to_string(&self) -> String {
870        format!(
871            "{}.{}.{}.{}",
872            self.major, self.minor, self.build, self.revision
873        )
874    }
875}
876
877/// Represents the fixed file information structure used in the version resource
878/// of a Portable Executable (PE) file.
879#[repr(C)]
880#[derive(PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
881pub struct VsFixedFileInfo {
882    /// The signature of the fixed file information structure. Must be equals to [`VS_FFI_SIGNATURE`].
883    pub signature: u32,
884    /// The version of the structure.
885    pub struct_version: u32,
886    /// The file version (most significant part).
887    pub file_version_ms: u32,
888    /// The file version (least significant part).
889    pub file_version_ls: u32,
890    /// The product version (most significant part).
891    pub product_version_ms: u32,
892    /// The product version (least significant part).
893    pub product_version_ls: u32,
894    /// The mask for the file flags.
895    pub file_flags_mask: u32,
896    /// The file flags that specify characteristics of the file.
897    pub file_flags: u32,
898    /// The operating system that the file is designed for.
899    pub file_os: u32,
900    /// The type of the file (e.g., executable, DLL).
901    pub file_type: u32,
902    /// The subtype of the file (specific to the file type).
903    pub file_subtype: u32,
904    /// The file date (most significant part).
905    pub file_date_ms: u32,
906    /// The file date (least significant part).
907    pub file_date_ls: u32,
908}
909
910impl fmt::Debug for VsFixedFileInfo {
911    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
912        f.debug_struct("VsFixedFileInfo")
913            .field(
914                "signature",
915                &format_args!(
916                    "{:#x} ({})",
917                    &self.signature,
918                    if self.is_valid() { "Valid" } else { "Invalid" }
919                ),
920            )
921            .field(
922                "struct_version",
923                &format_args!("{:#x}", &self.struct_version),
924            )
925            .field(
926                "file_version_ms",
927                &format_args!("{:#x}", &self.file_version_ms),
928            )
929            .field(
930                "file_version_ls",
931                &format_args!("{:#x}", &self.file_version_ls),
932            )
933            .field(
934                "product_version_ms",
935                &format_args!("{:#x}", &self.product_version_ms),
936            )
937            .field(
938                "product_version_ls",
939                &format_args!("{:#x}", &self.product_version_ls),
940            )
941            .field(
942                "file_flags_mask",
943                &format_args!("{:#x}", &self.file_flags_mask),
944            )
945            .field("file_flags", &format_args!("{:#x}", &self.file_flags))
946            .field("file_os", &format_args!("{:#x}", &self.file_os))
947            .field("file_type", &format_args!("{:#x}", &self.file_type))
948            .field("file_subtype", &format_args!("{:#x}", &self.file_subtype))
949            .field("file_date_ms", &format_args!("{:#x}", &self.file_date_ms))
950            .field("file_date_ls", &format_args!("{:#x}", &self.file_date_ls))
951            .finish()
952    }
953}
954
955/// Language and codepage identifier for U.S. English with Unicode (UTF-16) in Windows SDK version info.
956///
957/// This identifier consists of the following components:
958/// - `04`: Primary language identifier for English.
959/// - `09`: Sub-language identifier for United States.
960/// - `04E4`: Codepage identifier for Unicode (UTF-16).
961///
962/// This value may present in [`ResourceString::key`] without no dedicated value data.
963pub const VERSION_INFO_US_ENGLISH_UNICODE: &str = "040904E4";
964/// A [`ResourceString::key`] of [`VsFixedFileInfo`]
965pub const VS_VERSION_INFO_KEY: &str = "VS_VERSION_INFO";
966
967impl VsFixedFileInfo {
968    /// Returns `true` if [`Self::signature`] equals to [`VS_FFI_SIGNATURE`], otherwise `false`.
969    pub fn is_valid(&self) -> bool {
970        self.signature == VS_FFI_SIGNATURE
971    }
972
973    /// Reinterprets [`VsFixedFileInfo::file_version_ms`] and [`VsFixedFileInfo::file_version_ls`] into a generic [`VersionField`].
974    pub fn file_version(&self) -> VersionField {
975        VersionField::from_ms_ls(self.file_date_ms, self.file_date_ls)
976    }
977
978    /// Reinterprets [`VsFixedFileInfo::product_version_ms`] and [`VsFixedFileInfo::product_version_ls`] into a generic [`VersionField`].
979    pub fn product_version(&self) -> VersionField {
980        VersionField::from_ms_ls(self.product_version_ms, self.product_version_ls)
981    }
982}
983
984/// Represents a collection of string-based file information in a version resource.
985///
986/// This struct holds various metadata attributes about a file, such as the company name,
987/// file description, version information, and copyright details. Each field is optional and
988/// can be absent if the information is not available.
989#[derive(Copy, Clone)]
990pub struct StringFileInfo<'a> {
991    /// Additional information for diagnostic purposes. Can be of arbitrary length.
992    pub comments: Option<&'a [u8]>,
993    /// The name of the company that produced the file, e.g., "Microsoft Corporation".
994    pub company_name: Option<&'a [u8]>,
995    /// A description of the file suitable for presentation to users, e.g., "Keyboard driver for AT-style keyboards".
996    pub file_description: Option<&'a [u8]>,
997    /// The version of the file, e.g., "3.00A" or "5.00.RC2".
998    pub file_version: Option<&'a [u8]>,
999    /// The internal name of the file, which may include module names for DLLs or device names.
1000    pub internal_name: Option<&'a [u8]>,
1001    /// Copyright notices and trademarks related to the file, formatted as "Copyright Microsoft Corp. 1990 1994".
1002    pub legal_copyright: Option<&'a [u8]>,
1003    /// Trademarks and registered trademarks associated with the file, e.g., "Windows is a trademark of Microsoft Corporation".
1004    pub legal_trademarks: Option<&'a [u8]>,
1005    /// The original name of the file (without a path), used to determine if it has been renamed.
1006    pub original_filename: Option<&'a [u8]>,
1007    /// Information about who, where, and why the private version of the file was built,
1008    /// applicable only if the [`VS_FF_PRIVATEBUILD`] flag is set.
1009    pub private_build: Option<&'a [u8]>,
1010    /// The name of the product with which this file is distributed, e.g., "Microsoft Windows".
1011    pub product_name: Option<&'a [u8]>,
1012    /// The version of the product associated with this file, e.g., "3.00A" or "5.00.RC2".
1013    pub product_version: Option<&'a [u8]>,
1014    /// A description of how this version differs from the normal version, applicable only if the
1015    /// [`VS_FF_SPECIALBUILD`] flag is set.
1016    pub special_build: Option<&'a [u8]>,
1017}
1018
1019impl fmt::Debug for StringFileInfo<'_> {
1020    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1021        f.debug_struct("StringFileInfo")
1022            .field("comments", &format_args!("{:?}", self.comments()))
1023            .field("company_name", &format_args!("{:?}", self.company_name()))
1024            .field(
1025                "file_description",
1026                &format_args!("{:?}", self.file_description()),
1027            )
1028            .field("file_version", &format_args!("{:?}", self.file_version()))
1029            .field("internal_name", &format_args!("{:?}", self.internal_name()))
1030            .field(
1031                "legal_copyright",
1032                &format_args!("{:?}", self.legal_copyright()),
1033            )
1034            .field(
1035                "legal_trademarks",
1036                &format_args!("{:?}", self.legal_trademarks()),
1037            )
1038            .field(
1039                "original_filename",
1040                &format_args!("{:?}", self.original_filename()),
1041            )
1042            .field("private_build", &format_args!("{:?}", self.private_build()))
1043            .field("product_name", &format_args!("{:?}", self.product_name()))
1044            .field(
1045                "product_version",
1046                &format_args!("{:?}", self.product_version()),
1047            )
1048            .field("special_build", &format_args!("{:?}", self.special_build()))
1049            .finish()
1050    }
1051}
1052
1053impl<'a> StringFileInfo<'a> {
1054    fn from_resource_string_iterator(it: ResourceStringIterator<'a>) -> Self {
1055        let find = |s| {
1056            it.filter_map(Result::ok)
1057                .find(|x| x.key_string() == s)
1058                .and_then(|x| Some(x.value))
1059        };
1060
1061        Self {
1062            comments: find("Comments"),
1063            company_name: find("CompanyName"),
1064            file_description: find("FileDescription"),
1065            file_version: find("FileVersion"),
1066            internal_name: find("InternalName"),
1067            legal_copyright: find("LegalCopyright"),
1068            legal_trademarks: find("LegalTrademarks"),
1069            original_filename: find("OriginalFilename"),
1070            private_build: find("PrivateBuild"),
1071            product_name: find("ProductName"),
1072            product_version: find("ProductVersion"),
1073            special_build: find("SpecialBuild"),
1074        }
1075    }
1076
1077    /// Stringize the [`StringFileInfo::comments`] slice into a [`String`].
1078    pub fn comments(&self) -> Option<String> {
1079        self.comments.map(|x| to_utf16_string(x))
1080    }
1081
1082    /// Stringize the [`StringFileInfo::company_name`] slice into a [`String`].
1083    pub fn company_name(&self) -> Option<String> {
1084        self.company_name.map(|x| to_utf16_string(x))
1085    }
1086
1087    /// Stringize the [`StringFileInfo::file_description`] slice into a [`String`].
1088    pub fn file_description(&self) -> Option<String> {
1089        self.file_description.map(|x| to_utf16_string(x))
1090    }
1091
1092    /// Stringize the [`StringFileInfo::file_version`] slice into a [`String`].
1093    pub fn file_version(&self) -> Option<String> {
1094        self.file_version.map(|x| to_utf16_string(x))
1095    }
1096
1097    /// Stringize the [`StringFileInfo::internal_name`] slice into a [`String`].
1098    pub fn internal_name(&self) -> Option<String> {
1099        self.internal_name.map(|x| to_utf16_string(x))
1100    }
1101
1102    /// Stringize the [`StringFileInfo::legal_copyright`] slice into a [`String`].
1103    pub fn legal_copyright(&self) -> Option<String> {
1104        self.legal_copyright.map(|x| to_utf16_string(x))
1105    }
1106
1107    /// Stringize the [`StringFileInfo::legal_trademarks`] slice into a [`String`].
1108    pub fn legal_trademarks(&self) -> Option<String> {
1109        self.legal_trademarks.map(|x| to_utf16_string(x))
1110    }
1111
1112    /// Stringize the [`StringFileInfo::original_filename`] slice into a [`String`].
1113    pub fn original_filename(&self) -> Option<String> {
1114        self.original_filename.map(|x| to_utf16_string(x))
1115    }
1116
1117    /// Stringize the [`StringFileInfo::private_build`] slice into a [`String`].
1118    pub fn private_build(&self) -> Option<String> {
1119        self.private_build.map(|x| to_utf16_string(x))
1120    }
1121
1122    /// Stringize the [`StringFileInfo::product_name`] slice into a [`String`].
1123    pub fn product_name(&self) -> Option<String> {
1124        self.product_name.map(|x| to_utf16_string(x))
1125    }
1126
1127    /// Stringize the [`StringFileInfo::product_version`] slice into a [`String`].
1128    pub fn product_version(&self) -> Option<String> {
1129        self.product_version.map(|x| to_utf16_string(x))
1130    }
1131
1132    /// Stringize the [`StringFileInfo::special_build`] slice into a [`String`].
1133    pub fn special_build(&self) -> Option<String> {
1134        self.special_build.map(|x| to_utf16_string(x))
1135    }
1136}
1137
1138/// Represents a version information
1139#[derive(Copy, Clone)]
1140pub struct VersionInfo<'a> {
1141    /// Raw data of entire [`RT_VERSION`] area.
1142    data: &'a [u8],
1143    /// Fixed file information.
1144    pub fixed_info: Option<VsFixedFileInfo>,
1145    /// Dynamic key-value file information.
1146    pub string_info: StringFileInfo<'a>,
1147}
1148
1149impl fmt::Debug for VersionInfo<'_> {
1150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1151        f.debug_struct("VersionInfo")
1152            .field(
1153                "data",
1154                &format_args!("{:02x?} ({} bytes)", &self.data, self.data.len()),
1155            )
1156            .field("fixed_info", &self.fixed_info)
1157            .field("string_info", &self.string_info)
1158            .finish()
1159    }
1160}
1161
1162impl<'a> VersionInfo<'a> {
1163    pub fn parse(
1164        pe: &'a [u8],
1165        bytes: &'a [u8],
1166        it: ResourceEntryIterator<'a>,
1167        sections: &[section_table::SectionTable],
1168        file_alignment: u32,
1169        opts: &options::ParseOptions,
1170    ) -> error::Result<Option<Self>> {
1171        if let Some(entry) = it.find_by_id(RT_VERSION)? {
1172            let offset_to_data =
1173                match entry.recursive_next_depth(bytes, |e| e.offset_to_data().is_none())? {
1174                    Some(next) => match next.offset_to_data() {
1175                        Some(offset_to_data) => offset_to_data,
1176                        None => return Ok(None),
1177                    },
1178                    None => return Ok(None),
1179                };
1180            let mut offset = offset_to_data as usize;
1181            let data_entry = bytes.gread_with::<ResourceDataEntry>(&mut offset, scroll::LE)?;
1182            let rva = data_entry.offset_to_data as usize;
1183            offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
1184                error::Error::Malformed(format!(
1185                    "Cannot map ResourceDataEntry rva {:#x} into offset",
1186                    rva
1187                ))
1188            })?;
1189
1190            if offset + data_entry.size as usize > pe.len() {
1191                return Err(error::Error::Malformed(format!(
1192                    "offset ({:#x}) and data_entry.size ({:#x}) is greater than pe len {:#x}",
1193                    offset,
1194                    data_entry.size,
1195                    bytes.len()
1196                )));
1197            }
1198            let data = &pe[offset..offset + data_entry.size as usize];
1199            let iterator = ResourceStringIterator { data };
1200            let strings = iterator.collect::<Result<Vec<_>, _>>()?;
1201
1202            let fixed_info = match strings
1203                .iter()
1204                .find(|x| x.key_string() == VS_VERSION_INFO_KEY)
1205            {
1206                Some(version_info) => Some(version_info.value.pread_with(0, scroll::LE)?),
1207                None => None,
1208            };
1209            let string_info = StringFileInfo::from_resource_string_iterator(iterator);
1210
1211            Ok(Some(Self {
1212                data,
1213                fixed_info,
1214                string_info,
1215            }))
1216        } else {
1217            Ok(None)
1218        }
1219    }
1220}
1221
1222/// Represents a manifest data within resource in the PE (Portable Executable) format.
1223#[derive(Copy, Clone, Default)]
1224pub struct ManifestData<'a> {
1225    /// The raw binary data of the manifest
1226    pub data: &'a [u8],
1227}
1228
1229impl fmt::Debug for ManifestData<'_> {
1230    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1231        f.debug_struct("ManifestData")
1232            .field("value", &format_args!("{:02x?}", self.data))
1233            .finish()
1234    }
1235}
1236
1237impl<'a> ManifestData<'a> {
1238    pub fn parse(
1239        pe: &'a [u8],
1240        bytes: &'a [u8],
1241        it: ResourceEntryIterator<'a>,
1242        sections: &[section_table::SectionTable],
1243        file_alignment: u32,
1244        opts: &options::ParseOptions,
1245    ) -> error::Result<Option<Self>> {
1246        if let Some(entry) = it.find_by_id(RT_MANIFEST)? {
1247            let offset_to_data =
1248                match entry.recursive_next_depth(bytes, |e| e.offset_to_data().is_none())? {
1249                    Some(next) => match next.offset_to_data() {
1250                        Some(offset_to_data) => offset_to_data,
1251                        None => return Ok(None),
1252                    },
1253                    None => return Ok(None),
1254                };
1255            let mut offset = offset_to_data as usize;
1256            let data_entry = bytes.gread_with::<ResourceDataEntry>(&mut offset, scroll::LE)?;
1257            let rva = data_entry.offset_to_data as usize;
1258            offset = utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| {
1259                error::Error::Malformed(format!(
1260                    "Cannot map ResourceDataEntry rva {:#x} into offset",
1261                    rva
1262                ))
1263            })?;
1264
1265            if offset + data_entry.size as usize > pe.len() {
1266                return Err(error::Error::Malformed(format!(
1267                    "offset ({:#x}) and data_entry.size ({:#x}) is greater than pe len {:#x}",
1268                    offset,
1269                    data_entry.size,
1270                    bytes.len()
1271                )));
1272            }
1273            let data = &pe[offset..offset + data_entry.size as usize];
1274            Ok(Some(Self { data }))
1275        } else {
1276            Ok(None)
1277        }
1278    }
1279}
1280
1281#[cfg(test)]
1282mod tests {
1283    use super::{
1284        ResourceEntry, ResourceStringIterator, VersionField, VFT_APP, VOS_NT_WINDOWS32,
1285        VS_FFI_FILEFLAGSMASK, VS_FFI_SIGNATURE, VS_FFI_STRUCVERSION, VS_VERSION_INFO_KEY,
1286    };
1287
1288    const HAS_NO_RES: &[u8] = include_bytes!("../../tests/bins/pe/has_no_res.exe.bin");
1289    const HAS_RES_FULL_VERSION_AND_MANIFEST: &[u8] =
1290        include_bytes!("../../tests/bins/pe/has_res_full_version_and_manifest.exe.bin");
1291
1292    /// Binary representation of following default LLD manifest (`/MANIFEST`) expect as UTF-8.
1293    ///
1294    /// ```xml
1295    /// <?xml version='1.0' encoding='UTF-8' standalone='yes'?>
1296    /// <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
1297    ///     <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
1298    ///         <security>
1299    ///             <requestedPrivileges>
1300    ///                 <requestedExecutionLevel level='asInvoker' uiAccess='false' />
1301    ///             </requestedPrivileges>
1302    ///         </security>
1303    ///     </trustInfo>
1304    /// </assembly>
1305    ///
1306    /// ```
1307    ///
1308    /// NOTE: Break on last line is intentional.
1309    const EXPECTED_MANIFEST: &[u8; 413] = &[
1310        0x3C, 0x3F, 0x78, 0x6D, 0x6C, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x27,
1311        0x31, 0x2E, 0x30, 0x27, 0x20, 0x65, 0x6E, 0x63, 0x6F, 0x64, 0x69, 0x6E, 0x67, 0x3D, 0x27,
1312        0x55, 0x54, 0x46, 0x2D, 0x38, 0x27, 0x20, 0x73, 0x74, 0x61, 0x6E, 0x64, 0x61, 0x6C, 0x6F,
1313        0x6E, 0x65, 0x3D, 0x27, 0x79, 0x65, 0x73, 0x27, 0x3F, 0x3E, 0x0D, 0x0A, 0x3C, 0x61, 0x73,
1314        0x73, 0x65, 0x6D, 0x62, 0x6C, 0x79, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3D, 0x27, 0x75,
1315        0x72, 0x6E, 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73, 0x2D, 0x6D, 0x69, 0x63, 0x72,
1316        0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A, 0x61, 0x73, 0x6D, 0x2E, 0x76,
1317        0x31, 0x27, 0x20, 0x6D, 0x61, 0x6E, 0x69, 0x66, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x73,
1318        0x69, 0x6F, 0x6E, 0x3D, 0x27, 0x31, 0x2E, 0x30, 0x27, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20,
1319        0x20, 0x3C, 0x74, 0x72, 0x75, 0x73, 0x74, 0x49, 0x6E, 0x66, 0x6F, 0x20, 0x78, 0x6D, 0x6C,
1320        0x6E, 0x73, 0x3D, 0x22, 0x75, 0x72, 0x6E, 0x3A, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x73,
1321        0x2D, 0x6D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x2D, 0x63, 0x6F, 0x6D, 0x3A,
1322        0x61, 0x73, 0x6D, 0x2E, 0x76, 0x33, 0x22, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
1323        0x20, 0x20, 0x20, 0x3C, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x3E, 0x0D, 0x0A,
1324        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x72, 0x65,
1325        0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6C, 0x65, 0x67,
1326        0x65, 0x73, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1327        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65,
1328        0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6F, 0x6E, 0x4C, 0x65, 0x76, 0x65, 0x6C,
1329        0x20, 0x6C, 0x65, 0x76, 0x65, 0x6C, 0x3D, 0x27, 0x61, 0x73, 0x49, 0x6E, 0x76, 0x6F, 0x6B,
1330        0x65, 0x72, 0x27, 0x20, 0x75, 0x69, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x3D, 0x27, 0x66,
1331        0x61, 0x6C, 0x73, 0x65, 0x27, 0x20, 0x2F, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
1332        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
1333        0x74, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6C, 0x65, 0x67, 0x65, 0x73, 0x3E, 0x0D,
1334        0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x73, 0x65, 0x63, 0x75,
1335        0x72, 0x69, 0x74, 0x79, 0x3E, 0x0D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x2F, 0x74, 0x72,
1336        0x75, 0x73, 0x74, 0x49, 0x6E, 0x66, 0x6F, 0x3E, 0x0D, 0x0A, 0x3C, 0x2F, 0x61, 0x73, 0x73,
1337        0x65, 0x6D, 0x62, 0x6C, 0x79, 0x3E, 0x0D, 0x0A,
1338    ];
1339
1340    /// Binary representation of entire [`super::RT_VERSION`] data of `python-3.11.3-amd64.exe`
1341    /// to be coverted to appropriate [`super::VersionInfo`]
1342    ///
1343    /// This buffer is not aligned with 8 bytes and has no paddings
1344    const PYTHON_INSTALLER_VERSION_INFO: &[u8; 876] = &[
1345        0x78, 0x03, 0x34, 0x00, 0x00, 0x00, 0x56, 0x00, 0x53, 0x00, 0x5F, 0x00, 0x56, 0x00, 0x45,
1346        0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x5F, 0x00, 0x49, 0x00,
1347        0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x04, 0xEF, 0xFE, 0x00,
1348        0x00, 0x01, 0x00, 0x0B, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4E, 0x0C, 0x0B, 0x00, 0x03, 0x00,
1349        0x00, 0x00, 0x4E, 0x0C, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
1350        0x00, 0x01, 0x00, 0x00, 0x00, 0xD8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x74, 0x00,
1351        0x72, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65,
1352        0x00, 0x49, 0x00, 0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xB4, 0x02, 0x00, 0x00,
1353        0x00, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x39, 0x00, 0x30, 0x00, 0x34, 0x00, 0x45,
1354        0x00, 0x34, 0x00, 0x00, 0x00, 0x58, 0x00, 0x36, 0x00, 0x00, 0x00, 0x43, 0x00, 0x6F, 0x00,
1355        0x6D, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x79, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D,
1356        0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00,
1357        0x6F, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x53, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x77,
1358        0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x75, 0x00,
1359        0x6E, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00,
1360        0x00, 0x00, 0x00, 0x58, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00,
1361        0x65, 0x00, 0x44, 0x00, 0x65, 0x00, 0x73, 0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70,
1362        0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00,
1363        0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2E,
1364        0x00, 0x31, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x20, 0x00, 0x28, 0x00, 0x36, 0x00,
1365        0x34, 0x00, 0x2D, 0x00, 0x62, 0x00, 0x69, 0x00, 0x74, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00,
1366        0x00, 0x38, 0x00, 0x18, 0x00, 0x00, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00,
1367        0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00,
1368        0x00, 0x00, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x33, 0x00,
1369        0x31, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x06,
1370        0x00, 0x01, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00,
1371        0x61, 0x00, 0x6C, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x73,
1372        0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00, 0x70, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x7E, 0x00,
1373        0x00, 0x00, 0x4C, 0x00, 0x65, 0x00, 0x67, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x43, 0x00, 0x6F,
1374        0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00,
1375        0x00, 0x00, 0x43, 0x00, 0x6F, 0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67,
1376        0x00, 0x68, 0x00, 0x74, 0x00, 0x20, 0x00, 0x28, 0x00, 0x63, 0x00, 0x29, 0x00, 0x20, 0x00,
1377        0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x53,
1378        0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x77, 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00,
1379        0x20, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x75, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74,
1380        0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x2E, 0x00, 0x20, 0x00, 0x41, 0x00, 0x6C, 0x00,
1381        0x6C, 0x00, 0x20, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x73,
1382        0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00,
1383        0x65, 0x00, 0x64, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x30, 0x00, 0x00,
1384        0x00, 0x4F, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x61, 0x00,
1385        0x6C, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6D,
1386        0x00, 0x65, 0x00, 0x00, 0x00, 0x70, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6F, 0x00,
1387        0x6E, 0x00, 0x2D, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x33,
1388        0x00, 0x2D, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x64, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2E, 0x00,
1389        0x65, 0x00, 0x78, 0x00, 0x65, 0x00, 0x00, 0x00, 0x50, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x50,
1390        0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x4E, 0x00,
1391        0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x79, 0x00, 0x74,
1392        0x00, 0x68, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x31, 0x00,
1393        0x31, 0x00, 0x2E, 0x00, 0x33, 0x00, 0x20, 0x00, 0x28, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2D,
1394        0x00, 0x62, 0x00, 0x69, 0x00, 0x74, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00,
1395        0x18, 0x00, 0x00, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63,
1396        0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00,
1397        0x6E, 0x00, 0x00, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x33,
1398        0x00, 0x31, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x00, 0x00, 0x44, 0x00,
1399        0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x61, 0x00, 0x72, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C,
1400        0x00, 0x65, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00,
1401        0x24, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x73,
1402        0x00, 0x6C, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00,
1403        0x00, 0x00, 0x09, 0x04, 0xE4, 0x04,
1404    ];
1405
1406    /// Binary representation of entire [`super::RT_VERSION`] data of `python-3.11.3-amd64.exe`
1407    /// to be coverted to appropriate [`super::VersionInfo`]
1408    ///
1409    /// Unlike [`PYTHON_INSTALLER_VERSION_INFO`], this buffer is aligned with 8 bytes and has
1410    /// 4 bytes paddings at the tail
1411    const NTDLL_VERSION_INFO: &[u8; 896] = &[
1412        0x7C, 0x03, 0x34, 0x00, 0x00, 0x00, 0x56, 0x00, 0x53, 0x00, 0x5F, 0x00, 0x56, 0x00, 0x45,
1413        0x00, 0x52, 0x00, 0x53, 0x00, 0x49, 0x00, 0x4F, 0x00, 0x4E, 0x00, 0x5F, 0x00, 0x49, 0x00,
1414        0x4E, 0x00, 0x46, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x04, 0xEF, 0xFE, 0x00,
1415        0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xAA, 0x11, 0x61, 0x4A, 0x00, 0x00, 0x0A, 0x00,
1416        0xAA, 0x11, 0x61, 0x4A, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
1417        0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1418        0x00, 0x00, 0xDC, 0x02, 0x00, 0x00, 0x01, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69,
1419        0x00, 0x6E, 0x00, 0x67, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x49, 0x00,
1420        0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x00, 0x01, 0x00, 0x30,
1421        0x00, 0x34, 0x00, 0x30, 0x00, 0x39, 0x00, 0x30, 0x00, 0x34, 0x00, 0x42, 0x00, 0x30, 0x00,
1422        0x00, 0x00, 0x4C, 0x00, 0x16, 0x00, 0x01, 0x00, 0x43, 0x00, 0x6F, 0x00, 0x6D, 0x00, 0x70,
1423        0x00, 0x61, 0x00, 0x6E, 0x00, 0x79, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00,
1424        0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x73,
1425        0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6F, 0x00, 0x72, 0x00,
1426        0x70, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E,
1427        0x00, 0x00, 0x00, 0x42, 0x00, 0x0D, 0x00, 0x01, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00,
1428        0x65, 0x00, 0x44, 0x00, 0x65, 0x00, 0x73, 0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70,
1429        0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x00,
1430        0x54, 0x00, 0x20, 0x00, 0x4C, 0x00, 0x61, 0x00, 0x79, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20,
1431        0x00, 0x44, 0x00, 0x4C, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x27, 0x00,
1432        0x01, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72,
1433        0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00,
1434        0x30, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x39, 0x00, 0x30, 0x00, 0x34,
1435        0x00, 0x31, 0x00, 0x2E, 0x00, 0x34, 0x00, 0x35, 0x00, 0x32, 0x00, 0x32, 0x00, 0x20, 0x00,
1436        0x28, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x42, 0x00, 0x75, 0x00, 0x69, 0x00, 0x6C,
1437        0x00, 0x64, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x36, 0x00, 0x30, 0x00, 0x31, 0x00, 0x30, 0x00,
1438        0x31, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x29, 0x00, 0x00,
1439        0x00, 0x00, 0x00, 0x34, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00,
1440        0x65, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D,
1441        0x00, 0x65, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x6C, 0x00,
1442        0x2E, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x80, 0x00, 0x2E, 0x00, 0x01,
1443        0x00, 0x4C, 0x00, 0x65, 0x00, 0x67, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x43, 0x00, 0x6F, 0x00,
1444        0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x00,
1445        0x00, 0xA9, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6F, 0x00,
1446        0x73, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6F, 0x00, 0x72,
1447        0x00, 0x70, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00,
1448        0x6E, 0x00, 0x2E, 0x00, 0x20, 0x00, 0x41, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x20, 0x00, 0x72,
1449        0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x73, 0x00, 0x20, 0x00, 0x72, 0x00,
1450        0x65, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x64, 0x00, 0x2E,
1451        0x00, 0x00, 0x00, 0x3C, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x4F, 0x00, 0x72, 0x00, 0x69, 0x00,
1452        0x67, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x46, 0x00, 0x69, 0x00, 0x6C,
1453        0x00, 0x65, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x00, 0x00, 0x6E, 0x00,
1454        0x74, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x2E, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x6C,
1455        0x00, 0x00, 0x00, 0x6A, 0x00, 0x25, 0x00, 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00,
1456        0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65,
1457        0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6F, 0x00,
1458        0x73, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0xAE, 0x00, 0x20, 0x00, 0x57, 0x00, 0x69,
1459        0x00, 0x6E, 0x00, 0x64, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x73, 0x00, 0xAE, 0x00, 0x20, 0x00,
1460        0x4F, 0x00, 0x70, 0x00, 0x65, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6E,
1461        0x00, 0x67, 0x00, 0x20, 0x00, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00,
1462        0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x10, 0x00, 0x01, 0x00, 0x50, 0x00, 0x72,
1463        0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00,
1464        0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30,
1465        0x00, 0x2E, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x39, 0x00, 0x30, 0x00, 0x34, 0x00,
1466        0x31, 0x00, 0x2E, 0x00, 0x34, 0x00, 0x35, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x44,
1467        0x00, 0x00, 0x00, 0x01, 0x00, 0x56, 0x00, 0x61, 0x00, 0x72, 0x00, 0x46, 0x00, 0x69, 0x00,
1468        0x6C, 0x00, 0x65, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x00,
1469        0x00, 0x24, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6E, 0x00,
1470        0x73, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00,
1471        0x00, 0x00, 0x00, 0x09, 0x04, 0xB0, 0x04, 0x00, 0x00, 0x00, 0x00,
1472    ];
1473
1474    #[test]
1475    fn test_resource_entry_unions() {
1476        let entry = ResourceEntry::from(0x8000183880002938);
1477        assert_eq!(entry.name_is_string(), true);
1478        assert_eq!(entry.name_offset(), 0x1838);
1479        assert_eq!(entry.id(), None);
1480        assert_eq!(entry.data_is_directory(), true);
1481        assert_eq!(entry.offset_to_directory(), 0x2938);
1482        assert_eq!(entry.value(), 0x8000183880002938);
1483
1484        let entry = ResourceEntry::from(0x183880002938);
1485        assert_eq!(entry.name_is_string(), false);
1486        assert_eq!(entry.name_offset(), 0x1838); // invalid, just for assertion
1487        assert_eq!(entry.id(), Some(6200)); // invalid, just for assertion
1488        assert_eq!(entry.data_is_directory(), true);
1489        assert_eq!(entry.offset_to_directory(), 0x2938);
1490        assert_eq!(entry.value(), 0x183880002938);
1491
1492        let entry = ResourceEntry::from(0x8000183800002938);
1493        assert_eq!(entry.name_is_string(), true);
1494        assert_eq!(entry.name_offset(), 0x1838);
1495        assert_eq!(entry.id(), None);
1496        assert_eq!(entry.data_is_directory(), false);
1497        assert_eq!(entry.offset_to_directory(), 0x2938);
1498        assert_eq!(entry.value(), 0x8000183800002938);
1499
1500        let entry = ResourceEntry::from(0x208800008080);
1501        assert_eq!(entry.name_is_string(), false);
1502        assert_eq!(entry.name_offset(), 0x2088); // invalid, just for assertion
1503        assert_eq!(entry.id(), Some(8328)); // invalid, just for assertion
1504        assert_eq!(entry.data_is_directory(), false);
1505        assert_eq!(entry.offset_to_directory(), 0x8080);
1506        assert_eq!(entry.value(), 0x208800008080);
1507
1508        let entry = ResourceEntry::from(0x3880008080);
1509        assert_eq!(entry.name_is_string(), false);
1510        assert_eq!(entry.id(), Some(56));
1511        assert_eq!(entry.data_is_directory(), true);
1512        assert_eq!(entry.offset_to_directory(), 0x8080);
1513        assert_eq!(entry.value(), 0x3880008080);
1514    }
1515
1516    #[test]
1517    fn test_version_field_from_ms_ls() {
1518        const MS: u32 = (4 << 16) | 2; // major: 4, minor: 2
1519        const LS: u32 = (3 << 16) | 1; // build: 3, revision: 1
1520
1521        let mut version = VersionField::from_ms_ls(MS, LS);
1522
1523        assert_eq!(version.major, 4);
1524        assert_eq!(version.minor, 2);
1525        assert_eq!(version.build, 3);
1526        assert_eq!(version.revision, 1);
1527
1528        assert_eq!(version.to_string(), "4.2.3.1");
1529        assert_eq!(version.to_ms(), MS);
1530        assert_eq!(version.to_ls(), LS);
1531
1532        version.major += 1;
1533        version.minor += 2;
1534        assert_eq!(version.to_ms(), (4 + 1 << 16) | 2 + 2);
1535        version.build += 3;
1536        version.revision += 4;
1537        assert_eq!(version.to_ls(), (3 + 3 << 16) | 1 + 4);
1538    }
1539
1540    #[test]
1541    fn parse_no_resource() {
1542        let binary = crate::pe::PE::parse(HAS_NO_RES).expect("Unable to parse binary");
1543        assert_eq!(binary.resource_data.is_none(), true);
1544    }
1545
1546    #[test]
1547    fn parse_full_version_and_manifest() {
1548        let binary = crate::pe::PE::parse(HAS_RES_FULL_VERSION_AND_MANIFEST)
1549            .expect("Unable to parse binary");
1550        assert_eq!(binary.resource_data.is_some(), true);
1551        let res_data = binary.resource_data.unwrap();
1552        assert_eq!(res_data.version_info.is_some(), true);
1553        let ver_info = res_data.version_info.unwrap();
1554
1555        assert_eq!(ver_info.fixed_info.is_some(), true);
1556        let fixed_info = ver_info.fixed_info.unwrap();
1557        assert_eq!(fixed_info.signature, VS_FFI_SIGNATURE);
1558        assert_eq!(fixed_info.is_valid(), true);
1559        assert_eq!(fixed_info.struct_version, VS_FFI_STRUCVERSION);
1560        assert_eq!(fixed_info.file_version_ms, 0x1);
1561        assert_eq!(fixed_info.file_version_ls, 0x0);
1562        assert_eq!(fixed_info.product_version_ms, 0x1);
1563        assert_eq!(fixed_info.product_version_ls, 0x0);
1564        assert_eq!(fixed_info.file_flags_mask, VS_FFI_FILEFLAGSMASK);
1565        assert_eq!(fixed_info.file_flags, 0x0);
1566        assert_eq!(fixed_info.file_os, VOS_NT_WINDOWS32);
1567        assert_eq!(fixed_info.file_type, VFT_APP);
1568        assert_eq!(fixed_info.file_subtype, 0x0);
1569        assert_eq!(fixed_info.file_date_ms, 0x0);
1570        assert_eq!(fixed_info.file_date_ls, 0x0);
1571
1572        let str_info = ver_info.string_info;
1573        assert_eq!(
1574            str_info.comments(),
1575            Some(String::from("GOBLIN-TEST-BIN-COMMENTS"))
1576        );
1577        assert_eq!(
1578            str_info.company_name(),
1579            Some(String::from("GOBLIN-TEST-BIN-COMPANY-NAME"))
1580        );
1581        assert_eq!(
1582            str_info.file_description(),
1583            Some(String::from("GOBLIN-TEST-BIN-FILE-DESCRIPTION"))
1584        );
1585        assert_eq!(
1586            str_info.file_version(),
1587            Some(String::from("GOBLIN-TEST-BIN-FILE-VERSION"))
1588        );
1589        assert_eq!(
1590            str_info.internal_name(),
1591            Some(String::from("GOBLIN-TEST-BIN-INTERNAL-NAME"))
1592        );
1593        assert_eq!(
1594            str_info.legal_copyright(),
1595            Some(String::from("GOBLIN-TEST-BIN-LEGAL-COPYRIGHT"))
1596        );
1597        assert_eq!(
1598            str_info.legal_trademarks(),
1599            Some(String::from("GOBLIN-TEST-BIN-LEGAL-TRADEMARKS"))
1600        );
1601        assert_eq!(
1602            str_info.original_filename(),
1603            Some(String::from("GOBLIN-TEST-BIN-ORIGINAL-FILENAME"))
1604        );
1605        assert_eq!(
1606            str_info.private_build(),
1607            Some(String::from("GOBLIN-TEST-BIN-PRIVATE-BUILD"))
1608        );
1609        assert_eq!(
1610            str_info.product_name(),
1611            Some(String::from("GOBLIN-TEST-BIN-PRODUCT-NAME"))
1612        );
1613        assert_eq!(
1614            str_info.product_version(),
1615            Some(String::from("GOBLIN-TEST-BIN-PRODUCT-VERSION"))
1616        );
1617        assert_eq!(
1618            str_info.special_build(),
1619            Some(String::from("GOBLIN-TEST-BIN-SPECIAL-BUILD"))
1620        );
1621
1622        assert_eq!(res_data.manifest_data.is_some(), true);
1623        let manifest_info = res_data.manifest_data.unwrap();
1624        assert_eq!(manifest_info.data, EXPECTED_MANIFEST);
1625    }
1626
1627    #[test]
1628    fn test_resource_string_iterator() {
1629        // NOT Aligned with 8 bytes and has no paddings
1630        let it = ResourceStringIterator {
1631            data: PYTHON_INSTALLER_VERSION_INFO,
1632        };
1633        let it_vec = it.collect::<Result<Vec<_>, _>>();
1634        assert_eq!(it_vec.is_ok(), true);
1635        let it_vec = it_vec.unwrap();
1636
1637        assert_eq!(it_vec[0].is_binary_data(), true);
1638        assert_eq!(it_vec[0].key_string(), VS_VERSION_INFO_KEY);
1639        assert_eq!(
1640            it_vec[0].value,
1641            &[
1642                0xbd, 0x04, 0xef, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00,
1643                0x4e, 0x0c, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x4e, 0x0c, 0x3f, 0x00, 0x00, 0x00,
1644                0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd8, 0x02,
1645                0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00
1646            ]
1647        );
1648
1649        assert_eq!(it_vec[1].r#type, 103); // Invalid, seems broken by RC (resource compiler)
1650        assert_eq!(it_vec[1].key_string(), "FileInfo");
1651        assert_eq!(
1652            it_vec[1].value,
1653            &[
1654                0xb4, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x34, 0x00, 0x30, 0x00, 0x39, 0x00,
1655                0x30, 0x00, 0x34, 0x00, 0x45, 0x00, 0x34, 0x00, 0x00, 0x00, 0x58, 0x00, 0x36, 0x00,
1656                0x00, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x61, 0x00, 0x6e, 0x00,
1657                0x79, 0x00, 0x4e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
1658                0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00,
1659                0x53, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x77, 0x00, 0x61, 0x00, 0x72, 0x00,
1660                0x65, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00,
1661                0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
1662            ]
1663        );
1664
1665        assert_eq!(it_vec[2].is_binary_data(), true);
1666        assert_eq!(it_vec[2].key_string(), "FileDescription");
1667        assert_eq!(it_vec[2].value_string(), "Python 3.11.3 (64-bit)");
1668        assert_eq!(
1669            it_vec[2].value,
1670            &[
1671                0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00,
1672                0x33, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x20, 0x00,
1673                0x28, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x62, 0x00, 0x69, 0x00, 0x74, 0x00,
1674                0x29, 0x00, 0x00, 0x00, 0x00, 0x00,
1675            ]
1676        );
1677
1678        assert_eq!(it_vec[3].is_binary_data(), true);
1679        assert_eq!(it_vec[3].key_string(), "FileVersion");
1680        assert_eq!(it_vec[3].value_string(), "3.11.3150.0");
1681        assert_eq!(
1682            it_vec[3].value,
1683            &[
1684                0x33, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x31, 0x00,
1685                0x35, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00,
1686            ]
1687        );
1688
1689        assert_eq!(it_vec[4].is_text_data(), true);
1690        assert_eq!(it_vec[4].key_string(), "InternalName");
1691        assert_eq!(it_vec[4].value_string(), "setup");
1692        assert_eq!(
1693            it_vec[4].value,
1694            &[0x73, 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00, 0x70, 0x00, 0x00, 0x00,]
1695        );
1696
1697        assert_eq!(it_vec[5].is_binary_data(), true);
1698        assert_eq!(it_vec[5].key_string(), "LegalCopyright");
1699        assert_eq!(
1700            it_vec[5].value_string(),
1701            "Copyright (c) Python Software Foundation. All rights reserved."
1702        );
1703        assert_eq!(
1704            it_vec[5].value,
1705            &[
1706                0x43, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00,
1707                0x68, 0x00, 0x74, 0x00, 0x20, 0x00, 0x28, 0x00, 0x63, 0x00, 0x29, 0x00, 0x20, 0x00,
1708                0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00,
1709                0x53, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x77, 0x00, 0x61, 0x00, 0x72, 0x00,
1710                0x65, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x64, 0x00,
1711                0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x20, 0x00,
1712                0x41, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00,
1713                0x68, 0x00, 0x74, 0x00, 0x73, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00,
1714                0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x00, 0x00,
1715                0x00, 0x00,
1716            ]
1717        );
1718
1719        assert_eq!(it_vec[6].is_binary_data(), true);
1720        assert_eq!(it_vec[6].key_string(), "OriginalFilename");
1721        assert_eq!(it_vec[6].value_string(), "python-3.11.3-amd64.exe");
1722        assert_eq!(
1723            it_vec[6].value,
1724            &[
1725                0x70, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2d, 0x00,
1726                0x33, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x2d, 0x00,
1727                0x61, 0x00, 0x6d, 0x00, 0x64, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x65, 0x00,
1728                0x78, 0x00, 0x65, 0x00, 0x00, 0x00,
1729            ]
1730        );
1731
1732        assert_eq!(it_vec[7].is_binary_data(), true);
1733        assert_eq!(it_vec[7].key_string(), "ProductName");
1734        assert_eq!(it_vec[7].value_string(), "Python 3.11.3 (64-bit)");
1735        assert_eq!(
1736            it_vec[7].value,
1737            &[
1738                0x50, 0x00, 0x79, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00,
1739                0x33, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x20, 0x00,
1740                0x28, 0x00, 0x36, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x62, 0x00, 0x69, 0x00, 0x74, 0x00,
1741                0x29, 0x00, 0x00, 0x00, 0x00, 0x00,
1742            ]
1743        );
1744
1745        assert_eq!(it_vec[8].is_binary_data(), true);
1746        assert_eq!(it_vec[8].key_string(), "ProductVersion");
1747        assert_eq!(it_vec[8].value_string(), "3.11.3150.0");
1748        assert_eq!(
1749            it_vec[8].value,
1750            &[
1751                0x33, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x33, 0x00, 0x31, 0x00,
1752                0x35, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00,
1753            ]
1754        );
1755
1756        assert_eq!(it_vec[9].is_binary_data(), true);
1757        assert_eq!(it_vec[9].key_string(), "VarFileInfo");
1758        assert_eq!(it_vec[9].value, &[]);
1759
1760        assert_eq!(it_vec[9].is_binary_data(), true);
1761        assert_eq!(it_vec[10].key_string(), "Translation");
1762        assert_eq!(it_vec[10].value, &[0x09, 0x04, 0xe4, 0x04]);
1763
1764        assert_eq!(it_vec.get(11), None);
1765
1766        // Aligned with 8 bytes and has 4 bytes zero paddings at the tail
1767        let it = ResourceStringIterator {
1768            data: NTDLL_VERSION_INFO,
1769        };
1770        let it_vec = it.collect::<Result<Vec<_>, _>>();
1771        assert_eq!(it_vec.is_ok(), true);
1772    }
1773}