BIOSUtilities v24.10.29

Added graceful exception hanlding during "main" flow
Improved and cleaned 7-Zip and EFI compression logic
Improved too aggressive extraction directory handling
Fixed input name detection at VAIO Package Extractor
Fixed Intel IBIOSI detection at Apple EFI Identifier
This commit is contained in:
Plato Mavropoulos 2024-10-30 00:47:41 +02:00
parent d8e23f9ef3
commit f895fc208c
24 changed files with 327 additions and 380 deletions

View file

@ -1,3 +1,11 @@
24.10.29
Added graceful exception hanlding during "main" flow
Improved and cleaned 7-Zip and EFI compression logic
Improved too aggressive extraction directory handling
Fixed input name detection at VAIO Package Extractor
Fixed Intel IBIOSI detection at Apple EFI Identifier
24.10.23
New "package" flow, arguments now provided during utility call (README)

View file

@ -272,6 +272,8 @@ No additional optional arguments are provided for this utility.
To run the utility, you must have the following 3rd party tools at PATH or "external":
* [7-Zip Console](https://www.7-zip.org/) (i.e. 7z.exe for Windows or 7zz for macOS or 7zz, 7zzs for Linux)
* [UEFIFind](https://github.com/LongSoft/UEFITool/) (i.e. [UEFIFind.exe for Windows or UEFIFind for Linux/macOS](https://github.com/LongSoft/UEFITool/releases))
* [UEFIExtract](https://github.com/LongSoft/UEFITool/) (i.e. [UEFIExtract.exe for Windows or UEFIExtract for Linux/macOS](https://github.com/LongSoft/UEFITool/releases))
### Apple EFI PBZX Extractor

View file

@ -5,4 +5,4 @@
Copyright (C) 2018-2024 Plato Mavropoulos
"""
__version__ = '24.10.23'
__version__ = '24.10.29'

View file

@ -216,16 +216,12 @@ class AmiPfatExtract(BIOSUtility):
def check_format(self) -> bool:
""" Check if input is AMI BIOS Guard """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
return bool(self._get_ami_pfat(input_object=input_buffer))
return bool(self._get_ami_pfat(input_object=self.input_buffer))
def parse_format(self) -> bool:
""" Process and store AMI BIOS Guard output file """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
pfat_buffer: bytes = self._get_ami_pfat(input_object=input_buffer)
pfat_buffer: bytes = self._get_ami_pfat(input_object=self.input_buffer)
file_path: str = ''
@ -235,7 +231,7 @@ class AmiPfatExtract(BIOSUtility):
extract_name: str = path_name(in_path=self.extract_path).removesuffix(extract_suffix())
make_dirs(in_path=self.extract_path, delete=True)
make_dirs(in_path=self.extract_path)
block_all, block_off, file_count = self._parse_pfat_hdr(buffer=pfat_buffer, padding=self.padding)

View file

@ -17,12 +17,12 @@ 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, extract_folder, make_dirs, safe_name, safe_path
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 file_to_bytes, to_string
from biosutilities.common.texts import to_string
from biosutilities.ami_pfat_extract import AmiPfatExtract
from biosutilities.insyde_ifd_extract import InsydeIfdExtract
@ -259,23 +259,19 @@ class AmiUcpExtract(BIOSUtility):
def check_format(self) -> bool:
""" Check if input is AMI UCP image """
buffer: bytes = file_to_bytes(in_object=self.input_object)
return bool(self._get_ami_ucp(input_object=buffer)[0])
return bool(self._get_ami_ucp()[0])
def parse_format(self) -> bool:
""" Parse & Extract AMI UCP structures """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
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, delete=True)
make_dirs(in_path=self.extract_path)
# Get best AMI UCP Pattern match based on @UAF|@HPU Size
ucp_buffer, ucp_tag = self._get_ami_ucp(input_object=input_buffer)
ucp_buffer, ucp_tag = self._get_ami_ucp()
# Parse @UAF|@HPU Header Structure
uaf_hdr: Any = ctypes_struct(buffer=ucp_buffer, start_offset=0, class_object=UafHeader)
@ -284,14 +280,17 @@ class AmiUcpExtract(BIOSUtility):
uaf_hdr.struct_print(padding=self.padding + 8)
fake = struct.pack('<II', len(ucp_buffer), len(ucp_buffer)) # Generate UafModule Structure
# 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)
uaf_name = self.UAF_TAG_DICT[ucp_tag][0] # Get @UAF|@HPU Module Filename
# Get @UAF|@HPU Module Filename
uaf_name = self.UAF_TAG_DICT[ucp_tag][0]
uaf_desc = self.UAF_TAG_DICT[ucp_tag][1] # Get @UAF|@HPU Module Description
# Get @UAF|@HPU Module Description
uaf_desc = 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)
@ -316,23 +315,21 @@ class AmiUcpExtract(BIOSUtility):
else:
printer(message=f'Checksum of UCP Module {tag} is valid!', padding=padding)
@staticmethod
def _get_ami_ucp(input_object: str | bytes | bytearray) -> tuple[bytes, str]:
def _get_ami_ucp(self) -> tuple[bytes, str]:
""" Get all input file AMI UCP patterns """
buffer: bytes = file_to_bytes(in_object=input_object)
uaf_len_max: int = 0x0 # Length of largest detected @UAF|@HPU
uaf_buf_bin: bytes = b'' # Buffer of largest detected @UAF|@HPU
uaf_buf_tag: str = '@UAF' # Tag of largest detected @UAF|@HPU
for uaf in PAT_AMI_UCP.finditer(buffer):
uaf_len_cur: int = int.from_bytes(buffer[uaf.start() + 0x4:uaf.start() + 0x8], byteorder='little')
for uaf in PAT_AMI_UCP.finditer(self.input_buffer):
uaf_len_cur: int = int.from_bytes(
self.input_buffer[uaf.start() + 0x4:uaf.start() + 0x8], byteorder='little')
if uaf_len_cur > uaf_len_max:
uaf_len_max = uaf_len_cur
uaf_buf_bin = buffer[uaf.start():uaf.start() + uaf_len_max]
uaf_buf_bin = self.input_buffer[uaf.start():uaf.start() + uaf_len_max]
uaf_buf_tag = uaf.group(0)[:4].decode('utf-8', 'ignore')
@ -431,7 +428,7 @@ class AmiUcpExtract(BIOSUtility):
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, exist_ok=True)
make_dirs(in_path=uaf_npath)
uaf_fname: str = safe_path(base_path=uaf_npath, user_paths=uaf_sname)
else:
@ -484,7 +481,7 @@ class AmiUcpExtract(BIOSUtility):
uaf_out.write(uaf_data_raw)
# @UAF|@HPU Module EFI/Tiano Decompression
if is_comp and is_efi_compressed(data=uaf_data_raw, strict=False):
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)
@ -492,7 +489,7 @@ class AmiUcpExtract(BIOSUtility):
with open(dec_fname, 'rb') as dec:
uaf_data_raw = dec.read() # Read back the @UAF|@HPU Module decompressed Raw data
os.remove(uaf_fname) # Successful decompression, delete compressed @UAF|@HPU Module file
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
@ -536,7 +533,7 @@ class AmiUcpExtract(BIOSUtility):
dis_mod.struct_print(padding=4) # Store @DIS Module Entry Info
os.remove(uaf_fname) # Delete @DIS Module binary, info exported as text
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):
@ -573,7 +570,7 @@ class AmiUcpExtract(BIOSUtility):
if insyde_ifd_extract.check_format():
if insyde_ifd_extract.parse_format():
os.remove(uaf_fname) # Delete raw nested Insyde IFD image after successful extraction
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))
@ -584,7 +581,7 @@ class AmiUcpExtract(BIOSUtility):
if ami_pfat_extract.check_format():
ami_pfat_extract.parse_format()
os.remove(uaf_fname) # Delete raw PFAT BIOS image after successful extraction
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):
@ -601,6 +598,6 @@ class AmiUcpExtract(BIOSUtility):
if ami_ucp_extract.check_format():
ami_ucp_extract.parse_format()
os.remove(uaf_fname) # Delete raw nested AMI UCP image after successful extraction
delete_file(in_path=uaf_fname) # Delete raw nested AMI UCP image after successful extraction
return nal_dict

View file

@ -8,7 +8,6 @@ Copyright (C) 2018-2024 Plato Mavropoulos
"""
import ctypes
import logging
import os
import struct
import subprocess
@ -19,12 +18,13 @@ from re import Match
from typing import Any, Final
from biosutilities.common.externals import uefiextract_path, uefifind_path
from biosutilities.common.paths import delete_dirs, delete_file, is_access, is_file, path_suffixes, runtime_root
from biosutilities.common.paths import delete_dirs, delete_file, is_file, make_dirs, path_suffixes, runtime_root
from biosutilities.common.patterns import PAT_INTEL_IBIOSI, PAT_APPLE_ROM_VER
from biosutilities.common.structs import CHAR, ctypes_struct, UINT8
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
EFI_EXTENSIONS: Final[list[str]] = ['.fd', '.scap', '.im4p']
class IntelBiosId(ctypes.LittleEndianStructure):
@ -131,87 +131,82 @@ class AppleEfiIdentify(BIOSUtility):
def check_format(self) -> bool:
""" Check if input is Apple EFI image """
if isinstance(self.input_object, str) and is_file(in_path=self.input_object) and is_access(
in_path=self.input_object):
if path_suffixes(in_path=self.input_object)[-1].lower() not in ('.fd', '.scap', '.im4p'):
if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
if path_suffixes(in_path=self.input_object)[-1].lower() not in EFI_EXTENSIONS:
return False
input_path: str = self.input_object
input_buffer: bytes = file_to_bytes(in_object=input_path)
elif isinstance(self.input_object, (bytes, bytearray)):
else:
input_path = os.path.join(runtime_root(), 'APPLE_EFI_ID_INPUT_BUFFER_CHECK.tmp')
input_buffer = self.input_object
with open(input_path, 'wb') as check_out:
check_out.write(input_buffer)
else:
return False
check_out.write(self.input_buffer)
try:
if PAT_INTEL_IBIOSI.search(input_buffer):
return True
_ = subprocess.run([uefifind_path(), input_path, 'body', 'list', self.PAT_UEFIFIND],
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
input_buffer: bytes = self.input_buffer
if PAT_INTEL_IBIOSI.search(input_buffer):
return True
except Exception as error: # pylint: disable=broad-except
logging.debug('Error: Could not check if input is Apple EFI image: %s', error)
return False
finally:
if input_path != self.input_object:
delete_file(in_path=input_path)
uefifind_cmd: list[str] = [uefifind_path(), input_path, 'body', 'list', self.PAT_UEFIFIND]
uefifind_res: subprocess.CompletedProcess[bytes] = subprocess.run(
uefifind_cmd, check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if input_path != self.input_object:
delete_file(in_path=input_path)
if uefifind_res.returncode == 0:
return True
return False
def parse_format(self) -> bool:
""" Parse & Identify (or Rename) Apple EFI image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
input_path: str = self.input_object
else:
input_path = os.path.join(runtime_root(), 'APPLE_EFI_ID_INPUT_BUFFER_PARSE.bin')
input_path = os.path.join(runtime_root(), 'APPLE_EFI_ID_INPUT_BUFFER_PARSE.tmp')
with open(input_path, 'wb') as parse_out:
parse_out.write(input_buffer)
parse_out.write(self.input_buffer)
bios_id_match: Match[bytes] | None = PAT_INTEL_IBIOSI.search(input_buffer)
bios_id_match: Match[bytes] | None = PAT_INTEL_IBIOSI.search(self.input_buffer)
if bios_id_match:
bios_id_res: str = f'0x{bios_id_match.start():X}'
bios_id_hdr: Any = ctypes_struct(buffer=input_buffer, start_offset=bios_id_match.start(),
bios_id_hdr: Any = ctypes_struct(buffer=self.input_buffer, start_offset=bios_id_match.start(),
class_object=IntelBiosId)
else:
# The $IBIOSI$ pattern is within EFI compressed modules so we need to use UEFIFind and UEFIExtract
try:
bios_id_res = subprocess.check_output([uefifind_path(), input_path, 'body', 'list',
self.PAT_UEFIFIND], text=True)[:36]
# UEFIExtract must create its output folder itself
delete_dirs(in_path=self.extract_path)
bios_id_res = subprocess.check_output([uefifind_path(), input_path, 'body', 'list',
self.PAT_UEFIFIND], text=True)[:36]
_ = subprocess.run([uefiextract_path(), input_path, bios_id_res, '-o', self.extract_path, '-m', 'body'],
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
make_dirs(in_path=self.extract_path)
with open(os.path.join(self.extract_path, 'body.bin'), 'rb') as raw_body:
body_buffer: bytes = raw_body.read()
uefiextract_dir: str = os.path.join(self.extract_path, 'uefiextract_temp')
# Detect decompressed $IBIOSI$ pattern
bios_id_match = PAT_INTEL_IBIOSI.search(body_buffer)
# UEFIExtract must create its output folder itself
delete_dirs(in_path=uefiextract_dir)
if not bios_id_match:
raise RuntimeError('Failed to detect decompressed $IBIOSI$ pattern!')
_ = subprocess.run([uefiextract_path(), input_path, bios_id_res, '-o', uefiextract_dir, '-m', 'body'],
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
bios_id_hdr = ctypes_struct(buffer=body_buffer, start_offset=bios_id_match.start(),
class_object=IntelBiosId)
with open(os.path.join(uefiextract_dir, 'body.bin'), 'rb') as raw_body:
body_buffer: bytes = raw_body.read()
delete_dirs(in_path=self.extract_path) # Successful UEFIExtract extraction, remove its output folder
except Exception as error: # pylint: disable=broad-except
printer(message=f'Error: Failed to parse compressed $IBIOSI$ pattern: {error}!', padding=self.padding)
# Detect decompressed $IBIOSI$ pattern
bios_id_match = PAT_INTEL_IBIOSI.search(body_buffer)
return False
if not bios_id_match:
raise RuntimeError('Failed to detect decompressed $IBIOSI$ pattern!')
bios_id_hdr = ctypes_struct(buffer=body_buffer, start_offset=bios_id_match.start(),
class_object=IntelBiosId)
delete_dirs(in_path=uefiextract_dir) # Successful UEFIExtract extraction, remove its output folder
if not self.silent:
printer(message=f'Detected Intel BIOS Info at {bios_id_res}\n', padding=self.padding)
@ -220,10 +215,10 @@ class AppleEfiIdentify(BIOSUtility):
self.intel_bios_info = bios_id_hdr.get_bios_id()
self.efi_file_name = (f'{self.intel_bios_info["efi_name_id"]}_{zlib.adler32(input_buffer):08X}'
self.efi_file_name = (f'{self.intel_bios_info["efi_name_id"]}_{zlib.adler32(self.input_buffer):08X}'
f'{path_suffixes(in_path=input_path)[-1]}')
_ = self._apple_rom_version(input_buffer=input_buffer, padding=self.padding)
_ = self._apple_rom_version(input_buffer=self.input_buffer, padding=self.padding)
if input_path != self.input_object:
delete_file(in_path=input_path)

View file

@ -16,7 +16,6 @@ from biosutilities.common.paths import make_dirs, path_stem
from biosutilities.common.patterns import PAT_APPLE_IM4P, PAT_INTEL_FD
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class AppleEfiIm4pSplit(BIOSUtility):
@ -33,9 +32,7 @@ class AppleEfiIm4pSplit(BIOSUtility):
if isinstance(self.input_object, str) and not self.input_object.lower().endswith('.im4p'):
return False
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
if PAT_APPLE_IM4P.search(input_buffer) and PAT_INTEL_FD.search(input_buffer):
if PAT_APPLE_IM4P.search(self.input_buffer) and PAT_INTEL_FD.search(self.input_buffer):
return True
return False
@ -45,12 +42,10 @@ class AppleEfiIm4pSplit(BIOSUtility):
parse_success: bool = True
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
make_dirs(in_path=self.extract_path, delete=True)
make_dirs(in_path=self.extract_path)
# Detect IM4P EFI pattern
im4p_match: Match[bytes] | None = PAT_APPLE_IM4P.search(input_buffer)
im4p_match: Match[bytes] | None = PAT_APPLE_IM4P.search(self.input_buffer)
if not im4p_match:
return False
@ -59,14 +54,14 @@ class AppleEfiIm4pSplit(BIOSUtility):
# However, _MEFIBIN is not required for splitting SPI images due to Intel Flash Descriptor Components Density.
# IM4P mefi payload start offset
mefi_data_bgn: int = im4p_match.start() + input_buffer[im4p_match.start() - 0x1]
mefi_data_bgn: int = im4p_match.start() + self.input_buffer[im4p_match.start() - 0x1]
# IM4P mefi payload size
mefi_data_len: int = int.from_bytes(input_buffer[im4p_match.end() + 0x5:im4p_match.end() + 0x9],
mefi_data_len: int = int.from_bytes(self.input_buffer[im4p_match.end() + 0x5:im4p_match.end() + 0x9],
byteorder='big')
# Check if mefi is followed by _MEFIBIN
mefibin_exist: bool = input_buffer[mefi_data_bgn:mefi_data_bgn + 0x8] == b'_MEFIBIN'
mefibin_exist: bool = self.input_buffer[mefi_data_bgn:mefi_data_bgn + 0x8] == b'_MEFIBIN'
# Actual multi EFI payloads start after _MEFIBIN
efi_data_bgn: int = mefi_data_bgn + 0x100 if mefibin_exist else mefi_data_bgn
@ -75,7 +70,7 @@ class AppleEfiIm4pSplit(BIOSUtility):
efi_data_len: int = mefi_data_len - 0x100 if mefibin_exist else mefi_data_len
# Adjust input file buffer to actual multi EFI payloads data
input_buffer = input_buffer[efi_data_bgn:efi_data_bgn + efi_data_len]
input_buffer = self.input_buffer[efi_data_bgn:efi_data_bgn + efi_data_len]
# Parse Intel Flash Descriptor pattern matches
for ifd in PAT_INTEL_FD.finditer(input_buffer):

View file

@ -15,12 +15,11 @@ import os
from typing import Any, Final
from biosutilities.common.compression import is_szip_supported, szip_decompress
from biosutilities.common.paths import make_dirs, path_stem
from biosutilities.common.paths import delete_file, make_dirs, path_stem
from biosutilities.common.patterns import PAT_APPLE_PBZX
from biosutilities.common.structs import ctypes_struct, UINT32
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class PbzxChunk(ctypes.BigEndianStructure):
@ -54,16 +53,12 @@ class AppleEfiPbzxExtract(BIOSUtility):
def check_format(self) -> bool:
""" Check if input is Apple PBZX image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
return bool(PAT_APPLE_PBZX.search(input_buffer, 0, 4))
return bool(PAT_APPLE_PBZX.search(self.input_buffer, 0, 4))
def parse_format(self) -> bool:
""" Parse & Extract Apple PBZX image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
make_dirs(in_path=self.extract_path, delete=True)
make_dirs(in_path=self.extract_path)
cpio_bin: bytes = b'' # Initialize PBZX > CPIO Buffer
@ -71,8 +66,8 @@ class AppleEfiPbzxExtract(BIOSUtility):
chunk_off: int = 0xC # First PBZX Chunk starts at 0xC
while chunk_off < len(input_buffer):
chunk_hdr: Any = ctypes_struct(buffer=input_buffer, start_offset=chunk_off, class_object=PbzxChunk)
while chunk_off < len(self.input_buffer):
chunk_hdr: Any = ctypes_struct(buffer=self.input_buffer, start_offset=chunk_off, class_object=PbzxChunk)
printer(message=f'PBZX Chunk at 0x{chunk_off:08X}\n', padding=self.padding)
@ -84,7 +79,7 @@ class AppleEfiPbzxExtract(BIOSUtility):
# To avoid a potential infinite loop, double-check Compressed Size
comp_end: int = comp_bgn + max(chunk_hdr.CompSize, self.PBZX_CHUNK_HDR_LEN)
comp_bin: bytes = input_buffer[comp_bgn:comp_end]
comp_bin: bytes = self.input_buffer[comp_bgn:comp_end]
try:
# Attempt XZ decompression, if applicable to Chunk data
@ -117,10 +112,10 @@ class AppleEfiPbzxExtract(BIOSUtility):
cpio_object.write(cpio_bin)
# Decompress PBZX > CPIO archive with 7-Zip
if is_szip_supported(in_path=cpio_path, padding=self.padding, args=['-tCPIO'], silent=False):
if is_szip_supported(in_path=cpio_path, args=['-tCPIO']):
if szip_decompress(in_path=cpio_path, out_path=self.extract_path, in_name='CPIO',
padding=self.padding, args=['-tCPIO']):
os.remove(cpio_path) # Successful extraction, delete PBZX > CPIO archive
delete_file(in_path=cpio_path) # Successful extraction, delete PBZX > CPIO archive
else:
return False
else:

View file

@ -10,13 +10,12 @@ Copyright (C) 2019-2024 Plato Mavropoulos
import os
from biosutilities.common.compression import is_szip_supported, szip_decompress
from biosutilities.common.paths import (copy_file, delete_dirs, extract_folder, is_access, is_file, make_dirs,
path_files, path_name, path_parent, path_suffixes, runtime_root)
from biosutilities.common.paths import (copy_file, delete_dirs, delete_file, extract_folder, is_access, is_dir,
is_file, make_dirs, path_files, path_name, path_suffixes, runtime_root)
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
from biosutilities.apple_efi_id import AppleEfiIdentify
from biosutilities.apple_efi_id import AppleEfiIdentify, EFI_EXTENSIONS
from biosutilities.apple_efi_im4p import AppleEfiIm4pSplit
from biosutilities.apple_efi_pbzx import AppleEfiPbzxExtract
@ -31,15 +30,13 @@ class AppleEfiPkgExtract(BIOSUtility):
is_apple_efi_pkg: bool = False
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
input_path: str = self.input_object
else:
input_path = os.path.join(runtime_root(), 'APPLE_EFI_PKG_INPUT_BUFFER_CHECK.bin')
with open(input_path, 'wb') as input_path_object:
input_path_object.write(input_buffer)
input_path_object.write(self.input_buffer)
for pkg_type in ('XAR', 'TAR', 'DMG'):
if is_szip_supported(in_path=input_path, args=[f'-t{pkg_type}:s0']):
@ -48,7 +45,7 @@ class AppleEfiPkgExtract(BIOSUtility):
break
if input_path != self.input_object:
os.remove(input_path)
delete_file(in_path=input_path)
return is_apple_efi_pkg
@ -61,16 +58,14 @@ class AppleEfiPkgExtract(BIOSUtility):
input_path = os.path.join(runtime_root(), 'APPLE_EFI_PKG_INPUT_BUFFER_PARSE.bin')
with open(input_path, 'wb') as input_path_object:
input_path_object.write(file_to_bytes(in_object=self.input_object))
make_dirs(in_path=self.extract_path, delete=True)
input_path_object.write(self.input_buffer)
working_dir: str = os.path.join(self.extract_path, 'temp')
make_dirs(in_path=working_dir)
for pkg_type in ('XAR', 'TAR', 'DMG'):
if is_szip_supported(in_path=input_path, padding=self.padding, args=[f'-t{pkg_type}']):
if is_szip_supported(in_path=input_path, args=[f'-t{pkg_type}']):
if szip_decompress(in_path=input_path, out_path=working_dir, in_name=pkg_type, padding=self.padding,
args=None if pkg_type == 'DMG' else [f'-t{pkg_type}']):
break
@ -78,52 +73,52 @@ class AppleEfiPkgExtract(BIOSUtility):
return False
if input_path != self.input_object:
os.remove(input_path)
delete_file(in_path=input_path)
for work_file in path_files(in_path=working_dir):
if is_file(in_path=work_file) and is_access(in_path=work_file):
self._pbzx_zip(input_path=work_file, extract_path=self.extract_path, padding=self.padding + 4)
self._gzip_cpio(input_path=work_file, extract_path=self.extract_path, padding=self.padding + 4)
self._dmg_zip(input_path=work_file, extract_path=self.extract_path, padding=self.padding + 4)
self._xar_gzip(input_path=work_file, extract_path=self.extract_path, padding=self.padding + 4)
self._pbzx_zip(input_path=work_file, padding=self.padding + 4)
self._gzip_cpio(input_path=work_file, padding=self.padding + 4)
self._dmg_zip(input_path=work_file, padding=self.padding + 4)
self._xar_gzip(input_path=work_file, padding=self.padding + 4)
delete_dirs(in_path=working_dir)
return True
def _xar_gzip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
def _xar_gzip(self, input_path: str, padding: int = 0) -> None:
""" XAR/TAR > GZIP """
for pkg_type in ('XAR', 'TAR'):
if is_szip_supported(in_path=input_path, padding=padding, args=[f'-t{pkg_type}']):
if is_szip_supported(in_path=input_path, args=[f'-t{pkg_type}']):
pkg_path: str = extract_folder(in_path=input_path, suffix=f'_{pkg_type.lower()}_gzip')
if szip_decompress(in_path=input_path, out_path=pkg_path, in_name=pkg_type,
padding=padding, args=[f'-t{pkg_type}']):
for pkg_file in path_files(in_path=pkg_path):
if is_file(in_path=pkg_file) and is_access(in_path=pkg_file):
self._gzip_cpio(input_path=pkg_file, extract_path=extract_path, padding=padding + 4)
self._gzip_cpio(input_path=pkg_file, padding=padding + 4)
break
def _dmg_zip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
def _dmg_zip(self, input_path: str, padding: int = 0) -> None:
""" DMG > ZIP """
if is_szip_supported(in_path=input_path, padding=padding, args=['-tDMG']):
if is_szip_supported(in_path=input_path, args=['-tDMG']):
dmg_path: str = extract_folder(in_path=input_path, suffix='_dmg_zip')
if szip_decompress(in_path=input_path, out_path=dmg_path, in_name='DMG', padding=padding, args=None):
for dmg_file in path_files(in_path=dmg_path):
if is_file(in_path=dmg_file) and is_access(in_path=dmg_file):
if is_szip_supported(in_path=dmg_file, padding=padding + 4, args=['-tZIP']):
if is_szip_supported(in_path=dmg_file, args=['-tZIP']):
zip_path: str = extract_folder(in_path=dmg_file)
if szip_decompress(in_path=dmg_file, out_path=zip_path, in_name='ZIP',
padding=padding + 4, args=['-tZIP']):
for zip_file in path_files(in_path=zip_path):
self._im4p_id(input_path=zip_file, output_path=extract_path, padding=padding + 8)
self._im4p_id(input_path=zip_file, padding=padding + 8)
def _pbzx_zip(self, input_path: str, extract_path: str, padding: int = 0) -> None:
def _pbzx_zip(self, input_path: str, padding: int = 0) -> None:
""" PBZX > ZIP """
pbzx_path: str = extract_folder(in_path=input_path, suffix='_pbzx_zip')
@ -139,51 +134,48 @@ class AppleEfiPkgExtract(BIOSUtility):
for pbzx_file in path_files(in_path=pbzx_path):
if is_file(in_path=pbzx_file) and is_access(in_path=pbzx_file):
if is_szip_supported(in_path=pbzx_file, padding=padding + 4, args=['-tZIP']):
if is_szip_supported(in_path=pbzx_file, args=['-tZIP']):
zip_path: str = extract_folder(in_path=pbzx_file)
if szip_decompress(in_path=pbzx_file, out_path=zip_path, in_name='ZIP',
padding=padding + 4, args=['-tZIP']):
for zip_file in path_files(in_path=zip_path):
self._im4p_id(input_path=zip_file, output_path=extract_path, padding=padding + 8)
self._im4p_id(input_path=zip_file, padding=padding + 8)
def _gzip_cpio(self, input_path: str, extract_path: str, padding: int = 0) -> None:
def _gzip_cpio(self, input_path: str, padding: int = 0) -> None:
""" GZIP > CPIO """
if is_szip_supported(in_path=input_path, padding=padding, args=['-tGZIP']):
if is_szip_supported(in_path=input_path, args=['-tGZIP']):
gzip_path: str = extract_folder(in_path=input_path, suffix='_gzip_cpio')
if szip_decompress(in_path=input_path, out_path=gzip_path, in_name='GZIP',
padding=padding, args=['-tGZIP']):
for gzip_file in path_files(in_path=gzip_path):
if is_file(in_path=gzip_file) and is_access(in_path=gzip_file):
if is_szip_supported(in_path=gzip_file, padding=padding + 4, args=['-tCPIO']):
if is_szip_supported(in_path=gzip_file, args=['-tCPIO']):
cpio_path: str = extract_folder(in_path=gzip_file)
if szip_decompress(in_path=gzip_file, out_path=cpio_path, in_name='CPIO',
padding=padding + 4, args=['-tCPIO']):
for cpio_file in path_files(in_path=cpio_path):
self._im4p_id(input_path=cpio_file, output_path=extract_path, padding=padding + 8)
self._im4p_id(input_path=cpio_file, padding=padding + 8)
@staticmethod
def _im4p_id(input_path: str, output_path: str, padding: int = 0) -> None:
""" Split IM4P (if applicable), identify and rename EFI """
def _im4p_id(self, input_path: str, padding: int = 0) -> None:
""" Split IM4P (if applicable), identify and copy EFI """
if path_suffixes(in_path=input_path)[-1].lower() not in EFI_EXTENSIONS:
return None
if not (is_file(in_path=input_path) and is_access(in_path=input_path)):
return None
if path_suffixes(in_path=input_path)[-1].lower() not in ('.fd', '.scap', '.im4p'):
return None
if not AppleEfiIdentify(input_object=input_path).check_format():
return None
input_name: str = path_name(in_path=input_path)
printer(message=input_name, padding=padding)
working_dir: str = extract_folder(in_path=input_path)
if not AppleEfiIdentify(input_object=input_path, extract_path=working_dir).check_format():
return None
printer(message=path_name(in_path=input_path), padding=padding)
im4p_module: AppleEfiIm4pSplit = AppleEfiIm4pSplit(
input_object=input_path, extract_path=working_dir, padding=padding + 8)
@ -191,27 +183,19 @@ class AppleEfiPkgExtract(BIOSUtility):
printer(message=f'Splitting IM4P via {im4p_module.TITLE}', padding=padding + 4)
im4p_module.parse_format()
else:
make_dirs(in_path=working_dir, delete=True)
copy_file(in_path=input_path, out_path=working_dir, metadata=True)
for efi_source in path_files(in_path=working_dir):
if is_file(in_path=efi_source) and is_access(in_path=efi_source):
for efi_path in path_files(in_path=working_dir) if is_dir(in_path=working_dir) else [input_path]:
if is_file(in_path=efi_path) and is_access(in_path=efi_path):
efi_id_module: AppleEfiIdentify = AppleEfiIdentify(
input_object=efi_source, extract_path=extract_folder(in_path=efi_source), padding=padding + 8)
input_object=efi_path, extract_path=extract_folder(in_path=efi_path), padding=padding + 8)
if efi_id_module.check_format():
printer(message=f'Identifying EFI via {efi_id_module.TITLE}', padding=padding + 4)
if efi_id_module.parse_format():
efi_dest: str = os.path.join(path_parent(in_path=efi_source), efi_id_module.efi_file_name)
efi_path_final: str = os.path.join(self.extract_path, efi_id_module.efi_file_name)
os.replace(efi_source, efi_dest)
for efi_final in path_files(in_path=working_dir):
if is_file(in_path=efi_final) and is_access(in_path=efi_final):
copy_file(in_path=efi_final, out_path=output_path, metadata=True)
copy_file(in_path=efi_path, out_path=efi_path_final, metadata=True)
delete_dirs(in_path=working_dir)

View file

@ -10,11 +10,10 @@ Copyright (C) 2018-2024 Plato Mavropoulos
import os
from biosutilities.common.compression import szip_decompress
from biosutilities.common.paths import clear_readonly, extract_folder, is_file, make_dirs, safe_name
from biosutilities.common.paths import clear_readonly, delete_file, extract_folder, is_file, make_dirs, safe_name
from biosutilities.common.patterns import PAT_AWARD_LZH
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class AwardBiosExtract(BIOSUtility):
@ -25,18 +24,14 @@ class AwardBiosExtract(BIOSUtility):
def check_format(self) -> bool:
""" Check if input is Award BIOS image """
in_buffer: bytes = file_to_bytes(in_object=self.input_object)
return bool(PAT_AWARD_LZH.search(in_buffer))
return bool(PAT_AWARD_LZH.search(self.input_buffer))
def parse_format(self) -> bool:
""" Parse & Extract Award BIOS image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
make_dirs(in_path=self.extract_path)
make_dirs(in_path=self.extract_path, delete=True)
for lzh_match in PAT_AWARD_LZH.finditer(input_buffer):
for lzh_match in PAT_AWARD_LZH.finditer(self.input_buffer):
lzh_type: str = lzh_match.group(0).decode('utf-8')
lzh_text: str = f'LZH-{lzh_type.strip("-").upper()}'
@ -44,11 +39,11 @@ class AwardBiosExtract(BIOSUtility):
lzh_bgn: int = lzh_match.start()
mod_bgn: int = lzh_bgn - 0x2
hdr_len: int = input_buffer[mod_bgn]
mod_len: int = int.from_bytes(input_buffer[mod_bgn + 0x7:mod_bgn + 0xB], byteorder='little')
hdr_len: int = self.input_buffer[mod_bgn]
mod_len: int = int.from_bytes(self.input_buffer[mod_bgn + 0x7:mod_bgn + 0xB], byteorder='little')
mod_end: int = lzh_bgn + hdr_len + mod_len
mod_bin: bytes = input_buffer[mod_bgn:mod_end]
mod_bin: bytes = self.input_buffer[mod_bgn:mod_end]
if len(mod_bin) != 0x2 + hdr_len + mod_len:
printer(message=f'Error: Skipped incomplete LZH stream at 0x{mod_bgn:X}!',
@ -78,7 +73,7 @@ class AwardBiosExtract(BIOSUtility):
if is_file(in_path=mod_path):
clear_readonly(in_path=lzh_path)
os.remove(lzh_path) # Successful extraction, delete LZH archive
delete_file(in_path=lzh_path) # Successful extraction, delete LZH archive
award_bios_extract: AwardBiosExtract = AwardBiosExtract(
input_object=mod_path, extract_path=extract_folder(mod_path), padding=self.padding + 8)

View file

@ -5,18 +5,18 @@
Copyright (C) 2022-2024 Plato Mavropoulos
"""
import os
import subprocess
from typing import Final
from biosutilities.common.externals import szip_path, tiano_path
from biosutilities.common.paths import is_dir, is_empty_dir
from biosutilities.common.paths import is_access, is_dir, is_file, is_empty_dir, path_size
from biosutilities.common.system import printer
from biosutilities.common.texts import file_to_bytes
# 7-Zip switches to auto rename, ignore passwords, ignore prompts, ignore wildcards,
# eliminate root duplication, set UTF-8 charset, suppress stdout, suppress stderr,
# suppress progress, disable headers, disable progress, disable output logs
# suppress progress, disable headers, disable progress, disable output logging
SZIP_COMMON: Final[list[str]] = ['-aou', '-p', '-y', '-spd', '-spe', '-sccUTF-8',
'-bso0', '-bse0', '-bsp0', '-ba', '-bd', '-bb0']
@ -24,13 +24,6 @@ SZIP_COMMON: Final[list[str]] = ['-aou', '-p', '-y', '-spd', '-spe', '-sccUTF-8'
SZIP_SUCCESS: Final[list[int]] = [0, 1]
def szip_code_assert(exit_code: int) -> None:
""" Check 7-Zip bad exit codes (0 OK, 1 Warning) """
if exit_code not in SZIP_SUCCESS:
raise ValueError(f'Bad exit code: {exit_code}')
def szip_switches(in_switches: list[str]) -> list[str]:
""" Generate 7-Zip command line switches """
@ -46,77 +39,73 @@ def szip_switches(in_switches: list[str]) -> list[str]:
return [*set(common_switches + in_switches), '--']
def is_szip_supported(in_path: str, padding: int = 0, args: list | None = None, silent: bool = True) -> bool:
def is_szip_successful(exit_code: int) -> bool:
""" Check 7-Zip success exit codes """
if exit_code in SZIP_SUCCESS:
return True
return False
def is_szip_supported(in_path: str, args: list | None = None) -> bool:
""" Check if file is 7-Zip supported """
try:
if args is None:
args = []
szip_a: list[str] = [] if args is None else args
szip_c: list[str] = [szip_path(), 't', *szip_switches(in_switches=[*args]), in_path]
szip_c: list[str] = [szip_path(), 't', *szip_switches(in_switches=[*szip_a]), in_path]
szip_t: subprocess.CompletedProcess[bytes] = subprocess.run(szip_c, check=False)
szip_t: subprocess.CompletedProcess[bytes] = subprocess.run(szip_c, check=False, stdout=subprocess.DEVNULL)
szip_code_assert(exit_code=szip_t.returncode)
except Exception as error: # pylint: disable=broad-except
if not silent:
printer(message=f'Error: 7-Zip could not check support for file {in_path}: {error}!', padding=padding)
return False
return True
return is_szip_successful(exit_code=szip_t.returncode)
def szip_decompress(in_path: str, out_path: str, in_name: str = 'archive', padding: int = 0, args: list | None = None,
check: bool = False, silent: bool = False) -> bool:
""" Archive decompression via 7-Zip """
try:
if args is None:
args = []
szip_a: list[str] = [] if args is None else args
szip_c: list[str] = [szip_path(), 'x', *szip_switches(in_switches=[*args, f'-o{out_path}']), in_path]
szip_c: list[str] = [szip_path(), 'x', *szip_switches(in_switches=[*szip_a, f'-o{out_path}']), in_path]
szip_x: subprocess.CompletedProcess[bytes] = subprocess.run(szip_c, check=False)
szip_x: subprocess.CompletedProcess[bytes] = subprocess.run(szip_c, check=False, stdout=subprocess.DEVNULL)
if check:
szip_code_assert(exit_code=szip_x.returncode)
szip_s: bool = is_szip_successful(exit_code=szip_x.returncode) if check else True
if not (is_dir(in_path=out_path) and not is_empty_dir(in_path=out_path)):
raise OSError(f'Extraction directory is empty or missing: {out_path}')
except Exception as error: # pylint: disable=broad-except
if szip_s and is_dir(in_path=out_path) and not is_empty_dir(in_path=out_path):
if not silent:
printer(message=f'Error: 7-Zip could not extract {in_name} file {in_path}: {error}!', padding=padding)
printer(message=f'Successful {in_name} decompression via 7-Zip!', padding=padding)
return False
return True
if not silent:
printer(message=f'Successful {in_name} decompression via 7-Zip!', padding=padding)
return True
return False
def efi_compress_sizes(data: bytes | bytearray) -> tuple[int, int]:
""" Get EFI compression sizes """
def efi_header_info(in_object: str | bytes | bytearray) -> dict[str, int]:
""" Get EFI compression sizes from header """
size_compress: int = int.from_bytes(data[0x0:0x4], byteorder='little')
efi_data: bytes = file_to_bytes(in_object=in_object)
size_original: int = int.from_bytes(data[0x4:0x8], byteorder='little')
size_compressed: int = int.from_bytes(efi_data[0x0:0x4], byteorder='little')
return size_compress, size_original
size_decompressed: int = int.from_bytes(efi_data[0x4:0x8], byteorder='little')
return {'size_compressed': size_compressed, 'size_decompressed': size_decompressed}
def is_efi_compressed(data: bytes | bytearray, strict: bool = True) -> bool:
def is_efi_compressed(in_object: str | bytes | bytearray, strict: bool = True) -> bool:
""" Check if data is EFI compressed, controlling EOF padding """
size_comp, size_orig = efi_compress_sizes(data=data)
efi_data: bytes = file_to_bytes(in_object=in_object)
check_diff: bool = size_comp < size_orig
efi_sizes: dict[str, int] = efi_header_info(in_object=efi_data)
check_diff: bool = efi_sizes['size_compressed'] < efi_sizes['size_decompressed']
if strict:
check_size: bool = size_comp + 0x8 == len(data)
check_size: bool = efi_sizes['size_compressed'] + 0x8 == len(efi_data)
else:
check_size = size_comp + 0x8 <= len(data)
check_size = efi_sizes['size_compressed'] + 0x8 <= len(efi_data)
return check_diff and check_size
@ -125,22 +114,15 @@ def efi_decompress(in_path: str, out_path: str, padding: int = 0, silent: bool =
comp_type: str = '--uefi') -> bool:
""" EFI/Tiano Decompression via TianoCompress """
try:
subprocess.run([tiano_path(), '-d', in_path, '-o', out_path, '-q', comp_type],
check=True, stdout=subprocess.DEVNULL)
tiano_c: list[str] = [tiano_path(), '-d', in_path, '-o', out_path, '-q', comp_type]
with open(in_path, 'rb') as file:
_, size_orig = efi_compress_sizes(data=file.read())
tiano_x: subprocess.CompletedProcess[bytes] = subprocess.run(tiano_c, check=False, stdout=subprocess.DEVNULL)
if os.path.getsize(out_path) != size_orig:
raise OSError('EFI decompressed file & header size mismatch!')
except Exception as error: # pylint: disable=broad-except
if not silent:
printer(message=f'Error: TianoCompress could not extract file {in_path}: {error}!', padding=padding)
if tiano_x.returncode == 0 and is_file(in_path=out_path) and is_access(in_path=out_path):
if efi_header_info(in_object=in_path)['size_decompressed'] == path_size(in_path=out_path):
if not silent:
printer(message='Successful EFI decompression via TianoCompress!', padding=padding)
return False
return True
if not silent:
printer(message='Successful EFI decompression via TianoCompress!', padding=padding)
return True
return False

View file

@ -113,13 +113,19 @@ def path_stem(in_path: str) -> str:
return PurePath(in_path).stem
def path_size(in_path: str) -> int:
""" Get path size (bytes) """
return os.stat(in_path).st_size
def path_suffixes(in_path: str) -> list[str]:
""" Get list of path file extensions """
return PurePath(in_path).suffixes or ['']
def make_dirs(in_path: str, parents: bool = True, exist_ok: bool = False, delete: bool = False):
def make_dirs(in_path: str, parents: bool = True, exist_ok: bool = True, delete: bool = False):
""" Create folder(s), controlling parents, existence and prior deletion """
if delete:
@ -138,12 +144,28 @@ def delete_dirs(in_path: str) -> None:
def delete_file(in_path: str) -> None:
""" Delete file, if present """
if Path(in_path).is_file():
if is_file(in_path=in_path):
clear_readonly(in_path=in_path)
os.remove(in_path)
def rename_file(in_path: str, in_dest: str) -> None:
""" Rename file with path or name destination, if present """
if is_file(in_path=in_path):
clear_readonly(in_path=in_path)
if is_file(in_path=in_dest, allow_broken_links=True):
clear_readonly(in_path=in_dest)
out_path: str = in_dest
else:
out_path = os.path.join(path_parent(in_path=in_path), in_dest)
os.replace(in_path, out_path)
def copy_file(in_path: str, out_path: str, metadata: bool = False) -> None:
""" Copy file to path with or w/o metadata """

View file

@ -5,9 +5,11 @@
Copyright (C) 2022-2024 Plato Mavropoulos
"""
from biosutilities.common.texts import file_to_bytes
class BIOSUtility:
""" Base utility class for BIOSUtilities """
""" Base class for BIOSUtilities """
TITLE: str = 'BIOS Utility'
@ -16,6 +18,8 @@ class BIOSUtility:
self.extract_path: str = extract_path
self.padding: int = padding
self.input_buffer: bytes = file_to_bytes(in_object=self.input_object)
def check_format(self) -> bool:
""" Check if input object is of specific supported format """

View file

@ -19,7 +19,7 @@ from typing import Any, Final
from biosutilities.common.checksums import checksum_8_xor
from biosutilities.common.compression import is_szip_supported, szip_decompress
from biosutilities.common.paths import (delete_dirs, path_files, is_access, is_file, make_dirs,
from biosutilities.common.paths import (delete_dirs, delete_file, path_files, is_access, is_file, make_dirs,
path_name, path_parent, path_stem, safe_name)
from biosutilities.common.patterns import PAT_DELL_FTR, PAT_DELL_HDR, PAT_DELL_PKG
from biosutilities.common.structs import CHAR, ctypes_struct, UINT8, UINT16, UINT32, UINT64
@ -244,12 +244,10 @@ class DellPfsExtract(BIOSUtility):
def check_format(self) -> bool:
""" Check if input is Dell PFS/PKG image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
if self._is_pfs_pkg(input_object=input_buffer):
if self._is_pfs_pkg(input_object=self.input_buffer):
return True
if self._is_pfs_hdr(input_object=input_buffer) and self._is_pfs_ftr(input_object=input_buffer):
if self._is_pfs_hdr(input_object=self.input_buffer) and self._is_pfs_ftr(input_object=self.input_buffer):
return True
return False
@ -257,18 +255,16 @@ class DellPfsExtract(BIOSUtility):
def parse_format(self) -> bool:
""" Parse & Extract Dell PFS Update image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
make_dirs(in_path=self.extract_path)
make_dirs(in_path=self.extract_path, delete=True)
is_dell_pkg: bool = self._is_pfs_pkg(input_object=input_buffer)
is_dell_pkg: bool = self._is_pfs_pkg(input_object=self.input_buffer)
if is_dell_pkg:
pfs_results: dict[str, bytes] = self._thinos_pkg_extract(
input_object=input_buffer, extract_path=self.extract_path)
input_object=self.input_buffer, extract_path=self.extract_path)
else:
pfs_results = {path_stem(in_path=self.input_object) if isinstance(self.input_object, str) and is_file(
in_path=self.input_object) else 'Image': input_buffer}
in_path=self.input_object) else 'Image': self.input_buffer}
# Parse each Dell PFS image contained in the input file
for pfs_index, (pfs_name, pfs_buffer) in enumerate(pfs_results.items(), start=1):
@ -356,10 +352,10 @@ class DellPfsExtract(BIOSUtility):
with open(pkg_tar_path, 'wb') as pkg_payload:
pkg_payload.write(lzma.decompress(lzma_bin_dat))
if is_szip_supported(in_path=pkg_tar_path, padding=0, args=['-tTAR']):
if is_szip_supported(in_path=pkg_tar_path, args=['-tTAR']):
if szip_decompress(in_path=pkg_tar_path, out_path=working_path, in_name='TAR', padding=0,
args=['-tTAR'], check=True, silent=True):
os.remove(pkg_tar_path)
delete_file(in_path=pkg_tar_path)
else:
return pfs_results
else:
@ -423,7 +419,7 @@ class DellPfsExtract(BIOSUtility):
section_path: str = os.path.join(extract_path, safe_name(in_name=section_name))
# Create extraction subdirectory and delete old (if present, not in recursions)
make_dirs(in_path=section_path, delete=(not is_rec), parents=True, exist_ok=True)
make_dirs(in_path=section_path, delete=not is_rec)
# Store the compressed zlib stream start offset
compressed_start: int = zlib_start + 0xB

View file

@ -11,11 +11,10 @@ import os
import re
from biosutilities.common.compression import is_szip_supported, szip_decompress
from biosutilities.common.paths import make_dirs
from biosutilities.common.paths import delete_file, make_dirs
from biosutilities.common.patterns import PAT_FUJITSU_SFX
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class FujitsuSfxExtract(BIOSUtility):
@ -26,17 +25,13 @@ class FujitsuSfxExtract(BIOSUtility):
def check_format(self) -> bool:
""" Check if input is Fujitsu SFX image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
return bool(PAT_FUJITSU_SFX.search(input_buffer))
return bool(PAT_FUJITSU_SFX.search(self.input_buffer))
def parse_format(self) -> bool:
""" Parse & Extract Fujitsu SFX image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
# Microsoft CAB Header XOR 0xFF
match_cab: re.Match[bytes] | None = PAT_FUJITSU_SFX.search(input_buffer)
match_cab: re.Match[bytes] | None = PAT_FUJITSU_SFX.search(self.input_buffer)
if not match_cab:
return False
@ -47,7 +42,7 @@ class FujitsuSfxExtract(BIOSUtility):
cab_start: int = match_cab.start() + 0xA
# Get LE XOR-ed CAB size
cab_size: int = int.from_bytes(input_buffer[cab_start + 0x8:cab_start + 0xC], byteorder='little')
cab_size: int = int.from_bytes(self.input_buffer[cab_start + 0x8:cab_start + 0xC], byteorder='little')
# Create CAB size XOR value
xor_size: int = int.from_bytes(b'\xFF' * 0x4, byteorder='little')
@ -58,7 +53,7 @@ class FujitsuSfxExtract(BIOSUtility):
printer(message='Removing obfuscation...', padding=self.padding + 4)
# Get BE XOR-ed CAB data
cab_data: int = int.from_bytes(input_buffer[cab_start:cab_start + cab_size], byteorder='big')
cab_data: int = int.from_bytes(self.input_buffer[cab_start:cab_start + cab_size], byteorder='big')
# Create CAB data XOR value
xor_data: int = int.from_bytes(b'\xFF' * cab_size, byteorder='big')
@ -68,7 +63,7 @@ class FujitsuSfxExtract(BIOSUtility):
printer(message='Extracting archive...', padding=self.padding + 4)
make_dirs(in_path=self.extract_path, delete=True)
make_dirs(in_path=self.extract_path)
cab_path: str = os.path.join(self.extract_path, 'FjSfxBinay.cab')
@ -76,10 +71,10 @@ class FujitsuSfxExtract(BIOSUtility):
with open(cab_path, 'wb') as cab_file_object:
cab_file_object.write(raw_data)
if is_szip_supported(in_path=cab_path, padding=self.padding + 8, silent=False):
if is_szip_supported(in_path=cab_path):
if szip_decompress(in_path=cab_path, out_path=self.extract_path, in_name='FjSfxBinay CAB',
padding=self.padding + 8, check=True):
os.remove(cab_path)
delete_file(in_path=cab_path)
else:
return False
else:

View file

@ -10,9 +10,8 @@ Copyright (C) 2021-2024 Plato Mavropoulos
import os
from biosutilities.common.compression import efi_decompress, is_efi_compressed
from biosutilities.common.paths import make_dirs, is_file, path_name, path_suffixes
from biosutilities.common.paths import delete_file, make_dirs, is_file, path_name, path_suffixes
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class FujitsuUpcExtract(BIOSUtility):
@ -30,14 +29,14 @@ class FujitsuUpcExtract(BIOSUtility):
is_upc = True
if is_upc:
is_upc = is_efi_compressed(data=file_to_bytes(in_object=self.input_object))
is_upc = is_efi_compressed(in_object=self.input_object)
return is_upc
def parse_format(self) -> bool:
""" Parse & Extract Fujitsu UPC image """
make_dirs(in_path=self.extract_path, delete=True)
make_dirs(in_path=self.extract_path)
if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
input_name: str = path_name(in_path=self.input_object)
@ -52,13 +51,13 @@ class FujitsuUpcExtract(BIOSUtility):
input_path = os.path.join(self.extract_path, f'{input_name}.UPC')
with open(input_path, 'wb') as input_path_object:
input_path_object.write(file_to_bytes(in_object=self.input_object))
input_path_object.write(self.input_buffer)
output_path: str = os.path.join(self.extract_path, f'{input_name}.bin')
efi_status: bool = efi_decompress(in_path=input_path, out_path=output_path, padding=self.padding)
if input_path != self.input_object:
os.remove(input_path)
delete_file(in_path=input_path)
return efi_status

View file

@ -14,13 +14,12 @@ import re
from typing import Any, Final
from biosutilities.common.compression import is_szip_supported, szip_decompress
from biosutilities.common.paths import (extract_folder, is_access, is_file, path_files,
from biosutilities.common.paths import (delete_file, extract_folder, is_access, is_file, path_files,
make_dirs, path_name, safe_name)
from biosutilities.common.patterns import PAT_INSYDE_IFL, PAT_INSYDE_SFX
from biosutilities.common.structs import CHAR, ctypes_struct, UINT32
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class IflashHeader(ctypes.LittleEndianStructure):
@ -83,12 +82,10 @@ class InsydeIfdExtract(BIOSUtility):
def check_format(self) -> bool:
""" Check if input is Insyde iFlash/iFdPacker Update image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
if bool(self._insyde_iflash_detect(input_buffer=input_buffer)):
if bool(self._insyde_iflash_detect(input_buffer=self.input_buffer)):
return True
if bool(PAT_INSYDE_SFX.search(input_buffer)):
if bool(PAT_INSYDE_SFX.search(self.input_buffer)):
return True
return False
@ -96,14 +93,12 @@ class InsydeIfdExtract(BIOSUtility):
def parse_format(self) -> bool:
""" Parse & Extract Insyde iFlash/iFdPacker Update images """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
iflash_code: int = self._insyde_iflash_extract(input_buffer=input_buffer, extract_path=self.extract_path,
iflash_code: int = self._insyde_iflash_extract(input_buffer=self.input_buffer, extract_path=self.extract_path,
padding=self.padding)
ifdpack_path: str = os.path.join(self.extract_path, 'Insyde iFdPacker SFX')
ifdpack_code: int = self._insyde_packer_extract(input_buffer=input_buffer, extract_path=ifdpack_path,
ifdpack_code: int = self._insyde_packer_extract(input_buffer=self.input_buffer, extract_path=ifdpack_path,
padding=self.padding)
return (iflash_code and ifdpack_code) == 0
@ -142,7 +137,7 @@ class InsydeIfdExtract(BIOSUtility):
printer(message='Detected Insyde iFlash Update image!', padding=padding)
make_dirs(in_path=extract_path, delete=True)
make_dirs(in_path=extract_path)
exit_codes: list = []
@ -217,10 +212,10 @@ class InsydeIfdExtract(BIOSUtility):
with open(sfx_path, 'wb') as sfx_file_object:
sfx_file_object.write(sfx_buffer)
if is_szip_supported(in_path=sfx_path, padding=padding + 8, args=[f'-p{self.INS_SFX_PWD}'], silent=False):
if is_szip_supported(in_path=sfx_path, args=[f'-p{self.INS_SFX_PWD}']):
if szip_decompress(in_path=sfx_path, out_path=extract_path, in_name='Insyde iFdPacker > 7-Zip SFX',
padding=padding + 8, args=[f'-p{self.INS_SFX_PWD}'], check=True):
os.remove(sfx_path)
delete_file(in_path=sfx_path)
else:
return 125
else:

View file

@ -20,7 +20,7 @@ import pefile
from dissect.util.compression import lznt1
from biosutilities.common.compression import is_szip_supported, szip_decompress
from biosutilities.common.paths import is_access, is_file, path_files, make_dirs, path_stem, safe_name
from biosutilities.common.paths import delete_file, is_access, is_file, path_files, make_dirs, path_stem, safe_name
from biosutilities.common.executables import ms_pe_desc, ms_pe, is_ms_pe, ms_pe_info_show
from biosutilities.common.patterns import PAT_MICROSOFT_CAB
from biosutilities.common.system import printer
@ -64,7 +64,7 @@ class PanasonicBiosExtract(BIOSUtility):
ms_pe_info_show(pe_file=upd_pe_file, padding=self.padding + 4)
make_dirs(in_path=self.extract_path, delete=True)
make_dirs(in_path=self.extract_path)
upd_pe_path: str = self._panasonic_cab_extract(input_object=self.input_object,
extract_path=self.extract_path, padding=self.padding + 8)
@ -82,7 +82,7 @@ class PanasonicBiosExtract(BIOSUtility):
ms_pe_info_show(pe_file=upd_pe_file, padding=upd_padding + 4)
os.remove(upd_pe_path)
delete_file(in_path=upd_pe_path)
is_upd_extracted: bool = self._panasonic_res_extract(pe_file=upd_pe_file, extract_path=self.extract_path,
pe_name=upd_pe_name, padding=upd_padding + 8)
@ -121,12 +121,12 @@ class PanasonicBiosExtract(BIOSUtility):
with open(cab_path, 'wb') as cab_file_object:
cab_file_object.write(input_data[cab_bgn:cab_end])
if is_szip_supported(in_path=cab_path, padding=padding, silent=False):
if is_szip_supported(in_path=cab_path):
printer(message=f'Panasonic BIOS Package > PE > CAB {cab_tag}', padding=padding)
if szip_decompress(in_path=cab_path, out_path=extract_path, in_name='CAB',
padding=padding + 4, check=True):
os.remove(cab_path) # Successful extraction, delete CAB archive
delete_file(in_path=cab_path) # Successful extraction, delete CAB archive
for extracted_file_path in path_files(in_path=extract_path):
if is_file(in_path=extracted_file_path) and is_access(in_path=extracted_file_path):

View file

@ -23,7 +23,6 @@ from biosutilities.common.patterns import PAT_MICROSOFT_MZ, PAT_MICROSOFT_PE, PA
from biosutilities.common.structs import CHAR, ctypes_struct, UINT32
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class PhoenixTdkHeader(ctypes.LittleEndianStructure):
@ -106,25 +105,21 @@ class PhoenixTdkExtract(BIOSUtility):
def check_format(self) -> bool:
""" Check if input contains valid Phoenix TDK image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
return bool(self._get_phoenix_tdk(in_buffer=input_buffer)[1] is not None)
return bool(self._get_phoenix_tdk(in_buffer=self.input_buffer)[1] is not None)
def parse_format(self) -> bool:
""" Parse & Extract Phoenix Tools Development Kit (TDK) Packer """
exit_code: int = 0
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
make_dirs(in_path=self.extract_path, delete=True)
make_dirs(in_path=self.extract_path)
printer(message='Phoenix Tools Development Kit Packer', padding=self.padding)
base_off, pack_off = self._get_phoenix_tdk(in_buffer=input_buffer)
base_off, pack_off = self._get_phoenix_tdk(in_buffer=self.input_buffer)
# Parse TDK Header structure
tdk_hdr: Any = ctypes_struct(buffer=input_buffer, start_offset=pack_off, class_object=PhoenixTdkHeader)
tdk_hdr: Any = ctypes_struct(buffer=self.input_buffer, start_offset=pack_off, class_object=PhoenixTdkHeader)
# Print TDK Header structure info
printer(message='Phoenix TDK Header:\n', padding=self.padding + 4)
@ -143,8 +138,10 @@ class PhoenixTdkExtract(BIOSUtility):
# Parse and extract each TDK Header Entry
for entry_index in range(tdk_hdr.Count):
# Parse TDK Entry structure
tdk_mod: Any = ctypes_struct(buffer=input_buffer, start_offset=entries_off + entry_index * self.TDK_MOD_LEN,
class_object=PhoenixTdkEntry, param_list=[base_off])
tdk_mod: Any = ctypes_struct(
buffer=self.input_buffer, start_offset=entries_off + entry_index * self.TDK_MOD_LEN,
class_object=PhoenixTdkEntry, param_list=[base_off]
)
# Print TDK Entry structure info
printer(message=f'Phoenix TDK Entry ({entry_index + 1}/{tdk_hdr.Count}):\n', padding=self.padding + 8)
@ -155,13 +152,13 @@ class PhoenixTdkExtract(BIOSUtility):
mod_off: int = tdk_mod.get_offset()
# Check if TDK Entry raw data Offset is valid
if mod_off >= len(input_buffer):
if mod_off >= len(self.input_buffer):
printer(message='Error: Phoenix TDK Entry > Offset is out of bounds!\n', padding=self.padding + 12)
exit_code = 2
# Store TDK Entry raw data (relative to TDK Base, not TDK Header)
mod_data: bytes = input_buffer[mod_off:mod_off + tdk_mod.Size]
mod_data: bytes = self.input_buffer[mod_off:mod_off + tdk_mod.Size]
# Check if TDK Entry raw data is complete
if len(mod_data) != tdk_mod.Size:

View file

@ -16,12 +16,11 @@ from typing import Final
from pefile import PE
from biosutilities.common.compression import efi_decompress, is_efi_compressed
from biosutilities.common.paths import make_dirs, safe_name
from biosutilities.common.paths import delete_file, make_dirs, rename_file, safe_name
from biosutilities.common.executables import ms_pe
from biosutilities.common.patterns import PAT_MICROSOFT_MZ, PAT_PORTWELL_EFI
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class PortwellEfiExtract(BIOSUtility):
@ -40,17 +39,15 @@ class PortwellEfiExtract(BIOSUtility):
def check_format(self) -> bool:
""" Check if input is Portwell EFI executable """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
try:
pe_buffer: bytes = self._get_portwell_pe(in_buffer=input_buffer)[1]
pe_buffer: bytes = self._get_portwell_pe(in_buffer=self.input_buffer)[1]
except Exception as error: # pylint: disable=broad-except
logging.debug('Error: Could not check if input is Portwell EFI executable: %s', error)
return False
# EFI images start with PE Header MZ
if PAT_MICROSOFT_MZ.search(input_buffer[:0x2]):
if PAT_MICROSOFT_MZ.search(self.input_buffer[:0x2]):
# Portwell EFI files start with <UU>
if PAT_PORTWELL_EFI.search(pe_buffer[:0x4]):
return True
@ -63,13 +60,11 @@ class PortwellEfiExtract(BIOSUtility):
# Initialize EFI Payload file chunks
efi_files: list[bytes] = []
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
make_dirs(in_path=self.extract_path)
make_dirs(in_path=self.extract_path, delete=True)
pe_file, pe_data = self._get_portwell_pe(in_buffer=self.input_buffer)
pe_file, pe_data = self._get_portwell_pe(in_buffer=input_buffer)
efi_title: str = self._get_unpacker_tag(input_buffer=input_buffer, pe_file=pe_file)
efi_title: str = self._get_unpacker_tag(input_buffer=self.input_buffer, pe_file=pe_file)
printer(message=efi_title, padding=self.padding)
@ -155,13 +150,13 @@ class PortwellEfiExtract(BIOSUtility):
out_file.write(file_data)
# Attempt to detect EFI compression & decompress when applicable
if is_efi_compressed(data=file_data):
if is_efi_compressed(in_object=file_data):
# Store temporary compressed file name
comp_fname: str = file_path + '.temp'
file_path_temp: str = f'{file_path}.temp'
# Rename initial/compressed file
os.replace(file_path, comp_fname)
rename_file(in_path=file_path, in_dest=file_path_temp)
# Successful decompression, delete compressed file
if efi_decompress(in_path=comp_fname, out_path=file_path, padding=padding + 8):
os.remove(comp_fname)
if efi_decompress(in_path=file_path_temp, out_path=file_path, padding=padding + 8):
delete_file(in_path=file_path_temp)

View file

@ -11,11 +11,10 @@ import os
import subprocess
from biosutilities.common.externals import comextract_path
from biosutilities.common.paths import is_file, make_dirs, path_stem, safe_name
from biosutilities.common.paths import delete_file, is_file, make_dirs, path_stem
from biosutilities.common.patterns import PAT_TOSHIBA_COM
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class ToshibaComExtract(BIOSUtility):
@ -26,14 +25,12 @@ class ToshibaComExtract(BIOSUtility):
def check_format(self) -> bool:
""" Check if input is Toshiba BIOS COM image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
return bool(PAT_TOSHIBA_COM.search(input_buffer, 0, 0x100))
return bool(PAT_TOSHIBA_COM.search(self.input_buffer, 0, 0x100))
def parse_format(self) -> bool:
""" Parse & Extract Toshiba BIOS COM image """
make_dirs(in_path=self.extract_path, delete=True)
make_dirs(in_path=self.extract_path)
if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
input_path: str = self.input_object
@ -41,26 +38,19 @@ class ToshibaComExtract(BIOSUtility):
input_path = os.path.join(self.extract_path, 'toshiba_bios.com')
with open(input_path, 'wb') as input_buffer:
input_buffer.write(file_to_bytes(in_object=self.input_object))
input_buffer.write(self.input_buffer)
output_name: str = f'{safe_name(in_name=path_stem(in_path=input_path))}_extracted.bin'
output_path: str = os.path.join(self.extract_path, f'{path_stem(in_path=input_path)}_extracted.bin')
output_path: str = os.path.join(self.extract_path, output_name)
try:
subprocess.run([comextract_path(), input_path, output_path], check=True, stdout=subprocess.DEVNULL)
if not is_file(in_path=output_path):
raise FileNotFoundError('EXTRACTED_FILE_MISSING')
except Exception as error: # pylint: disable=broad-except
printer(message=f'Error: ToshibaComExtractor could not extract {input_path}: {error}!',
padding=self.padding)
return False
comextract_res: subprocess.CompletedProcess[bytes] = subprocess.run(
[comextract_path(), input_path, output_path], check=False, stdout=subprocess.DEVNULL)
if input_path != self.input_object:
os.remove(input_path)
delete_file(in_path=input_path)
printer(message='Successful extraction via ToshibaComExtractor!', padding=self.padding)
if comextract_res.returncode == 0 and is_file(in_path=output_path):
printer(message='Successful extraction via ToshibaComExtractor!', padding=self.padding)
return True
return True
return False

View file

@ -12,11 +12,10 @@ import os
from re import Match
from biosutilities.common.compression import is_szip_supported, szip_decompress
from biosutilities.common.paths import make_dirs
from biosutilities.common.paths import delete_file, make_dirs, path_name
from biosutilities.common.patterns import PAT_VAIO_CAB, PAT_VAIO_CFG, PAT_VAIO_CHK, PAT_VAIO_EXT
from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class VaioPackageExtract(BIOSUtility):
@ -27,23 +26,19 @@ class VaioPackageExtract(BIOSUtility):
def check_format(self) -> bool:
""" Check if input is VAIO Packaging Manager """
input_buffer: bytes = file_to_bytes(in_object=self.input_object)
return bool(PAT_VAIO_CFG.search(input_buffer))
return bool(PAT_VAIO_CFG.search(self.input_buffer))
def parse_format(self) -> bool:
""" Parse & Extract or Unlock VAIO Packaging Manager """
input_buffer: bytes = file_to_bytes(self.input_object)
input_name: str = path_name(self.input_object) if isinstance(self.input_object, str) else 'VAIO_Package'
input_name: str = os.path.basename(self.input_object) if isinstance(input_buffer, str) else 'VAIO_Package'
make_dirs(in_path=self.extract_path)
make_dirs(in_path=self.extract_path, delete=True)
if self._vaio_cabinet(name=input_name, buffer=input_buffer, extract_path=self.extract_path,
if self._vaio_cabinet(name=input_name, buffer=self.input_buffer, extract_path=self.extract_path,
padding=self.padding) == 0:
printer(message='Successfully Extracted!', padding=self.padding)
elif self._vaio_unlock(name=input_name, buffer=input_buffer, extract_path=self.extract_path,
elif self._vaio_unlock(name=input_name, buffer=self.input_buffer, extract_path=self.extract_path,
padding=self.padding) == 0:
printer(message='Successfully Unlocked!', padding=self.padding)
else:
@ -93,10 +88,10 @@ class VaioPackageExtract(BIOSUtility):
with open(cab_path, 'wb') as cab_file:
cab_file.write(raw_data)
if is_szip_supported(in_path=cab_path, padding=padding + 8, silent=False):
if is_szip_supported(in_path=cab_path):
if szip_decompress(in_path=cab_path, out_path=extract_path, in_name='VAIO CAB',
padding=padding + 8, check=True):
os.remove(cab_path)
delete_file(in_path=cab_path)
else:
return 3
else:

41
main.py
View file

@ -6,6 +6,8 @@ Copyright (C) 2018-2024 Plato Mavropoulos
"""
import os
import sys
import traceback
from argparse import ArgumentParser, Namespace
from typing import Any, Final
@ -89,8 +91,8 @@ class BIOSUtilities:
else:
self._output_path = runtime_root()
def _check_sys_py(self) -> None:
""" Check Python Version """
def _check_system_support(self) -> None:
""" Check Python Version and OS Platform """
sys_py: tuple = python_version()
@ -100,21 +102,33 @@ class BIOSUtilities:
raise RuntimeError(f'Python >= {min_py_str} required, not {sys_py_str}')
@staticmethod
def _check_sys_os() -> None:
""" Check OS Platform """
os_tag, is_win, is_lnx = system_platform()
if not (is_win or is_lnx):
raise OSError(f'Unsupported operating system: {os_tag}')
def run_main(self, padding: int = 0) -> bool:
""" Run main """
def _exit_main(self, exit_code: int = 0) -> None:
if not self.main_arguments.auto_exit:
input('\nPress any key to exit...')
self._check_sys_py()
sys.exit(exit_code)
self._check_sys_os()
def _show_exception_and_exit(self, exc_type, exc_value, tb) -> None:
if exc_type is KeyboardInterrupt:
print('\n')
else:
print(f'\nError: BIOSUtilities v{__version__} crashed:\n')
traceback.print_exception(exc_type, exc_value, tb)
self._exit_main(exit_code=1)
def run_main(self, padding: int = 0) -> None:
""" Run main flow """
sys.excepthook = self._show_exception_and_exit
self._check_system_support()
self._setup_input_files(padding=padding)
@ -131,7 +145,7 @@ class BIOSUtilities:
for input_file in self._input_files:
input_name: str = path_name(in_path=input_file, limit=True)
printer(message=f'{input_name}\n', padding=padding)
printer(message=f'{to_boxed(in_text=input_name)}\n', padding=padding)
for utility_class in utilities_classes:
extract_path: str = os.path.join(self._output_path, extract_folder(in_path=input_name))
@ -167,10 +181,7 @@ class BIOSUtilities:
printer(message=None, new_line=False)
if not self.main_arguments.auto_exit:
input('Press any key to exit...')
return exit_code == 0
self._exit_main(exit_code=exit_code)
if __name__ == '__main__':

View file

@ -27,7 +27,6 @@ classifiers = [
"License :: OSI Approved :: BSD License",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Operating System :: POSIX :: BSD",
"Operating System :: MacOS",
"Topic :: Security",
"Topic :: Scientific/Engineering :: Information Analysis"