def parse_version_information(self, version_struct):
        """Parse version information structure.

        The date will be made available in three attributes of the PE object.

        VS_VERSIONINFO   will contain the first three fields of the main structure:
            'Length', 'ValueLength', and 'Type'

        VS_FIXEDFILEINFO will hold the rest of the fields, accessible as sub-attributes:
            'Signature', 'StrucVersion', 'FileVersionMS', 'FileVersionLS',
            'ProductVersionMS', 'ProductVersionLS', 'FileFlagsMask', 'FileFlags',
            'FileOS', 'FileType', 'FileSubtype', 'FileDateMS', 'FileDateLS'

        FileInfo    is a list of all StringFileInfo and VarFileInfo structures.

        StringFileInfo structures will have a list as an attribute named 'StringTable'
        containing all the StringTable structures. Each of those structures contains a
        dictionary 'entries' with all the key / value version information string pairs.

        VarFileInfo structures will have a list as an attribute named 'Var' containing
        all Var structures. Each Var structure will have a dictionary as an attribute
        named 'entry' which will contain the name and value of the Var.
        """

        # Retrieve the data for the version info resource
        #
        try:
            start_offset = self.get_offset_from_rva(version_struct.OffsetToData)
        except PEFormatError:
            self.__warnings.append(
                "Error parsing the version information, "
                "attempting to read OffsetToData with RVA: 0x{:x}".format(
                    version_struct.OffsetToData
                )
            )
            return
        raw_data = self.__data__[start_offset : start_offset + version_struct.Size]

        # Map the main structure and the subsequent string
        #
        versioninfo_struct = self.__unpack_data__(
            self.__VS_VERSIONINFO_format__, raw_data, file_offset=start_offset
        )

        if versioninfo_struct is None:
            return

        ustr_offset = version_struct.OffsetToData + versioninfo_struct.sizeof()
        section = self.get_section_by_rva(ustr_offset)
        section_end = None
        if section:
            section_end = section.VirtualAddress + max(
                section.SizeOfRawData, section.Misc_VirtualSize
            )

        versioninfo_string = None
        # These should return 'ascii' decoded data. For the case when it's
        # garbled data the ascii string will retain the byte values while
        # encoding it to something else may yield values that don't match the
        # file's contents.
        try:
            if section_end is None:
                versioninfo_string = self.get_string_u_at_rva(
                    ustr_offset, encoding="ascii"
                )
            else:
                versioninfo_string = self.get_string_u_at_rva(
                    ustr_offset, (section_end - ustr_offset) >> 1, encoding="ascii"
                )
        except PEFormatError:
            self.__warnings.append(
                "Error parsing the version information, "
                "attempting to read VS_VERSION_INFO string. Can't "
                "read unicode string at offset 0x%x" % (ustr_offset)
            )

        if versioninfo_string is None:
            self.__warnings.append(
                "Invalid VS_VERSION_INFO block: {0}".format(versioninfo_string)
            )
            return

        # If the structure does not contain the expected name, it's assumed to
        # be invalid
        if versioninfo_string is not None and versioninfo_string != b"VS_VERSION_INFO":
            if len(versioninfo_string) > 128:
                excerpt = versioninfo_string[:128].decode("ascii")
                # Don't leave any half-escaped characters
                excerpt = excerpt[: excerpt.rfind("\\u")]
                versioninfo_string = "{0} ... ({1} bytes, too long to display)".format(
                    excerpt, len(versioninfo_string)
                ).encode()
            self.__warnings.append(
                "Invalid VS_VERSION_INFO block: {0}".format(
                    versioninfo_string.decode("ascii").replace("\00", "\\00")
                )
            )
            return

        if not hasattr(self, "VS_VERSIONINFO"):
            self.VS_VERSIONINFO = []

        # Set the PE object's VS_VERSIONINFO to this one
        vinfo = versioninfo_struct

        # Set the Key attribute to point to the unicode string identifying the structure
        vinfo.Key = versioninfo_string

        self.VS_VERSIONINFO.append(vinfo)

        if versioninfo_string is None:
            versioninfo_string = ""
        # Process the fixed version information, get the offset and structure
        fixedfileinfo_offset = self.dword_align(
            versioninfo_struct.sizeof() + 2 * (len(versioninfo_string) + 1),
            version_struct.OffsetToData,
        )
        fixedfileinfo_struct = self.__unpack_data__(
            self.__VS_FIXEDFILEINFO_format__,
            raw_data[fixedfileinfo_offset:],
            file_offset=start_offset + fixedfileinfo_offset,
        )

        if not fixedfileinfo_struct:
            return

        if not hasattr(self, "VS_FIXEDFILEINFO"):
            self.VS_FIXEDFILEINFO = []

        # Set the PE object's VS_FIXEDFILEINFO to this one
        self.VS_FIXEDFILEINFO.append(fixedfileinfo_struct)

        # Start parsing all the StringFileInfo and VarFileInfo structures

        # Get the first one
        stringfileinfo_offset = self.dword_align(
            fixedfileinfo_offset + fixedfileinfo_struct.sizeof(),
            version_struct.OffsetToData,
        )

        # Set the PE object's attribute that will contain them all.
        if not hasattr(self, "FileInfo"):
            self.FileInfo = []

        finfo = []
        while True:
            # Process the StringFileInfo/VarFileInfo structure
            stringfileinfo_struct = self.__unpack_data__(
                self.__StringFileInfo_format__,
                raw_data[stringfileinfo_offset:],
                file_offset=start_offset + stringfileinfo_offset,
            )

            if stringfileinfo_struct is None:
                self.__warnings.append(
                    "Error parsing StringFileInfo/VarFileInfo struct"
                )
                return None

            # Get the subsequent string defining the structure.
            ustr_offset = (
                version_struct.OffsetToData
                + stringfileinfo_offset
                + versioninfo_struct.sizeof()
            )
            try:
                stringfileinfo_string = self.get_string_u_at_rva(ustr_offset)
            except PEFormatError:
                self.__warnings.append(
                    "Error parsing the version information, "
                    "attempting to read StringFileInfo string. Can't "
                    "read unicode string at offset 0x{0:x}".format(ustr_offset)
                )
                break

            # Set such string as the Key attribute
            stringfileinfo_struct.Key = stringfileinfo_string

            # Append the structure to the PE object's list
            finfo.append(stringfileinfo_struct)

            # Parse a StringFileInfo entry
            if stringfileinfo_string and stringfileinfo_string.startswith(
                b"StringFileInfo"
            ):
                if (
                    stringfileinfo_struct.Type in (0, 1)
                    and stringfileinfo_struct.ValueLength == 0
                ):
                    stringtable_offset = self.dword_align(
                        stringfileinfo_offset
                        + stringfileinfo_struct.sizeof()
                        + 2 * (len(stringfileinfo_string) + 1),
                        version_struct.OffsetToData,
                    )

                    stringfileinfo_struct.StringTable = []

                    # Process the String Table entries
                    while True:
                        stringtable_struct = self.__unpack_data__(
                            self.__StringTable_format__,
                            raw_data[stringtable_offset:],
                            file_offset=start_offset + stringtable_offset,
                        )

                        if not stringtable_struct:
                            break

                        ustr_offset = (
                            version_struct.OffsetToData
                            + stringtable_offset
                            + stringtable_struct.sizeof()
                        )
                        try:
                            stringtable_string = self.get_string_u_at_rva(ustr_offset)
                        except PEFormatError:
                            self.__warnings.append(
                                "Error parsing the version information, "
                                "attempting to read StringTable string. Can't "
                                "read unicode string at offset 0x{0:x}".format(
                                    ustr_offset
                                )
                            )
                            break

                        stringtable_struct.LangID = stringtable_string
                        stringtable_struct.entries = {}
                        stringtable_struct.entries_offsets = {}
                        stringtable_struct.entries_lengths = {}
                        stringfileinfo_struct.StringTable.append(stringtable_struct)

                        entry_offset = self.dword_align(
                            stringtable_offset
                            + stringtable_struct.sizeof()
                            + 2 * (len(stringtable_string) + 1),
                            version_struct.OffsetToData,
                        )

                        # Process all entries in the string table

                        while (
                            entry_offset
                            < stringtable_offset + stringtable_struct.Length
                        ):
                            string_struct = self.__unpack_data__(
                                self.__String_format__,
                                raw_data[entry_offset:],
                                file_offset=start_offset + entry_offset,
                            )

                            if not string_struct:
                                break

                            ustr_offset = (
                                version_struct.OffsetToData
                                + entry_offset
                                + string_struct.sizeof()
                            )
                            try:
                                key = self.get_string_u_at_rva(ustr_offset)
                                key_offset = self.get_offset_from_rva(ustr_offset)
                            except PEFormatError:
                                self.__warnings.append(
                                    "Error parsing the version information, "
                                    "attempting to read StringTable Key string. Can't "
                                    "read unicode string at offset 0x{0:x}".format(
                                        ustr_offset
                                    )
                                )
                                break

                            value_offset = self.dword_align(
                                2 * (len(key) + 1)
                                + entry_offset
                                + string_struct.sizeof(),
                                version_struct.OffsetToData,
                            )

                            ustr_offset = version_struct.OffsetToData + value_offset
                            try:
                                value = self.get_string_u_at_rva(
                                    ustr_offset, max_length=string_struct.ValueLength
                                )
                                value_offset = self.get_offset_from_rva(ustr_offset)
                            except PEFormatError:
                                self.__warnings.append(
                                    "Error parsing the version information, attempting "
                                    "to read StringTable Value string. Can't read "
                                    f"unicode string at offset 0x{ustr_offset:x}"
                                )
                                break

                            if string_struct.Length == 0:
                                entry_offset = (
                                    stringtable_offset + stringtable_struct.Length
                                )
                            else:
                                entry_offset = self.dword_align(
                                    string_struct.Length + entry_offset,
                                    version_struct.OffsetToData,
                                )

                            stringtable_struct.entries[key] = value
                            stringtable_struct.entries_offsets[key] = (
                                key_offset,
                                value_offset,
                            )
                            stringtable_struct.entries_lengths[key] = (
                                len(key),
                                len(value),
                            )

                        new_stringtable_offset = self.dword_align(
                            stringtable_struct.Length + stringtable_offset,
                            version_struct.OffsetToData,
                        )

                        # Check if the entry is crafted in a way that would lead
                        # to an infinite loop and break if so.
                        if new_stringtable_offset == stringtable_offset:
                            break
                        stringtable_offset = new_stringtable_offset

                        if stringtable_offset >= stringfileinfo_struct.Length:
                            break

            # Parse a VarFileInfo entry
            elif stringfileinfo_string and stringfileinfo_string.startswith(
                b"VarFileInfo"
            ):
                varfileinfo_struct = stringfileinfo_struct
                varfileinfo_struct.name = "VarFileInfo"

                if (
                    varfileinfo_struct.Type in (0, 1)
                    and varfileinfo_struct.ValueLength == 0
                ):
                    var_offset = self.dword_align(
                        stringfileinfo_offset
                        + varfileinfo_struct.sizeof()
                        + 2 * (len(stringfileinfo_string) + 1),
                        version_struct.OffsetToData,
                    )

                    varfileinfo_struct.Var = []

                    # Process all entries

                    while True:
                        var_struct = self.__unpack_data__(
                            self.__Var_format__,
                            raw_data[var_offset:],
                            file_offset=start_offset + var_offset,
                        )

                        if not var_struct:
                            break

                        ustr_offset = (
                            version_struct.OffsetToData
                            + var_offset
                            + var_struct.sizeof()
                        )
                        try:
                            var_string = self.get_string_u_at_rva(ustr_offset)
                        except PEFormatError:
                            self.__warnings.append(
                                "Error parsing the version information, "
                                "attempting to read VarFileInfo Var string. "
                                "Can't read unicode string at offset 0x{0:x}".format(
                                    ustr_offset
                                )
                            )
                            break

                        if var_string is None:
                            break

                        varfileinfo_struct.Var.append(var_struct)

                        varword_offset = self.dword_align(
                            2 * (len(var_string) + 1)
                            + var_offset
                            + var_struct.sizeof(),
                            version_struct.OffsetToData,
                        )
                        orig_varword_offset = varword_offset

                        while (
                            varword_offset
                            < orig_varword_offset + var_struct.ValueLength
                        ):
                            word1 = self.get_word_from_data(
                                raw_data[varword_offset : varword_offset + 2], 0
                            )
                            word2 = self.get_word_from_data(
                                raw_data[varword_offset + 2 : varword_offset + 4], 0
                            )
                            varword_offset += 4

                            if isinstance(word1, int) and isinstance(word2, int):
                                var_struct.entry = {
                                    var_string: "0x%04x 0x%04x" % (word1, word2)
                                }

                        var_offset = self.dword_align(
                            var_offset + var_struct.Length, version_struct.OffsetToData
                        )

                        if var_offset <= var_offset + var_struct.Length:
                            break

            # Increment and align the offset
            stringfileinfo_offset = self.dword_align(
                stringfileinfo_struct.Length + stringfileinfo_offset,
                version_struct.OffsetToData,
            )

            # Check if all the StringFileInfo and VarFileInfo items have been processed
            if (
                stringfileinfo_struct.Length == 0
                or stringfileinfo_offset >= versioninfo_struct.Length
            ):
                break

        self.FileInfo.append(finfo)
Parse version information structure.

        The date will be made available in three attributes of the PE object.

        VS_VERSIONINFO   will contain the first three fields of the main structure:
            'Length', 'ValueLength', and 'Type'

        VS_FIXEDFILEINFO will hold the rest of the fields, accessible as sub-attributes:
            'Signature', 'StrucVersion', 'FileVersionMS', 'FileVersionLS',
            'ProductVersionMS', 'ProductVersionLS', 'FileFlagsMask', 'FileFlags',
            'FileOS', 'FileType', 'FileSubtype', 'FileDateMS', 'FileDateLS'

        FileInfo    is a list of all StringFileInfo and VarFileInfo structures.

        StringFileInfo structures will have a list as an attribute named 'StringTable'
        containing all the StringTable structures. Each of those structures contains a
        dictionary 'entries' with all the key / value version information string pairs.

        VarFileInfo structures will have a list as an attribute named 'Var' containing
        all Var structures. Each Var structure will have a dictionary as an attribute
        named 'entry' which will contain the name and value of the Var.
        
pefile.py