##
# Copyright (C) Microsoft Corporation. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
# Python script that converts a raw IVRS table into a struct, the spec version is based on
# https://www.amd.com/system/files/TechDocs/48882_IOMMU.pdf
##
"""Module for converting a raw IVRS table into a struct."""

import struct
import sys
import xml.etree.ElementTree as ET
from enum import IntEnum
from typing import Optional

IVRSParserVersion = "1.00"
# spell-checker:ignore IVMD, IOMMUEFR


class IVRS_TABLE(object):
    """Object representing a IVRS Table."""

    def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE":
        """Inits an empty object."""
        self.acpi_header = None
        self.SubStructs = list()
        self.IVMD_list = list()

        if data is not None:
            self.Decode(data)

    def Decode(self, data: bytes) -> None:
        """Loads data into the object from the header_byte_array."""
        self.acpi_header = IVRS_TABLE.ACPI_TABLE_HEADER(data[: IVRS_TABLE.ACPI_TABLE_HEADER.struct_format_size])

        # Start from the end of ACPI header, but store the parsed length for verification
        t_length = self.acpi_header.Length
        self.acpi_header.Length = IVRS_TABLE.ACPI_TABLE_HEADER.struct_format_size
        t_data = data[IVRS_TABLE.ACPI_TABLE_HEADER.struct_format_size :]

        # sanity check on incoming data
        Checksum8 = IVRS_TABLE.validateChecksum8(data)
        if Checksum8 != 0:
            raise Exception(
                "Incoming data checksum does not add up: checksum field %x, calculated is %x"
                % (self.acpi_header.Checksum, Checksum8)
            )

        while len(t_data) > 0:
            # Get type and length of remapping struct
            remapping_header = self.REMAPPING_STRUCT_HEADER(t_data)

            # Parse remapping struct
            if (
                (remapping_header.Type == IVRS_TABLE.IVHD_STRUCT.IVHD_TYPE.TYPE_10H)
                or (remapping_header.Type == IVRS_TABLE.IVHD_STRUCT.IVHD_TYPE.TYPE_11H)
                or (remapping_header.Type == IVRS_TABLE.IVHD_STRUCT.IVHD_TYPE.TYPE_40H)
            ):
                remapping_header = self.IVHD_STRUCT(t_data)
                self.addIVHDEntry(remapping_header)
            elif (
                (remapping_header.Type == IVRS_TABLE.IVMD_STRUCT.IVMD_TYPE.TYPE_20H)
                or (remapping_header.Type == IVRS_TABLE.IVMD_STRUCT.IVMD_TYPE.TYPE_21H)
                or (remapping_header.Type == IVRS_TABLE.IVMD_STRUCT.IVMD_TYPE.TYPE_22H)
            ):
                remapping_header = self.IVMD_STRUCT(t_data[: IVRS_TABLE.IVMD_STRUCT.struct_format_size])
                self.addIVMDEntry(remapping_header)
                if remapping_header.Type == IVRS_TABLE.IVMD_STRUCT.IVMD_TYPE.TYPE_20H:
                    self.IVRSBit = 0
            else:
                print("Reserved remapping struct found in IVRS table %d" % remapping_header.Type)
                sys.exit(-1)

            # Update data position
            t_data = t_data[remapping_header.Length :]

        if (self.acpi_header.Length != t_length) or (len(t_data) != 0):
            raise Exception(
                "IVRS length does not add up. Parsed len: %d, reported len: %d" % (t_length, self.acpi_header.Length)
            )

    def Encode(self) -> bytes:
        r"""Serializes the object.

        Returns:
            (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
        """
        bytes_str = b""

        # Append ACPI header
        bytes_str += self.acpi_header.Encode()

        # All IVHD/IVMD entries
        for ivxd in self.SubStructs:
            bytes_str += ivxd.Encode()
        return bytes_str

    def ToXmlElementTree(self) -> ET.Element:
        """Transforms the object into an xml item and returns it."""
        root = ET.Element("IVRSTable")
        root.append(self.acpi_header.ToXmlElementTree())
        for sub in self.SubStructs:
            root.append(sub.ToXmlElementTree())

        return root

    def DumpInfo(self) -> None:
        """Prints internal information to the console."""
        self.acpi_header.DumpInfo()

        for sub in self.SubStructs:
            sub.DumpInfo()

    @staticmethod
    def validateChecksum8(data: bytes) -> bool:
        """Validate the Checksum."""
        return sum(data) & 0xFF

    def updateACPISum(self) -> None:
        """Update the ACPI Checksum."""
        temp_sum = 0
        # Clear the checksum before calculating sum
        self.acpi_header.Checksum = 0
        temp_str = self.Encode()
        temp_sum = sum(temp_str)
        self.acpi_header.Checksum = (0x100 - (temp_sum & 0xFF)) & 0xFF

    def addIVHDEntry(self, ivhd: "IVRS_TABLE.IVHD_STRUCT") -> None:
        """Append entry to the list, update length and checksum."""
        self.acpi_header.Length += len(ivhd.Encode())
        self.SubStructs.append(ivhd)
        self.updateACPISum()

    def addIVMDEntry(self, ivmd: "IVRS_TABLE.IVMD_STRUCT") -> None:
        """Append entry to the list, update length and checksum."""
        self.acpi_header.Length += len(ivmd.Encode())
        # IVMD has to follow the corresponding IVHD, thus the list records all entries to maintain order
        self.SubStructs.append(ivmd)
        self.IVMD_list.append(ivmd)
        self.updateACPISum()

    def IVRSBitEnabled(self) -> bool:
        """Returns the IVRSBit Value."""
        return bool(self.acpi_header.IVRSBit)

    class ACPI_TABLE_HEADER(object):
        """Object representing a ACPI Table Header."""

        struct_format = "=4sIBB6s8sI4sIIQ"
        struct_format_size = struct.calcsize(struct_format)

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.ACPI_TABLE_HEADER":
            """Inits an empty object."""
            self.Signature = None
            self.Length = 0
            self.Revision = 0
            self.Checksum = 0
            self.OEMID = 0
            self.OEMTableID = 0
            self.OEMRevision = 0
            self.CreatorID = 0
            self.CreatorRevision = 0
            self.IVinfo = None
            self.Reserved = 0

            self.IVRSBit = 0

            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (
                self.Signature,
                self.Length,
                self.Revision,
                self.Checksum,
                self.OEMID,
                self.OEMTableID,
                self.OEMRevision,
                self.CreatorID,
                self.CreatorRevision,
                self.IVinfo,
                self.Reserved,
            ) = struct.unpack(IVRS_TABLE.ACPI_TABLE_HEADER.struct_format, header_byte_array)

            self.IVRSBit = self.IVinfo & 0x02
            if (self.IVinfo & 0x1E) == 0:
                sys.exit(-1)

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            return struct.pack(
                self.struct_format,
                self.Signature,
                self.Length,
                self.Revision,
                self.Checksum,
                self.OEMID,
                self.OEMTableID,
                self.OEMRevision,
                self.CreatorID,
                self.CreatorRevision,
                self.IVinfo,
                self.Reserved,
            )

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("  ACPI Table Header")
            print("------------------------------------------------------------------")
            print("Signature          : {Signature:s}".format(Signature=self.Signature.decode()))
            print("Length             : 0x{Length:08X}".format(Length=self.Length))
            print("Revision           : 0x{Revision:02X}".format(Revision=self.Revision))
            print("Checksum           : 0x{Checksum:02X}".format(Checksum=self.Checksum))
            print("OEM ID             : {OEMID:s}".format(OEMID=self.OEMID.decode()))
            print("OEM Table ID       : {OEMTableID:s}".format(OEMTableID=self.OEMTableID.decode()))
            print("OEM Revision       : 0x{OEMRevision:08X}".format(OEMRevision=self.OEMRevision))
            print("Creator ID         : {CreatorID:s}".format(CreatorID=self.CreatorID.decode()))
            print("Creator Revision   : 0x{CreatorRevision:08X}".format(CreatorRevision=self.CreatorRevision))
            print("IVinfo             : 0x{IVinfo:08X}".format(IVinfo=self.IVinfo))

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_repr = ET.Element("AcpiTableHeader")
            xml_repr.set("Signature", "%s" % self.Signature)
            xml_repr.set("Length", "0x%X" % self.Length)
            xml_repr.set("Revision", "0x%X" % self.Revision)
            xml_repr.set("Checksum", "0x%X" % self.Checksum)
            xml_repr.set("OEMID", "%s" % self.OEMID)
            xml_repr.set("OEMTableID", "%s" % self.OEMTableID)
            xml_repr.set("OEMRevision", "0x%X" % self.OEMRevision)
            xml_repr.set("CreatorID", "%s" % self.CreatorID)
            xml_repr.set("CreatorRevision", "0x%X" % self.CreatorRevision)
            xml_repr.set("IVinfo", "0x%X" % self.IVinfo)
            return xml_repr

    class REMAPPING_STRUCT_HEADER(object):
        """Generic ReMapping Struct Header."""

        struct_format = "=B"
        struct_format_size = struct.calcsize(struct_format)

        def __init__(self, header_byte_array: bytes) -> "IVRS_TABLE.REMAPPING_STRUCT_HEADER":
            """Inits an empty object."""
            (self.Type,) = struct.unpack(
                IVRS_TABLE.REMAPPING_STRUCT_HEADER.struct_format,
                header_byte_array[: IVRS_TABLE.REMAPPING_STRUCT_HEADER.struct_format_size],
            )

    class IVHD_STRUCT(REMAPPING_STRUCT_HEADER):
        """Object representing a IVHD Struct."""

        # cspell:disable-next disable the spell checker from thinking this a word
        struct_format = "=BBHHHQHHI"
        struct_format_size = struct.calcsize(struct_format)
        ex_format = "=QQ"
        ex_format_size = struct.calcsize(ex_format)

        class IVHD_TYPE(IntEnum):
            """IVMD_STRUCT type enum."""

            TYPE_10H = 0x10
            TYPE_11H = 0x11
            TYPE_40H = 0x40

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.IVHD_STRUCT.IVHD_TYPE":
            """Inits an empty object."""
            self.Type = None
            self.Flags = None
            self.Length = 0
            self.DeviceID = 0
            self.CapabilityOffset = 0
            self.IOMMUBaseAddress = 0
            self.SegmentGroup = 0
            self.IOMMUInfo = None
            self.IOMMUFeatureInfo = None
            self.IOMMUEFRImage = None
            self.Reserved = 0

            self.DeviceTableEntries = list()

            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (
                self.Type,
                self.Flags,
                t_Length,
                self.DeviceID,
                self.CapabilityOffset,
                self.IOMMUBaseAddress,
                self.SegmentGroup,
                self.IOMMUInfo,
                self.IOMMUFeatureInfo,
            ) = struct.unpack(
                IVRS_TABLE.IVHD_STRUCT.struct_format, header_byte_array[: IVRS_TABLE.IVHD_STRUCT.struct_format_size]
            )

            self.Length = 0

            if (self.Type == IVRS_TABLE.IVHD_STRUCT.IVHD_TYPE.TYPE_11H) or (
                self.Type == IVRS_TABLE.IVHD_STRUCT.IVHD_TYPE.TYPE_40H
            ):
                ivhd_hdr_size = IVRS_TABLE.IVHD_STRUCT.struct_format_size + IVRS_TABLE.IVHD_STRUCT.ex_format_size
                (self.IOMMUEFRImage, self.Reserved) = struct.unpack(
                    IVRS_TABLE.IVHD_STRUCT.ex_format,
                    header_byte_array[IVRS_TABLE.IVHD_STRUCT.struct_format_size : ivhd_hdr_size],
                )
            else:
                ivhd_hdr_size = IVRS_TABLE.IVHD_STRUCT.struct_format_size

            header_byte_array = header_byte_array[ivhd_hdr_size:]
            bytes_left = t_Length - ivhd_hdr_size
            self.Length += ivhd_hdr_size

            # Get Sub Structs
            while bytes_left > 0:
                device_scope = IVRS_TABLE.DEVICE_TABLE_ENTRY.Factory(header_byte_array)
                header_byte_array = header_byte_array[device_scope.Length :]
                bytes_left -= device_scope.Length
                if device_scope.Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RANGE_END:
                    self.addDTEEntry(device_scope)

            if (t_Length != self.Length) or (bytes_left != 0):
                raise Exception(
                    "IVHD length does not add up. Parsed len: %d, reported len: %d" % (self.Length, t_Length)
                )

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            byte_str = b""
            byte_str += struct.pack(
                IVRS_TABLE.IVHD_STRUCT.struct_format,
                self.Type,
                self.Flags,
                self.Length,
                self.DeviceID,
                self.CapabilityOffset,
                self.IOMMUBaseAddress,
                self.SegmentGroup,
                self.IOMMUInfo,
                self.IOMMUFeatureInfo,
            )

            if self.IOMMUEFRImage is not None:
                byte_str += struct.pack(IVRS_TABLE.IVHD_STRUCT.ex_format, self.IOMMUEFRImage, self.Reserved)

            for dte in self.DeviceTableEntries:
                byte_str += dte.Encode()

            return byte_str

        def addDTEEntry(self, dte: "IVRS_TABLE.DEVICE_TABLE_ENTRY") -> None:
            """Append raw data, update length. checksum will be left untouched."""
            self.Length += len(dte.Encode())
            self.DeviceTableEntries.append(dte)

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_repr = ET.Element("IVHD")
            xml_repr.set("Type", "0x%X" % self.Type)
            xml_repr.set("Flags", "0x%X" % self.Flags)
            xml_repr.set("Length", "0x%X" % self.Length)
            xml_repr.set("IOMMUDeviceID", "0x%X" % self.DeviceID)
            xml_repr.set("CapabilityOffset", "0x%X" % self.CapabilityOffset)
            xml_repr.set("IOMMUBaseAddress", "0x%X" % self.IOMMUBaseAddress)
            xml_repr.set("SegmentGroup", "0x%X" % self.SegmentGroup)
            xml_repr.set("IOMMUInfo", "0x%X" % self.IOMMUInfo)
            xml_repr.set("IOMMUFeatureInfo", "0x%X" % self.IOMMUFeatureInfo)

            if (self.Type == IVRS_TABLE.IVHD_STRUCT.IVHD_TYPE.TYPE_11H) or (
                self.Type == IVRS_TABLE.IVHD_STRUCT.IVHD_TYPE.TYPE_40H
            ):
                xml_repr.set("IOMMUEFRImage", "0x%X" % self.IOMMUEFRImage)

            # Add SubStructs
            for item in self.DeviceTableEntries:
                xml_repr.append(item.ToXmlElementTree())

            return xml_repr

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t  IVHD")
            print("\t----------------------------------------------------------------")
            print("\tType                  : 0x{Type:02X}".format(Type=self.Type))
            print("\tFlags                 : 0x{Flags:02X}".format(Flags=self.Flags))
            print("\tLength                : 0x{Length:04X}".format(Length=self.Length))
            print("\tIOMMU Device ID       : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print("\tCapability Offset     : 0x{CapabilityOffset:04X}".format(CapabilityOffset=self.CapabilityOffset))
            print("\tIOMMU Base Address    : 0x{IOMMUBaseAddress:016X}".format(IOMMUBaseAddress=self.IOMMUBaseAddress))
            print("\tSegment Group         : 0x{SegmentGroup:04X}".format(SegmentGroup=self.SegmentGroup))
            print("\tIOMMU Info            : 0x{IOMMUInfo:04X}".format(IOMMUInfo=self.IOMMUInfo))
            print("\tIOMMU Feature Info    : 0x{IOMMUFeatureInfo:08X}".format(IOMMUFeatureInfo=self.IOMMUFeatureInfo))

            for item in self.DeviceTableEntries:
                item.DumpInfo()

    class IVMD_STRUCT(REMAPPING_STRUCT_HEADER):
        """Object representing an IVMD Struct."""

        # cspell:disable-next disable spell checker from thinking this is a word
        struct_format = "=BBHHHQQQ"
        struct_format_size = struct.calcsize(struct_format)

        class IVMD_TYPE(IntEnum):
            """IVMD_STRUCT type enum."""

            TYPE_20H = 0x20  # All peripherals
            TYPE_21H = 0x21  # Specified peripheral
            TYPE_22H = 0x22  # Peripheral range

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.IVMD_STRUCT":
            """Inits an empty object."""
            self.Type = None
            self.Flags = None
            self.Length = 0
            self.DeviceID = 0
            self.AuxiliaryData = None
            self.Reserved = 0
            self.IVMDStartAddress = 0
            self.IVMDMemoryBlockLength = 0

            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (
                self.Type,
                self.Flags,
                self.Length,
                self.DeviceID,
                self.AuxiliaryData,
                self.Reserved,
                self.IVMDStartAddress,
                self.IVMDMemoryBlockLength,
            ) = struct.unpack(IVRS_TABLE.IVMD_STRUCT.struct_format, header_byte_array)
            # IVMD is simple, the length is fixed, so assert if not
            if self.Length != len(header_byte_array):
                raise Exception("Bad IVMD entry size %d, expecting %d" % (self.Length, len(header_byte_array)))

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            return struct.pack(
                IVRS_TABLE.IVMD_STRUCT.struct_format,
                self.Type,
                self.Flags,
                self.Length,
                self.DeviceID,
                self.AuxiliaryData,
                self.Reserved,
                self.IVMDStartAddress,
                self.IVMDMemoryBlockLength,
            )

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_repr = ET.Element("IVMD")

            xml_repr.set("Type", "0x%X" % self.Type)
            xml_repr.set("Flags", "0x%X" % self.Flags)
            xml_repr.set("Length", "0x%X" % self.Length)
            xml_repr.set("DeviceID", "0x%X" % self.DeviceID)
            if self.Type != IVRS_TABLE.IVMD_STRUCT.IVMD_TYPE.TYPE_22H:
                xml_repr.set("AuxiliaryData", "0x%X" % self.AuxiliaryData)
            else:
                xml_repr.set("EndofRange", "0x%X" % self.AuxiliaryData)
            xml_repr.set("Reserved", "0x%X" % self.Reserved)
            xml_repr.set("IVMDStartAddress", "0x%X" % self.IVMDStartAddress)
            xml_repr.set("IVMDMemoryBlockLength", "0x%X" % self.IVMDMemoryBlockLength)

            return xml_repr

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t  IVMD")
            print("\t----------------------------------------------------------------")
            print("\tType                                 : 0x{Type:02X}".format(Type=self.Type))
            print("\tFlags                                : 0x{Flags:02X}".format(Flags=self.Flags))
            print("\tLength                               : 0x{Length:04X}".format(Length=self.Length))
            print("\tDeviceID                             : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print(
                "\tAuxiliaryData                        : 0x{AuxiliaryData:04X}".format(
                    AuxiliaryData=self.AuxiliaryData
                )
            )
            print("\tReserved                             : 0x{Reserved:016X}".format(Reserved=self.Reserved))
            print(
                "\tIVMD Start Address                   : 0x{IVMDStartAddress:016X}".format(
                    IVMDStartAddress=self.IVMDStartAddress
                )
            )
            print(
                "\tIVMD Memory Block Length             : 0x{IVMDMemoryBlockLength:016X}".format(
                    IVMDMemoryBlockLength=self.Type
                )
            )

    class DEVICE_TABLE_ENTRY(object):
        """A Generic Device Table Entry."""

        struct_format = "=BHB"
        struct_format_size = struct.calcsize(struct_format)
        dte_var_ext_format = "=8s8sBB"
        dte_var_len = struct_format_size + struct.calcsize(dte_var_ext_format)

        class DTE_TYPE(IntEnum):
            """Enum for Device Table Entry Types."""

            RESERVED = 0
            ALL = 1
            SELECT = 2
            RANGE_START = 3
            RANGE_END = 4
            ALIAS_SELECT = 66
            ALIAS_RANGE_START = 67
            EX_SELECT = 70
            EX_RANGE_START = 71
            SPECIAL = 72
            ACPI = 240

        @staticmethod
        def Factory(data: bytes) -> Optional["IVRS_TABLE.DEVICE_TABLE_ENTRY_SPECIAL"]:
            """Factory method to generate a specific table entry type."""
            if data is None:
                raise Exception("Invalid File stream")

            RemapHeader = IVRS_TABLE.REMAPPING_STRUCT_HEADER(data)
            Type = RemapHeader.Type

            if Type == IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RESERVED:
                return IVRS_TABLE.DEVICE_TABLE_ENTRY_RESERVED(data)
            elif Type == IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ALL:
                return IVRS_TABLE.DEVICE_TABLE_ENTRY_ALL(data)
            elif Type == IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.SELECT:
                return IVRS_TABLE.DEVICE_TABLE_ENTRY_SELECT(data)
            elif Type == IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RANGE_START:
                return IVRS_TABLE.DEVICE_TABLE_ENTRY_RANGE_START(data)
            elif Type == IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ALIAS_SELECT:
                return IVRS_TABLE.DEVICE_TABLE_ENTRY_ALIAS_SELECT(data)
            elif Type == IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ALIAS_RANGE_START:
                return IVRS_TABLE.DEVICE_TABLE_ENTRY_ALIAS_RANGE_START(data)
            elif Type == IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.EX_SELECT:
                return IVRS_TABLE.DEVICE_TABLE_ENTRY_EX_SELECT(data)
            elif Type == IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.EX_RANGE_START:
                return IVRS_TABLE.DEVICE_TABLE_ENTRY_EX_RANGE_START(data)
            elif Type == IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.SPECIAL:
                return IVRS_TABLE.DEVICE_TABLE_ENTRY_SPECIAL(data)
            elif Type == IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ACPI:
                return IVRS_TABLE.DEVICE_TABLE_ENTRY_ACPI(data)
            else:
                return None

    class DEVICE_TABLE_ENTRY_RESERVED(object):
        """Object representing a Device Table Entry RESERVED."""

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.DEVICE_TABLE_ENTRY":
            """Inits an empty object."""
            self.Type = 0
            self.DeviceID = 0
            self.DTESetting = 0
            self.TypeString = None
            self.Length = 0
            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (self.Type, self.DeviceID, self.DTESetting) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[: IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size],
            )
            if self.Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RESERVED:
                raise Exception(
                    "Input device type (%d) does not match expectation (%d)",
                    IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RESERVED,
                    self.Type,
                )

            self.TypeString = "Reserved"
            self.Length = IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            return struct.pack(IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, self.Type, self.DeviceID, self.DTESetting)

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_item = ET.Element(self.TypeString.replace(" ", ""))
            xml_item.set("DeviceID", "0x%X" % (self.DeviceID))
            xml_item.set("DTESetting", "0x%X" % (self.DTESetting))
            xml_item.set("Type", "0x%X" % self.Type)
            return xml_item

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t\t  {TypeString:s}".format(TypeString=self.TypeString))
            print("\t\t--------------------------------------------------")
            print("\t\tType                  : 0x{Type:02X}".format(Type=self.Type))
            print("\t\tDevice ID             : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print("\t\tDTE Setting           : 0x{DTESetting:02X}".format(DTESetting=self.DTESetting))

    class DEVICE_TABLE_ENTRY_ALL(object):
        """Object representing a Device Table Entry ALL."""

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.DEVICE_TABLE_ENTRY_ALL":
            """Inits an empty object."""
            self.Type = 0
            self.DeviceID = 0
            self.DTESetting = 0
            self.TypeString = None
            self.Length = 0
            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (self.Type, self.DeviceID, self.DTESetting) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[: IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size],
            )
            if self.Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ALL:
                raise Exception(
                    "Input device type (%d) does not match expectation (%d)",
                    IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ALL,
                    self.Type,
                )

            self.TypeString = "All"
            self.Length = IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            return struct.pack(IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, self.Type, self.DeviceID, self.DTESetting)

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_item = ET.Element(self.TypeString.replace(" ", ""))
            xml_item.set("Type", "0x%X" % self.Type)
            xml_item.set("DeviceID", "0x%X" % (self.DeviceID))
            xml_item.set("DTESetting", "0x%X" % (self.DTESetting))
            return xml_item

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t\t  {TypeString:s}".format(TypeString=self.TypeString))
            print("\t\t--------------------------------------------------")
            print("\t\tType                  : 0x{Type:02X}".format(Type=self.Type))
            print("\t\tDevice ID             : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print("\t\tDTE Setting           : 0x{DTESetting:02X}".format(DTESetting=self.DTESetting))

    class DEVICE_TABLE_ENTRY_SELECT(object):
        """Object representing a Device Table Entry SELECT."""

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.DEVICE_TABLE_ENTRY_SELECT":
            """Inits an empty object."""
            self.Type = 0
            self.DeviceID = 0
            self.DTESetting = 0
            self.TypeString = None
            self.Length = 0
            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (self.Type, self.DeviceID, self.DTESetting) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[: IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size],
            )
            if self.Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.SELECT:
                raise Exception(
                    "Input device type (%d) does not match expectation (%d)",
                    IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.SELECT,
                    self.Type,
                )

            self.TypeString = "Reserved"
            self.Length = IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            return struct.pack(IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, self.Type, self.DeviceID, self.DTESetting)

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_item = ET.Element(self.TypeString.replace(" ", ""))
            xml_item.set("Type", "0x%X" % self.Type)
            xml_item.set("DeviceID", "0x%X" % (self.DeviceID))
            xml_item.set("DTESetting", "0x%X" % (self.DTESetting))
            return xml_item

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t\t  {TypeString:s}".format(TypeString=self.TypeString))
            print("\t\t--------------------------------------------------")
            print("\t\tType                  : 0x{Type:02X}".format(Type=self.Type))
            print("\t\tDevice ID             : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print("\t\tDTE Setting           : 0x{DTESetting:02X}".format(DTESetting=self.DTESetting))

    class DEVICE_TABLE_ENTRY_RANGE_START(object):
        """Object representing a Device Table Entry RANGE_START."""

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.DEVICE_TABLE_ENTRY_RANGE_START":
            """Inits an empty object."""
            self.Type = 0
            self.DeviceID = 0
            self.DTESetting = 0
            self.TypeString = None
            self.Length = 0
            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (self.Type, self.DeviceID, self.DTESetting) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[: IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size],
            )
            if self.Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RANGE_START:
                raise Exception(
                    "Input device type (%d) does not match expectation (%d)",
                    IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RANGE_START,
                    self.Type,
                )

            self.TypeString = "Range"
            self.Length = IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size
            (Type, self.EndDeviceID, _) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[self.Length : (self.Length + IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size)],
            )
            if Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RANGE_END:
                print("Start of range does not follow end of range")
                sys.exit(-1)
            self.Length += IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            byte_str = struct.pack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, self.Type, self.DeviceID, self.DTESetting
            )
            byte_str += struct.pack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RANGE_END,
                self.EndDeviceID,
                0,
            )
            return byte_str

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_item = ET.Element(self.TypeString.replace(" ", ""))
            xml_item.set("Type", "0x%X" % self.Type)
            xml_item.set("StartofRange", "0x%X" % self.DeviceID)
            xml_item.set("EndofRange", "0x%X" % (self.EndDeviceID))
            xml_item.set("DTESetting", "0x%X" % (self.DTESetting))
            return xml_item

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t\t  {TypeString:s}".format(TypeString=self.TypeString))
            print("\t\t--------------------------------------------------")
            print("\t\tType                  : 0x{Type:02X}".format(Type=self.Type))
            print("\t\tStart of Range        : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print("\t\tEnd of Range          : 0x{EndDeviceID:04X}".format(EndDeviceID=self.EndDeviceID))
            print("\t\tDTE Setting           : 0x{DTESetting:02X}".format(DTESetting=self.DTESetting))

    class DEVICE_TABLE_ENTRY_ALIAS_SELECT(object):
        """Object representing a Device Table Entry ALIAS_SELECT."""

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.DEVICE_TABLE_ENTRY_ALIAS_SELECT":
            """Inits an empty object."""
            self.Type = 0
            self.DeviceID = 0
            self.DTESetting = 0
            self.SourceDeviceID = 0
            self.TypeString = None
            self.Length = 0
            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (self.Type, self.DeviceID, self.DTESetting) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[: IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size],
            )
            if self.Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ALIAS_SELECT:
                raise Exception(
                    "Input device type (%d) does not match expectation (%d)",
                    IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ALIAS_SELECT,
                    self.Type,
                )

            self.TypeString = "Alias Select"
            # Two DevID, one for alias, one for source
            self.Length = (
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size + IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size
            )
            (_, self.SourceDeviceID, _) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size : self.Length],
            )

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            byte_str = struct.pack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, self.Type, self.DeviceID, self.DTESetting
            )
            byte_str += struct.pack(IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, 0, self.SourceDeviceID, 0)
            return byte_str

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_item = ET.Element(self.TypeString.replace(" ", ""))
            xml_item.set("Type", "0x%X" % self.Type)
            xml_item.set("DeviceID", "0x%X" % (self.DeviceID))
            xml_item.set("DTESetting", "0x%X" % (self.DTESetting))
            xml_item.set("SourceDeviceID", "0x%X" % (self.SourceDeviceID))
            return xml_item

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t\t  {TypeString:s}".format(TypeString=self.TypeString))
            print("\t\t--------------------------------------------------")
            print("\t\tType                  : 0x{Type:02X}".format(Type=self.Type))
            print("\t\tDevice ID             : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print("\t\tDTE Setting           : 0x{DTESetting:02X}".format(DTESetting=self.DTESetting))
            print("\t\tSource Device ID      : 0x{SourceDeviceID:04X}".format(SourceDeviceID=self.SourceDeviceID))

    class DEVICE_TABLE_ENTRY_ALIAS_RANGE_START(object):
        """Object representing a Device Table Range Start."""

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.DEVICE_TABLE_ENTRY_ALIAS_RANGE_START":
            """Inits an empty object."""
            self.Type = 0
            self.DeviceID = 0
            self.DTESetting = 0
            self.SourceDeviceID = 0
            self.EndDeviceID = 0
            self.TypeString = None
            self.Length = 0
            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (self.Type, self.DeviceID, self.DTESetting) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[: IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size],
            )
            if self.Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ALIAS_RANGE_START:
                raise Exception(
                    "Input device type (%d) does not match expectation (%d)",
                    IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ALIAS_RANGE_START,
                    self.Type,
                )

            self.TypeString = "Alias Range"
            # Two DevID, one for alias start, one for source start
            self.Length = (
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size + IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size
            )
            (_, self.SourceDeviceID, _) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size : self.Length],
            )
            (Type, self.EndDeviceID, _) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[self.Length : (self.Length + IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size)],
            )
            if Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RANGE_END:
                print("Start of range does not follow end of range")
                sys.exit(-1)
            self.Length += IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            byte_str = struct.pack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, self.Type, self.DeviceID, self.DTESetting
            )
            byte_str += struct.pack(IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, 0, self.SourceDeviceID, 0)
            byte_str += struct.pack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RANGE_END,
                self.EndDeviceID,
                0,
            )
            return byte_str

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_item = ET.Element(self.TypeString.replace(" ", ""))
            xml_item.set("Type", "0x%X" % self.Type)
            xml_item.set("StartofRange", "0x%X" % self.DeviceID)
            xml_item.set("EndofRange", "0x%X" % (self.EndDeviceID))
            xml_item.set("DTESetting", "0x%X" % (self.DTESetting))
            xml_item.set("SourceDeviceID", "0x%X" % (self.SourceDeviceID))
            return xml_item

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t\t  {TypeString:s}".format(TypeString=self.TypeString))
            print("\t\t--------------------------------------------------")
            print("\t\tType                  : 0x{Type:02X}".format(Type=self.Type))
            print("\t\tStart of Range        : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print("\t\tEnd of Range          : 0x{EndDeviceID:04X}".format(EndDeviceID=self.EndDeviceID))
            print("\t\tDTE Setting           : 0x{DTESetting:02X}".format(DTESetting=self.DTESetting))
            print("\t\tSource Device ID      : 0x{SourceDeviceID:04X}".format(SourceDeviceID=self.SourceDeviceID))

    class DEVICE_TABLE_ENTRY_EX_SELECT(object):
        """Object representing a Device Table Entry EX_SELECT."""

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.DEVICE_TABLE_ENTRY_EX_SELECT":
            """Inits an empty object."""
            self.Type = 0
            self.DeviceID = 0
            self.DTESetting = 0
            self.ExtendedDTESetting = 0
            self.TypeString = None
            self.Length = 0
            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (self.Type, self.DeviceID, self.DTESetting) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[: IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size],
            )
            if self.Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.EX_SELECT:
                raise Exception(
                    "Input device type (%d) does not match expectation (%d)",
                    IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.EX_SELECT,
                    self.Type,
                )

            self.TypeString = "Extended Select"
            # Two DTE setting, one for standard setting, one for extended setting (AtsDisabled, etc.)
            self.Length = (
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size + IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size
            )
            (self.ExtendedDTESetting,) = struct.unpack(
                "=I", header_byte_array[IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size : self.Length]
            )

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            byte_str = struct.pack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, self.Type, self.DeviceID, self.DTESetting
            )
            # Two DTE setting, one for standard setting, one for extended setting (AtsDisabled, etc.)
            byte_str += struct.pack("=I", self.ExtendedDTESetting)
            return byte_str

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_item = ET.Element(self.TypeString.replace(" ", ""))
            xml_item.set("Type", "0x%X" % self.Type)
            xml_item.set("DeviceID", "0x%X" % (self.DeviceID))
            xml_item.set("DTESetting", "0x%X" % (self.DTESetting))
            if (self.ExtendedDTESetting & 0x80000000) != 0:
                xml_item.set("ExtendedDTESetting", "ATS requests blocked")
            else:
                xml_item.set("ExtendedDTESetting", "ATS allowed")
            return xml_item

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t\t  {TypeString:s}".format(TypeString=self.TypeString))
            print("\t\t--------------------------------------------------")
            print("\t\tType                  : 0x{Type:02X}".format(Type=self.Type))
            print("\t\tDevice ID             : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print("\t\tDTE Setting           : 0x{DTESetting:02X}".format(DTESetting=self.DTESetting))
            if (self.ExtendedDTESetting & 0x80000000) != 0:
                ats_str = "ATS requests blocked"
            else:
                ats_str = "ATS allowed"
            print("\t\tExtended DTE Setting  : {ExtendedDTESetting:s}".format(ExtendedDTESetting=ats_str))

    class DEVICE_TABLE_ENTRY_EX_RANGE_START(object):
        """Object representing a Device Table Entry EX_RANGE_START."""

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.DEVICE_TABLE_ENTRY_EX_RANGE_START":
            """Inits an empty object."""
            self.Type = 0
            self.DeviceID = 0
            self.DTESetting = 0
            self.TypeString = None
            self.Length = 0
            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (self.Type, self.DeviceID, self.DTESetting) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[: IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size],
            )
            if self.Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.EX_RANGE_START:
                raise Exception(
                    "Input device type (%d) does not match expectation (%d)",
                    IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.EX_RANGE_START,
                    self.Type,
                )

            self.TypeString = "Extended Range"
            # Two DTE setting, one for standard setting start, one for extended setting start
            self.Length = (
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size + IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size
            )
            (self.ExtendedDTESetting,) = struct.unpack(
                "=I", header_byte_array[IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size : self.Length]
            )
            (Type, self.EndDeviceID, _) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[self.Length : (self.Length + IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size)],
            )
            if Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.RANGE_END:
                print("Start of range does not follow end of range")
                sys.exit(-1)
            self.Length += IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            byte_str = struct.pack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, self.Type, self.DeviceID, self.DTESetting
            )
            # Two DTE setting, one for standard setting start, one for extended setting start
            byte_str += struct.pack("=I", self.ExtendedDTESetting)
            byte_str += struct.pack(IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, 4, self.EndDeviceID, 0)
            return byte_str

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_item = ET.Element(self.TypeString.replace(" ", ""))
            xml_item.set("Type", "0x%X" % self.Type)
            xml_item.set("StartofRange", "0x%X" % self.DeviceID)
            xml_item.set("EndofRange", "0x%X" % (self.EndDeviceID))
            xml_item.set("DTESetting", "0x%X" % (self.DTESetting))
            if (self.ExtendedDTESetting & 0x80000000) != 0:
                xml_item.set("ExtendedDTESetting", "ATS requests blocked")
            else:
                xml_item.set("ExtendedDTESetting", "ATS allowed")
            return xml_item

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t\t  {TypeString:s}".format(TypeString=self.TypeString))
            print("\t\t--------------------------------------------------")
            print("\t\tType                  : 0x{Type:02X}".format(Type=self.Type))
            print("\t\tStart of Range        : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print("\t\tEnd of Range          : 0x{EndDeviceID:04X}".format(EndDeviceID=self.EndDeviceID))
            print("\t\tDTE Setting           : 0x{DTESetting:02X}".format(DTESetting=self.DTESetting))
            if (self.ExtendedDTESetting & 0x80000000) != 0:
                ats_str = "ATS requests blocked"
            else:
                ats_str = "ATS allowed"
            print("\t\tExtended DTE Setting  : {ExtendedDTESetting:s}".format(ExtendedDTESetting=ats_str))

    class DEVICE_TABLE_ENTRY_SPECIAL(object):
        """Object representing a Device Table Entry Special."""

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.DEVICE_TABLE_ENTRY_SPECIAL":
            """Inits an empty object."""
            self.Type = 0
            self.DeviceID = 0
            self.DTESetting = 0
            self.ExtendedDTESetting = 0
            self.TypeString = None
            self.Length = 0
            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (self.Type, self.DeviceID, self.DTESetting) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[: IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size],
            )
            if self.Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.SPECIAL:
                raise Exception(
                    "Input device type (%d) does not match expectation (%d)",
                    IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.SPECIAL,
                    self.Type,
                )

            self.TypeString = "Special Device"
            # First half for standard DTE setting, second half for special DevID and its variety (APIC, HPET, etc.)
            self.Length = (
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size + IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size
            )
            (self.Handle, self.SourceDeviceID, self.Variety) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size : self.Length],
            )

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            byte_str = struct.pack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, self.Type, self.DeviceID, self.DTESetting
            )
            # First half for standard DTE setting, second half for special DevID and its variety (APIC, HPET, etc.)
            byte_str += struct.pack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, self.Handle, self.SourceDeviceID, self.Variety
            )
            return byte_str

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_item = ET.Element(self.TypeString.replace(" ", ""))
            xml_item.set("Type", "0x%X" % self.Type)
            xml_item.set("DeviceID", "0x%X" % (self.DeviceID))
            xml_item.set("DTESetting", "0x%X" % (self.DTESetting))
            xml_item.set("SourceDeviceID", "0x%X" % (self.SourceDeviceID))

            xml_item.set("Handle", "0x%X" % (self.Handle))
            if self.Variety == 1:
                xml_item.set("Variety", "IOAPIC")
            elif self.Variety == 2:
                xml_item.set("Variety", "HPET")
            else:
                xml_item.set("Variety", "Reserved %X" % (self.Variety))
            return xml_item

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t\t  {TypeString:s}".format(TypeString=self.TypeString))
            print("\t\t--------------------------------------------------")
            print("\t\tType                  : 0x{Type:02X}".format(Type=self.Type))
            print("\t\tDevice ID             : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print("\t\tDTE Setting           : 0x{DTESetting:02X}".format(DTESetting=self.DTESetting))
            print("\t\tSource Device ID      : 0x{SourceDeviceID:04X}".format(SourceDeviceID=self.SourceDeviceID))

            if self.Variety == 1:
                var_str = "IOAPIC"
            elif self.Variety == 2:
                var_str = "HPET"
            else:
                var_str = "Reserved 0x%02X" % (self.Variety)
            print("\t\tHandle                : 0x{Handle:02X}".format(Handle=self.Handle))
            print("\t\tVariety               : {Variety:s}".format(Variety=var_str))

    class DEVICE_TABLE_ENTRY_ACPI(object):
        """Object representing a Device Table Entry ACPI."""

        def __init__(self, data: Optional[bytes] = None) -> "IVRS_TABLE.DEVICE_TABLE_ENTRY_ACPI":
            """Inits an empty object."""
            self.Type = 0
            self.DeviceID = 0
            self.DTESetting = 0
            self.ExtendedDTESetting = 0
            self.TypeString = None
            self.Length = 0
            if data is not None:
                self.Decode(data)

        def Decode(self, header_byte_array: bytes) -> None:
            """Loads data into the object from the header_byte_array."""
            (self.Type, self.DeviceID, self.DTESetting) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format,
                header_byte_array[: IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size],
            )
            if self.Type != IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ACPI:
                raise Exception(
                    "Input device type (%d) does not match expectation (%d)",
                    IVRS_TABLE.DEVICE_TABLE_ENTRY.DTE_TYPE.ACPI,
                    self.Type,
                )

            self.TypeString = "Variable Length ACPI HID Device"
            (self.HID, self.CID, self.UIDFormat, self.UIDLength) = struct.unpack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.dte_var_ext_format,
                header_byte_array[
                    IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format_size : IVRS_TABLE.DEVICE_TABLE_ENTRY.dte_var_len
                ],
            )
            self.Length = IVRS_TABLE.DEVICE_TABLE_ENTRY.dte_var_len + self.UIDLength
            if self.UIDFormat == 0:
                self.UID = None
            elif self.UIDFormat == 1:
                (self.UID,) = struct.unpack(
                    "=Q", header_byte_array[IVRS_TABLE.DEVICE_TABLE_ENTRY.dte_var_len : self.Length]
                )
            elif self.UIDFormat == 2:
                (self.UID,) = struct.unpack(
                    "=%ss" % self.UIDLength, header_byte_array[IVRS_TABLE.DEVICE_TABLE_ENTRY.dte_var_len : self.Length]
                )

        def Encode(self) -> bytes:
            r"""Serializes the object.

            Returns:
                (str): string representing packed data as bytes (i.e. b'\x01\x00\x03')
            """
            byte_str = struct.pack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.struct_format, self.Type, self.DeviceID, self.DTESetting
            )
            # Variable Length ACPI HID Device
            byte_str += struct.pack(
                IVRS_TABLE.DEVICE_TABLE_ENTRY.dte_var_ext_format, self.HID, self.CID, self.UIDFormat, self.UIDLength
            )
            if self.UIDFormat == 1:
                byte_str += struct.pack("=Q", self.UID)
            elif self.UIDFormat == 2:
                byte_str += struct.pack("=%ss" % self.UIDLength, self.UID)
            return byte_str

        def ToXmlElementTree(self) -> ET.Element:
            """Transforms the object into an xml item and returns it."""
            xml_item = ET.Element(self.TypeString.replace(" ", ""))
            xml_item.set("Type", "0x%X" % self.Type)
            xml_item.set("DeviceID", "0x%X" % (self.DeviceID))
            xml_item.set("DTESetting", "0x%X" % (self.DTESetting))

            xml_item.set("HardwareID", "%s" % (self.HID))
            xml_item.set("ExtendedDTE Setting", "%s" % (self.CID))
            xml_item.set("UniqueIDFormat", "%d" % (self.UIDFormat))
            xml_item.set("UniqueIDLength", "%d" % (self.UIDLength))
            if self.UIDFormat == 0:
                xml_item.set("UniqueID", "None")
            elif self.UIDFormat == 1:
                xml_item.set("UniqueID", "0x%X" % (self.UID))
            elif self.UIDFormat == 2:
                xml_item.set("UniqueID", "%s" % (self.UID))
            else:
                print("Unrecognized UID format detected")
                sys.exit(-1)
            return xml_item

        def DumpInfo(self) -> None:
            """Prints internal information to the console."""
            print("\t\t  {TypeString:s}".format(TypeString=self.TypeString))
            print("\t\t--------------------------------------------------")
            print("\t\tType                  : 0x{Type:02X}".format(Type=self.Type))
            print("\t\tDevice ID             : 0x{DeviceID:04X}".format(DeviceID=self.DeviceID))
            print("\t\tDTE Setting           : 0x{DTESetting:02X}".format(DTESetting=self.DTESetting))

            print("\t\tHardware ID           : {HID:s}".format(HID=self.HID.decode()))
            print("\t\tExtended DTE Setting  : {CID:s}".format(CID=self.CID.decode()))
            print("\t\tUnique ID Format      : {UIDFormat:d}".format(UIDFormat=self.UIDFormat))
            print("\t\tUnique ID Length      : {UIDLength:d}".format(UIDLength=self.UIDLength))
            if self.UIDFormat == 0:
                print("\t\tUnique ID             : None")
            elif self.UIDFormat == 1:
                print("\t\tUnique ID             : 0x{UID:X}".format(UID=self.UID))
            elif self.UIDFormat == 2:
                print("\t\tUnique ID             : {UID:s}".format(UID=self.UID.decode()))
            else:
                raise Exception("Unrecognized UID format detected %d" % self.UIDFormat)
