#!/usr/bin/env python3 -B
# coding=utf-8

"""
AMI UCP Extract
AMI UCP Update Extractor
Copyright (C) 2021-2024 Plato Mavropoulos
"""

import contextlib
import ctypes
import os
import re
import struct

from typing import Any, Final

from biosutilities.common.checksums import checksum_16
from biosutilities.common.compression import efi_decompress, is_efi_compressed
from biosutilities.common.paths import agnostic_path, delete_file, extract_folder, make_dirs, safe_name, safe_path
from biosutilities.common.patterns import PAT_AMI_UCP, PAT_INTEL_ENGINE
from biosutilities.common.structs import CHAR, ctypes_struct, UINT8, UINT16, UINT32
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import to_string

from biosutilities.ami_pfat_extract import AmiPfatExtract
from biosutilities.insyde_ifd_extract import InsydeIfdExtract


class UafHeader(ctypes.LittleEndianStructure):
    """ UAF Header """

    _pack_ = 1
    _fields_ = [
        ('ModuleTag',       CHAR * 4),      # 0x00
        ('ModuleSize',      UINT32),        # 0x04
        ('Checksum',        UINT16),        # 0x08
        ('Unknown0',        UINT8),         # 0x0A
        ('Unknown1',        UINT8),         # 0x0A
        ('Reserved',        UINT8 * 4)      # 0x0C
        # 0x10
    ]

    def _get_reserved(self) -> str:
        res_bytes: bytes = bytes(self.Reserved)

        res_hex: str = f'0x{int.from_bytes(res_bytes, byteorder="big"):0{0x4 * 2}X}'

        res_str: str = re.sub(r'[\n\t\r\x00 ]', '', res_bytes.decode('utf-8', 'ignore'))

        res_txt: str = f' ({res_str})' if len(res_str) else ''

        return f'{res_hex}{res_txt}'

    def get_tag(self) -> str:
        """ Get UAF Module Tag """

        return self.ModuleTag.decode('utf-8')

    def struct_print(self, padding: int = 0) -> None:
        """ Display structure information """

        printer(message=['Tag          :', self.get_tag()], padding=padding, new_line=False)
        printer(message=['Size         :', f'0x{self.ModuleSize:X}'], padding=padding, new_line=False)
        printer(message=['Checksum     :', f'0x{self.Checksum:04X}'], padding=padding, new_line=False)
        printer(message=['Unknown 0    :', f'0x{self.Unknown0:02X}'], padding=padding, new_line=False)
        printer(message=['Unknown 1    :', f'0x{self.Unknown1:02X}'], padding=padding, new_line=False)
        printer(message=['Reserved     :', self._get_reserved()], padding=padding, new_line=False)


class UafModule(ctypes.LittleEndianStructure):
    """ UAF Module """

    _pack_ = 1
    _fields_ = [
        ('CompressSize',    UINT32),        # 0x00
        ('OriginalSize',    UINT32)         # 0x04
        # 0x08
    ]

    def struct_print(self, filename: str, description: str, padding: int = 0) -> None:
        """ Display structure information """

        printer(message=['Compress Size:', f'0x{self.CompressSize:X}'], padding=padding, new_line=False)
        printer(message=['Original Size:', f'0x{self.OriginalSize:X}'], padding=padding, new_line=False)
        printer(message=['Filename     :', filename], padding=padding, new_line=False)
        printer(message=['Description  :', description], padding=padding, new_line=False)


class UiiHeader(ctypes.LittleEndianStructure):
    """ UII Header """

    _pack_ = 1
    _fields_ = [
        ('UIISize',         UINT16),        # 0x00
        ('Checksum',        UINT16),        # 0x02
        ('UtilityVersion',  UINT32),        # 0x04 AFU|BGT (Unknown, Signed)
        ('InfoSize',        UINT16),        # 0x08
        ('SupportBIOS',     UINT8),         # 0x0A
        ('SupportOS',       UINT8),         # 0x0B
        ('DataBusWidth',    UINT8),         # 0x0C
        ('ProgramType',     UINT8),         # 0x0D
        ('ProgramMode',     UINT8),         # 0x0E
        ('SourceSafeRel',   UINT8)          # 0x0F
        # 0x10
    ]

    SBI: Final[dict[int, str]] = {1: 'ALL', 2: 'AMIBIOS8', 3: 'UEFI', 4: 'AMIBIOS8/UEFI'}
    SOS: Final[dict[int, str]] = {1: 'DOS', 2: 'EFI', 3: 'Windows', 4: 'Linux', 5: 'FreeBSD',
                                  6: 'MacOS', 128: 'Multi-Platform'}
    DBW: Final[dict[int, str]] = {1: '16b', 2: '16/32b', 3: '32b', 4: '64b'}
    PTP: Final[dict[int, str]] = {1: 'Executable', 2: 'Library', 3: 'Driver'}
    PMD: Final[dict[int, str]] = {1: 'API', 2: 'Console', 3: 'GUI', 4: 'Console/GUI'}

    def struct_print(self, description: str, padding: int = 0) -> None:
        """ Display structure information """

        support_bios: str = self.SBI.get(self.SupportBIOS, f'Unknown ({self.SupportBIOS})')
        support_os: str = self.SOS.get(self.SupportOS, f'Unknown ({self.SupportOS})')
        data_bus_width: str = self.DBW.get(self.DataBusWidth, f'Unknown ({self.DataBusWidth})')
        program_type: str = self.PTP.get(self.ProgramType, f'Unknown ({self.ProgramType})')
        program_mode: str = self.PMD.get(self.ProgramMode, f'Unknown ({self.ProgramMode})')

        printer(message=['UII Size      :', f'0x{self.UIISize:X}'], padding=padding, new_line=False)
        printer(message=['Checksum      :', f'0x{self.Checksum:04X}'], padding=padding, new_line=False)
        printer(message=['Tool Version  :', f'0x{self.UtilityVersion:08X}'], padding=padding, new_line=False)
        printer(message=['Info Size     :', f'0x{self.InfoSize:X}'], padding=padding, new_line=False)
        printer(message=['Supported BIOS:', support_bios], padding=padding, new_line=False)
        printer(message=['Supported OS  :', support_os], padding=padding, new_line=False)
        printer(message=['Data Bus Width:', data_bus_width], padding=padding, new_line=False)
        printer(message=['Program Type  :', program_type], padding=padding, new_line=False)
        printer(message=['Program Mode  :', program_mode], padding=padding, new_line=False)
        printer(message=['SourceSafe Tag:', f'{self.SourceSafeRel:02d}'], padding=padding, new_line=False)
        printer(message=['Description   :', description], padding=padding, new_line=False)


class DisHeader(ctypes.LittleEndianStructure):
    """ DIS Header """

    _pack_ = 1
    _fields_ = [
        ('PasswordSize',    UINT16),        # 0x00
        ('EntryCount',      UINT16),        # 0x02
        ('Password',        CHAR * 12)      # 0x04
        # 0x10
    ]

    def struct_print(self, padding: int = 0) -> None:
        """ Display structure information """

        printer(message=['Password Size:', f'0x{self.PasswordSize:X}'], padding=padding, new_line=False)
        printer(message=['Entry Count  :', self.EntryCount], padding=padding, new_line=False)
        printer(message=['Password     :', self.Password.decode('utf-8')], padding=padding, new_line=False)


class DisModule(ctypes.LittleEndianStructure):
    """ DIS Module """

    _pack_ = 1
    _fields_ = [
        ('EnabledDisabled', UINT8),         # 0x00
        ('ShownHidden',     UINT8),         # 0x01
        ('Command',         CHAR * 32),     # 0x02
        ('Description',     CHAR * 256)     # 0x22
        # 0x122
    ]

    ENDIS: Final[dict[int, str]] = {0: 'Disabled', 1: 'Enabled'}
    SHOWN: Final[dict[int, str]] = {0: 'Hidden', 1: 'Shown', 2: 'Shown Only'}

    def struct_print(self, padding: int = 0) -> None:
        """ Display structure information """

        enabled_disabled: str = self.ENDIS.get(self.EnabledDisabled, f'Unknown ({self.EnabledDisabled})')
        shown_hidden: str = self.SHOWN.get(self.ShownHidden, f'Unknown ({self.ShownHidden})')
        command: str = self.Command.decode('utf-8').strip()
        description: str = self.Description.decode('utf-8').strip()

        printer(message=['State      :', enabled_disabled], padding=padding, new_line=False)
        printer(message=['Display    :', shown_hidden], padding=padding, new_line=False)
        printer(message=['Command    :', command], padding=padding, new_line=False)
        printer(message=['Description:', description], padding=padding, new_line=False)


class AmiUcpExtract(BIOSUtility):
    """ AMI UCP Update Extractor """

    TITLE: str = 'AMI UCP Update Extractor'

    # Get common ctypes Structure Sizes
    UAF_HDR_LEN: Final[int] = ctypes.sizeof(UafHeader)
    UAF_MOD_LEN: Final[int] = ctypes.sizeof(UafModule)
    DIS_HDR_LEN: Final[int] = ctypes.sizeof(DisHeader)
    DIS_MOD_LEN: Final[int] = ctypes.sizeof(DisModule)
    UII_HDR_LEN: Final[int] = ctypes.sizeof(UiiHeader)

    # AMI UCP Tag Dictionary
    UAF_TAG_DICT: Final[dict[str, list[str]]] = {
        '@3FI': ['HpBiosUpdate32.efi', 'HpBiosUpdate32.efi', ''],
        '@3S2': ['HpBiosUpdate32.s12', 'HpBiosUpdate32.s12', ''],
        '@3S4': ['HpBiosUpdate32.s14', 'HpBiosUpdate32.s14', ''],
        '@3S9': ['HpBiosUpdate32.s09', 'HpBiosUpdate32.s09', ''],
        '@3SG': ['HpBiosUpdate32.sig', 'HpBiosUpdate32.sig', ''],
        '@AMI': ['UCP_Nested.bin', 'Nested AMI UCP', ''],
        '@B12': ['BiosMgmt.s12', 'BiosMgmt.s12', ''],
        '@B14': ['BiosMgmt.s14', 'BiosMgmt.s14', ''],
        '@B32': ['BiosMgmt32.s12', 'BiosMgmt32.s12', ''],
        '@B34': ['BiosMgmt32.s14', 'BiosMgmt32.s14', ''],
        '@B39': ['BiosMgmt32.s09', 'BiosMgmt32.s09', ''],
        '@B3E': ['BiosMgmt32.efi', 'BiosMgmt32.efi', ''],
        '@BM9': ['BiosMgmt.s09', 'BiosMgmt.s09', ''],
        '@BME': ['BiosMgmt.efi', 'BiosMgmt.efi', ''],
        '@CKV': ['Check_Version.txt', 'Check Version', 'Text'],
        '@CMD': ['AFU_Command.txt', 'AMI AFU Command', 'Text'],
        '@CML': ['CMOSD4.txt', 'CMOS Item Number-Value (MSI)', 'Text'],
        '@CMS': ['CMOSD4.exe', 'Get or Set CMOS Item (MSI)', ''],
        '@CPM': ['AC_Message.txt', 'Confirm Power Message', ''],
        '@D32': ['amifldrv32.sys', 'amifldrv32.sys', ''],
        '@D64': ['amifldrv64.sys', 'amifldrv64.sys', ''],
        '@DCT': ['DevCon32.exe', 'Device Console WIN32', ''],
        '@DCX': ['DevCon64.exe', 'Device Console WIN64', ''],
        '@DFE': ['HpDevFwUpdate.efi', 'HpDevFwUpdate.efi', ''],
        '@DFS': ['HpDevFwUpdate.s12', 'HpDevFwUpdate.s12', ''],
        '@DIS': ['Command_Status.bin', 'Default Command Status', ''],
        '@ENB': ['ENBG64.exe', 'ENBG64.exe', ''],
        '@HPU': ['UCP_Main.bin', 'Utility Auxiliary File (HP)', ''],
        '@INS': ['Insyde_Nested.bin', 'Nested Insyde SFX', ''],
        '@M32': ['HpBiosMgmt32.s12', 'HpBiosMgmt32.s12', ''],
        '@M34': ['HpBiosMgmt32.s14', 'HpBiosMgmt32.s14', ''],
        '@M39': ['HpBiosMgmt32.s09', 'HpBiosMgmt32.s09', ''],
        '@M3I': ['HpBiosMgmt32.efi', 'HpBiosMgmt32.efi', ''],
        '@MEC': ['FWUpdLcl.txt', 'Intel FWUpdLcl Command', 'Text'],
        '@MED': ['FWUpdLcl_DOS.exe', 'Intel FWUpdLcl DOS', ''],
        '@MET': ['FWUpdLcl_WIN32.exe', 'Intel FWUpdLcl WIN32', ''],
        '@MFI': ['HpBiosMgmt.efi', 'HpBiosMgmt.efi', ''],
        '@MS2': ['HpBiosMgmt.s12', 'HpBiosMgmt.s12', ''],
        '@MS4': ['HpBiosMgmt.s14', 'HpBiosMgmt.s14', ''],
        '@MS9': ['HpBiosMgmt.s09', 'HpBiosMgmt.s09', ''],
        '@NAL': ['UCP_List.txt', 'AMI UCP Module Name List', ''],
        '@OKM': ['OK_Message.txt', 'OK Message', ''],
        '@PFC': ['BGT_Command.txt', 'AMI BGT Command', 'Text'],
        '@R3I': ['CryptRSA32.efi', 'CryptRSA32.efi', ''],
        '@RFI': ['CryptRSA.efi', 'CryptRSA.efi', ''],
        '@UAF': ['UCP_Main.bin', 'Utility Auxiliary File (AMI)', ''],
        '@UFI': ['HpBiosUpdate.efi', 'HpBiosUpdate.efi', ''],
        '@UII': ['UCP_Info.txt', 'Utility Identification Information', ''],
        '@US2': ['HpBiosUpdate.s12', 'HpBiosUpdate.s12', ''],
        '@US4': ['HpBiosUpdate.s14', 'HpBiosUpdate.s14', ''],
        '@US9': ['HpBiosUpdate.s09', 'HpBiosUpdate.s09', ''],
        '@USG': ['HpBiosUpdate.sig', 'HpBiosUpdate.sig', ''],
        '@VER': ['OEM_Version.txt', 'OEM Version', 'Text'],
        '@VXD': ['amifldrv.vxd', 'amifldrv.vxd', ''],
        '@W32': ['amifldrv32.sys', 'amifldrv32.sys', ''],
        '@W64': ['amifldrv64.sys', 'amifldrv64.sys', '']
    }

    def __init__(self, input_object: str | bytes | bytearray = b'', extract_path: str = '', padding: int = 0,
                 checksum: bool = False) -> None:
        super().__init__(input_object=input_object, extract_path=extract_path, padding=padding)

        self.checksum: bool = checksum

    def check_format(self) -> bool:
        """ Check if input is AMI UCP image """

        return bool(self._get_ami_ucp())

    def parse_format(self) -> bool:
        """ Parse & Extract AMI UCP structures """

        nal_dict: dict[str, tuple[str, str]] = {}  # Initialize @NAL Dictionary per UCP

        printer(message='Utility Configuration Program', padding=self.padding)

        make_dirs(in_path=self.extract_path)

        # Get best AMI UCP Pattern match based on @UAF|@HPU Size
        ucp_buffer: bytes = self._get_ami_ucp()

        # Parse @UAF|@HPU Header Structure
        uaf_hdr: Any = ctypes_struct(buffer=ucp_buffer, start_offset=0, class_object=UafHeader)

        ucp_tag: str = uaf_hdr.get_tag()

        printer(message=f'Utility Auxiliary File > {ucp_tag}:\n', padding=self.padding + 4)

        uaf_hdr.struct_print(padding=self.padding + 8)

        # Generate UafModule Structure
        fake: bytes = struct.pack('<II', len(ucp_buffer), len(ucp_buffer))

        # Parse @UAF|@HPU Module EFI Structure
        uaf_mod: Any = ctypes_struct(buffer=fake, start_offset=0, class_object=UafModule)

        # Get @UAF|@HPU Module Filename
        uaf_name: str = self.UAF_TAG_DICT[ucp_tag][0]

        # Get @UAF|@HPU Module Description
        uaf_desc: str = self.UAF_TAG_DICT[ucp_tag][1]

        # Print @UAF|@HPU Module EFI Info
        uaf_mod.struct_print(filename=uaf_name, description=uaf_desc, padding=self.padding + 8)

        if self.checksum:
            self._chk16_validate(data=ucp_buffer, tag=ucp_tag, padding=self.padding + 8)

        uaf_all: list[list] = self._get_uaf_mod(buffer=ucp_buffer, uaf_off=self.UAF_HDR_LEN)

        for mod_info in uaf_all:
            nal_dict = self._uaf_extract(buffer=ucp_buffer, extract_path=self.extract_path, mod_info=mod_info,
                                         nal_dict=nal_dict, padding=self.padding + 8)

        return True

    @staticmethod
    def _chk16_validate(data: bytes | bytearray, tag: str, padding: int = 0) -> None:
        """ Validate UCP Module Checksum-16 """

        if checksum_16(data=data) != 0:
            printer(message=f'Error: Invalid UCP Module {tag} Checksum!', padding=padding)
        else:
            printer(message=f'Checksum of UCP Module {tag} is valid!', padding=padding)

    def _get_ami_ucp(self) -> bytes:
        """ Get all input file AMI UCP patterns """

        uaf_mod_dat_max: bytes = b''

        uaf_mod_len_max: int = 0

        for uaf_match in PAT_AMI_UCP.finditer(self.input_buffer):
            uaf_mod_off: int = uaf_match.start()

            uaf_mod_buf: bytes = self.input_buffer[uaf_mod_off:]

            if len(uaf_mod_buf) <= self.UAF_HDR_LEN:
                continue

            uaf_hdr: Any = ctypes_struct(buffer=uaf_mod_buf, start_offset=0, class_object=UafHeader)

            uaf_mod_len: int = uaf_hdr.ModuleSize

            uaf_mod_dat: bytes = uaf_mod_buf[:uaf_mod_len]

            if uaf_mod_len != len(uaf_mod_dat):
                continue

            if checksum_16(data=uaf_mod_dat) != 0:
                continue

            if uaf_mod_len > uaf_mod_len_max:
                uaf_mod_len_max = uaf_mod_len

                uaf_mod_dat_max = uaf_mod_dat

        return uaf_mod_dat_max

    @staticmethod
    def _get_uaf_mod(buffer: bytes | bytearray, uaf_off: int = 0x0) -> list[list]:
        """ Get list of @UAF|@HPU Modules """

        uaf_all: list[list] = []  # Initialize list of all @UAF|@HPU Modules

        while buffer[uaf_off] == 0x40:  # ASCII of @ is 0x40
            # Parse @UAF|@HPU Module Structure
            uaf_hdr: Any = ctypes_struct(buffer=buffer, start_offset=uaf_off, class_object=UafHeader)

            uaf_tag: str = uaf_hdr.get_tag()  # Get unique @UAF|@HPU Module Tag

            uaf_all.append([uaf_tag, uaf_off, uaf_hdr])  # Store @UAF|@HPU Module Info

            uaf_off += uaf_hdr.ModuleSize  # Adjust to next @UAF|@HPU Module offset

            if uaf_off >= len(buffer):
                break  # Stop parsing at EOF

        # Check if @UAF|@HPU Module @NAL exists and place it first
        # Parsing @NAL first allows naming all @UAF|@HPU Modules
        for mod_idx, mod_val in enumerate(uaf_all):
            if mod_val[0] == '@NAL':
                uaf_all.insert(1, uaf_all.pop(mod_idx))  # After UII for visual purposes

                break  # @NAL found, skip the rest

        return uaf_all

    def _uaf_extract(self, buffer: bytes | bytearray, extract_path: str, mod_info: list,
                     nal_dict: dict[str, tuple[str, str]], padding: int = 0) -> dict[str, tuple[str, str]]:
        """ Parse & Extract AMI UCP > @UAF|@HPU Module/Section """

        uaf_tag: str = mod_info[0]
        uaf_off: int = mod_info[1]
        uaf_hdr: Any = mod_info[2]

        uaf_data_all: bytes = buffer[uaf_off:uaf_off + uaf_hdr.ModuleSize]  # @UAF|@HPU Module Entire Data

        uaf_data_mod: bytes = uaf_data_all[self.UAF_HDR_LEN:]  # @UAF|@HPU Module EFI Data

        uaf_data_raw: bytes = uaf_data_mod[self.UAF_MOD_LEN:]  # @UAF|@HPU Module Raw Data

        printer(message=f'Utility Auxiliary File > {uaf_tag}:\n', padding=padding)

        uaf_hdr.struct_print(padding=padding + 4)  # Print @UAF|@HPU Module Info

        # Parse UAF Module EFI Structure
        uaf_mod: Any = ctypes_struct(buffer=buffer, start_offset=uaf_off + self.UAF_HDR_LEN, class_object=UafModule)

        is_comp: bool = uaf_mod.CompressSize != uaf_mod.OriginalSize  # Detect @UAF|@HPU Module EFI Compression

        if uaf_tag in nal_dict:
            uaf_name: str = nal_dict[uaf_tag][1]  # Always prefer @NAL naming first
        elif uaf_tag in self.UAF_TAG_DICT:
            uaf_name = self.UAF_TAG_DICT[uaf_tag][0]  # Otherwise use built-in naming
        elif uaf_tag == '@ROM':
            uaf_name = 'BIOS.bin'  # BIOS/PFAT Firmware (w/o Signature)
        elif uaf_tag.startswith('@R0'):
            uaf_name = f'BIOS_0{uaf_tag[3:]}.bin'  # BIOS/PFAT Firmware
        elif uaf_tag.startswith('@S0'):
            uaf_name = f'BIOS_0{uaf_tag[3:]}.sig'  # BIOS/PFAT Signature
        elif uaf_tag.startswith('@DR'):
            uaf_name = f'DROM_0{uaf_tag[3:]}.bin'  # Thunderbolt Retimer Firmware
        elif uaf_tag.startswith('@DS'):
            uaf_name = f'DROM_0{uaf_tag[3:]}.sig'  # Thunderbolt Retimer Signature
        elif uaf_tag.startswith('@EC'):
            uaf_name = f'EC_0{uaf_tag[3:]}.bin'  # Embedded Controller Firmware
        elif uaf_tag.startswith('@ME'):
            uaf_name = f'ME_0{uaf_tag[3:]}.bin'  # Management Engine Firmware
        else:
            uaf_name = uaf_tag  # Could not name the @UAF|@HPU Module, use Tag instead

        uaf_fext: str = '' if uaf_name != uaf_tag else '.bin'

        uaf_fdesc: str = self.UAF_TAG_DICT[uaf_tag][1] if uaf_tag in self.UAF_TAG_DICT else uaf_name

        # Print @UAF|@HPU Module EFI Info
        uaf_mod.struct_print(filename=uaf_name + uaf_fext, description=uaf_fdesc, padding=padding + 4)

        # Check if unknown @UAF|@HPU Module Tag is present in @NAL but not in built-in dictionary
        if uaf_tag in nal_dict and uaf_tag not in self.UAF_TAG_DICT and \
                not uaf_tag.startswith(('@ROM', '@R0', '@S0', '@DR', '@DS')):

            printer(message=f'Note: Detected new AMI UCP Module {uaf_tag} ({nal_dict[uaf_tag][1]}) in @NAL!',
                    padding=padding + 4)

        # Generate @UAF|@HPU Module File name, depending on whether decompression will be required
        uaf_sname: str = safe_name(in_name=uaf_name + ('.temp' if is_comp else uaf_fext))

        if uaf_tag in nal_dict:
            uaf_npath: str = safe_path(base_path=extract_path, user_paths=nal_dict[uaf_tag][0])

            make_dirs(in_path=uaf_npath)

            uaf_fname: str = safe_path(base_path=uaf_npath, user_paths=uaf_sname)
        else:
            uaf_fname = safe_path(base_path=extract_path, user_paths=uaf_sname)

        if self.checksum:
            self._chk16_validate(data=uaf_data_all, tag=uaf_tag, padding=padding + 4)

        # Parse Utility Identification Information @UAF|@HPU Module (@UII)
        if uaf_tag == '@UII':
            # Parse @UII Module Raw Structure
            info_hdr: Any = ctypes_struct(buffer=uaf_data_raw, start_offset=0, class_object=UiiHeader)

            # @UII Module Info Data
            info_data: bytes = uaf_data_raw[max(self.UII_HDR_LEN, info_hdr.InfoSize):info_hdr.UIISize]

            # Get @UII Module Info/Description text field
            info_desc: str = info_data.decode('utf-8', 'ignore').strip('\x00 ')

            printer(message='Utility Identification Information:\n', padding=padding + 4)

            info_hdr.struct_print(description=info_desc, padding=padding + 8)  # Print @UII Module Info

            if self.checksum:
                self._chk16_validate(data=uaf_data_raw, tag='@UII > Info', padding=padding + 8)

            # Store/Save @UII Module Info in file
            with open(uaf_fname[:-4] + '.txt', 'a', encoding='utf-8') as uii_out:
                with contextlib.redirect_stdout(uii_out):
                    info_hdr.struct_print(description=info_desc, padding=0)  # Store @UII Module Info

        # Adjust @UAF|@HPU Module Raw Data for extraction
        if is_comp:
            # Some Compressed @UAF|@HPU Module EFI data lack necessary EOF padding
            if uaf_mod.CompressSize > len(uaf_data_raw):
                comp_padd: bytes = b'\x00' * (uaf_mod.CompressSize - len(uaf_data_raw))

                # Add missing padding for decompression
                uaf_data_raw = uaf_data_mod[:self.UAF_MOD_LEN] + uaf_data_raw + comp_padd
            else:
                # Add the EFI/Tiano Compression info before Raw Data
                uaf_data_raw = uaf_data_mod[:self.UAF_MOD_LEN] + uaf_data_raw
        else:
            # No compression, extend to end of Original @UAF|@HPU Module size
            uaf_data_raw = uaf_data_raw[:uaf_mod.OriginalSize]

        # Store/Save @UAF|@HPU Module file
        if uaf_tag != '@UII':  # Skip @UII binary, already parsed
            with open(uaf_fname, 'wb') as uaf_out:
                uaf_out.write(uaf_data_raw)

        # @UAF|@HPU Module EFI/Tiano Decompression
        if is_comp and is_efi_compressed(in_object=uaf_data_raw, strict=False):
            # Decompressed @UAF|@HPU Module file path
            dec_fname: str = uaf_fname.replace('.temp', uaf_fext)

            if efi_decompress(in_path=uaf_fname, out_path=dec_fname, padding=padding + 4):
                with open(dec_fname, 'rb') as dec:
                    uaf_data_raw = dec.read()  # Read back the @UAF|@HPU Module decompressed Raw data

                delete_file(in_path=uaf_fname)  # Successful decompression, delete compressed @UAF|@HPU Module file

                uaf_fname = dec_fname  # Adjust @UAF|@HPU Module file path to the decompressed one

        # Process and Print known text only @UAF|@HPU Modules (after EFI/Tiano Decompression)
        if uaf_tag in self.UAF_TAG_DICT and self.UAF_TAG_DICT[uaf_tag][2] == 'Text':
            printer(message=f'{self.UAF_TAG_DICT[uaf_tag][1]}:', padding=padding + 4)

            printer(message=uaf_data_raw.decode('utf-8', 'ignore'), padding=padding + 8)

        # Parse Default Command Status @UAF|@HPU Module (@DIS)
        if len(uaf_data_raw) and uaf_tag == '@DIS':
            # Parse @DIS Module Raw Header Structure
            dis_hdr: Any = ctypes_struct(buffer=uaf_data_raw, start_offset=0, class_object=DisHeader)

            printer(message='Default Command Status Header:\n', padding=padding + 4)

            dis_hdr.struct_print(padding=padding + 8)  # Print @DIS Module Raw Header Info

            # Store/Save @DIS Module Header Info in file
            with open(uaf_fname[:-3] + 'txt', 'a', encoding='utf-8') as dis:
                with contextlib.redirect_stdout(dis):
                    dis_hdr.struct_print(padding=0)  # Store @DIS Module Header Info

            dis_data: bytes = uaf_data_raw[self.DIS_HDR_LEN:]  # @DIS Module Entries Data

            # Parse all @DIS Module Entries
            for mod_idx in range(dis_hdr.EntryCount):
                # Parse @DIS Module Raw Entry Structure
                dis_mod: Any = ctypes_struct(buffer=dis_data, start_offset=mod_idx * self.DIS_MOD_LEN,
                                             class_object=DisModule)

                printer(message=f'Default Command Status Entry {mod_idx + 1:02d}/{dis_hdr.EntryCount:02d}:\n',
                        padding=padding + 8)

                dis_mod.struct_print(padding=padding + 12)  # Print @DIS Module Raw Entry Info

                # Store/Save @DIS Module Entry Info in file
                with open(uaf_fname[:-3] + 'txt', 'a', encoding='utf-8') as dis:
                    with contextlib.redirect_stdout(dis):
                        printer(message=None)

                        dis_mod.struct_print(padding=4)  # Store @DIS Module Entry Info

            delete_file(in_path=uaf_fname)  # Delete @DIS Module binary, info exported as text

        # Parse Name List @UAF|@HPU Module (@NAL)
        if len(uaf_data_raw) >= 5 and (uaf_tag, uaf_data_raw[0], uaf_data_raw[4]) == ('@NAL', 0x40, 0x3A):
            nal_info: list[str] = uaf_data_raw.decode('utf-8',
                                                      errors='ignore').replace('\r', '').strip().split('\n')

            printer(message='AMI UCP Module Name List:\n', padding=padding + 4)

            # Parse all @NAL Module Entries
            for info in nal_info:
                info_tag, info_value = info.split(':', 1)

                # Print @NAL Module Tag-Path Info
                printer(message=f'{info_tag} : {info_value}', padding=padding + 8, new_line=False)

                # Split OS-agnostic path in parts
                info_part: Any = agnostic_path(in_path=info_value).parts

                # Get path without drive/root or file
                info_path: str = to_string(in_object=info_part[1:-1], sep_char=os.sep)

                # Get file from last path part
                info_name: str = info_part[-1]

                # Assign a file path & name to each Tag
                nal_dict[info_tag] = (info_path, info_name)

        # Parse Insyde BIOS @UAF|@HPU Module (@INS)
        if uaf_tag == '@INS':
            ins_dir: str = os.path.join(extract_path, safe_name(in_name=f'{uaf_tag}_nested-IFD'))

            insyde_ifd_extract: InsydeIfdExtract = InsydeIfdExtract(
                input_object=uaf_fname, extract_path=extract_folder(ins_dir), padding=padding + 4)

            if insyde_ifd_extract.check_format():
                if insyde_ifd_extract.parse_format():
                    delete_file(in_path=uaf_fname)  # Delete raw nested Insyde IFD image after successful extraction

        pfat_dir: str = os.path.join(extract_path, safe_name(in_name=uaf_name))

        ami_pfat_extract: AmiPfatExtract = AmiPfatExtract(
            input_object=uaf_data_raw, extract_path=extract_folder(pfat_dir), padding=padding + 4)

        # Detect & Unpack AMI BIOS Guard (PFAT) BIOS image
        if ami_pfat_extract.check_format():
            ami_pfat_extract.parse_format()

            delete_file(in_path=uaf_fname)  # Delete raw PFAT BIOS image after successful extraction

        # Detect Intel Engine firmware image and show ME Analyzer advice
        if uaf_tag.startswith('@ME') and PAT_INTEL_ENGINE.search(uaf_data_raw):
            printer(message='Intel Management Engine (ME) Firmware:\n', padding=padding + 4)
            printer(message='Use "ME Analyzer" from https://github.com/platomav/MEAnalyzer',
                    padding=padding + 8, new_line=False)

        uaf_dir: str = extract_folder(os.path.join(extract_path, safe_name(in_name=f'{uaf_tag}_nested-UCP')))

        ami_ucp_extract: AmiUcpExtract = AmiUcpExtract(
            input_object=uaf_data_raw, extract_path=uaf_dir, padding=padding + 4, checksum=self.checksum)

        # Parse Nested AMI UCP image
        if ami_ucp_extract.check_format():
            ami_ucp_extract.parse_format()

            delete_file(in_path=uaf_fname)  # Delete raw nested AMI UCP image after successful extraction

        return nal_dict
