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 24.10.23
New "package" flow, arguments now provided during utility call (README) 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": 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) * [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 ### Apple EFI PBZX Extractor

View file

@ -5,4 +5,4 @@
Copyright (C) 2018-2024 Plato Mavropoulos 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: def check_format(self) -> bool:
""" Check if input is AMI BIOS Guard """ """ 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=self.input_buffer))
return bool(self._get_ami_pfat(input_object=input_buffer))
def parse_format(self) -> bool: def parse_format(self) -> bool:
""" Process and store AMI BIOS Guard output file """ """ 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=self.input_buffer)
pfat_buffer: bytes = self._get_ami_pfat(input_object=input_buffer)
file_path: str = '' file_path: str = ''
@ -235,7 +231,7 @@ class AmiPfatExtract(BIOSUtility):
extract_name: str = path_name(in_path=self.extract_path).removesuffix(extract_suffix()) 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) 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.checksums import checksum_16
from biosutilities.common.compression import efi_decompress, is_efi_compressed 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.patterns import PAT_AMI_UCP, PAT_INTEL_ENGINE
from biosutilities.common.structs import CHAR, ctypes_struct, UINT8, UINT16, UINT32 from biosutilities.common.structs import CHAR, ctypes_struct, UINT8, UINT16, UINT32
from biosutilities.common.system import printer from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility 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.ami_pfat_extract import AmiPfatExtract
from biosutilities.insyde_ifd_extract import InsydeIfdExtract from biosutilities.insyde_ifd_extract import InsydeIfdExtract
@ -259,23 +259,19 @@ class AmiUcpExtract(BIOSUtility):
def check_format(self) -> bool: def check_format(self) -> bool:
""" Check if input is AMI UCP image """ """ Check if input is AMI UCP image """
buffer: bytes = file_to_bytes(in_object=self.input_object) return bool(self._get_ami_ucp()[0])
return bool(self._get_ami_ucp(input_object=buffer)[0])
def parse_format(self) -> bool: def parse_format(self) -> bool:
""" Parse & Extract AMI UCP structures """ """ 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 nal_dict: dict[str, tuple[str, str]] = {} # Initialize @NAL Dictionary per UCP
printer(message='Utility Configuration Program', padding=self.padding) 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 # 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 # Parse @UAF|@HPU Header Structure
uaf_hdr: Any = ctypes_struct(buffer=ucp_buffer, start_offset=0, class_object=UafHeader) 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) 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 # Parse @UAF|@HPU Module EFI Structure
uaf_mod: Any = ctypes_struct(buffer=fake, start_offset=0, class_object=UafModule) 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 # Print @UAF|@HPU Module EFI Info
uaf_mod.struct_print(filename=uaf_name, description=uaf_desc, padding=self.padding + 8) uaf_mod.struct_print(filename=uaf_name, description=uaf_desc, padding=self.padding + 8)
@ -316,23 +315,21 @@ class AmiUcpExtract(BIOSUtility):
else: else:
printer(message=f'Checksum of UCP Module {tag} is valid!', padding=padding) printer(message=f'Checksum of UCP Module {tag} is valid!', padding=padding)
@staticmethod def _get_ami_ucp(self) -> tuple[bytes, str]:
def _get_ami_ucp(input_object: str | bytes | bytearray) -> tuple[bytes, str]:
""" Get all input file AMI UCP patterns """ """ 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_len_max: int = 0x0 # Length of largest detected @UAF|@HPU
uaf_buf_bin: bytes = b'' # Buffer 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 uaf_buf_tag: str = '@UAF' # Tag of largest detected @UAF|@HPU
for uaf in PAT_AMI_UCP.finditer(buffer): for uaf in PAT_AMI_UCP.finditer(self.input_buffer):
uaf_len_cur: int = int.from_bytes(buffer[uaf.start() + 0x4:uaf.start() + 0x8], byteorder='little') 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: if uaf_len_cur > uaf_len_max:
uaf_len_max = uaf_len_cur 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') uaf_buf_tag = uaf.group(0)[:4].decode('utf-8', 'ignore')
@ -431,7 +428,7 @@ class AmiUcpExtract(BIOSUtility):
if uaf_tag in nal_dict: if uaf_tag in nal_dict:
uaf_npath: str = safe_path(base_path=extract_path, user_paths=nal_dict[uaf_tag][0]) 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) uaf_fname: str = safe_path(base_path=uaf_npath, user_paths=uaf_sname)
else: else:
@ -484,7 +481,7 @@ class AmiUcpExtract(BIOSUtility):
uaf_out.write(uaf_data_raw) uaf_out.write(uaf_data_raw)
# @UAF|@HPU Module EFI/Tiano Decompression # @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 # Decompressed @UAF|@HPU Module file path
dec_fname: str = uaf_fname.replace('.temp', uaf_fext) dec_fname: str = uaf_fname.replace('.temp', uaf_fext)
@ -492,7 +489,7 @@ class AmiUcpExtract(BIOSUtility):
with open(dec_fname, 'rb') as dec: with open(dec_fname, 'rb') as dec:
uaf_data_raw = dec.read() # Read back the @UAF|@HPU Module decompressed Raw data 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 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 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) # 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): 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.check_format():
if insyde_ifd_extract.parse_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)) 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(): if ami_pfat_extract.check_format():
ami_pfat_extract.parse_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 # Detect Intel Engine firmware image and show ME Analyzer advice
if uaf_tag.startswith('@ME') and PAT_INTEL_ENGINE.search(uaf_data_raw): 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(): if ami_ucp_extract.check_format():
ami_ucp_extract.parse_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 return nal_dict

View file

@ -8,7 +8,6 @@ Copyright (C) 2018-2024 Plato Mavropoulos
""" """
import ctypes import ctypes
import logging
import os import os
import struct import struct
import subprocess import subprocess
@ -19,12 +18,13 @@ from re import Match
from typing import Any, Final from typing import Any, Final
from biosutilities.common.externals import uefiextract_path, uefifind_path 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.patterns import PAT_INTEL_IBIOSI, PAT_APPLE_ROM_VER
from biosutilities.common.structs import CHAR, ctypes_struct, UINT8 from biosutilities.common.structs import CHAR, ctypes_struct, UINT8
from biosutilities.common.system import printer from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility 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): class IntelBiosId(ctypes.LittleEndianStructure):
@ -131,71 +131,70 @@ class AppleEfiIdentify(BIOSUtility):
def check_format(self) -> bool: def check_format(self) -> bool:
""" Check if input is Apple EFI image """ """ Check if input is Apple EFI image """
if isinstance(self.input_object, str) and is_file(in_path=self.input_object) and is_access( if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
in_path=self.input_object): if path_suffixes(in_path=self.input_object)[-1].lower() not in EFI_EXTENSIONS:
if path_suffixes(in_path=self.input_object)[-1].lower() not in ('.fd', '.scap', '.im4p'):
return False return False
input_path: str = self.input_object input_path: str = self.input_object
input_buffer: bytes = file_to_bytes(in_object=input_path) else:
elif isinstance(self.input_object, (bytes, bytearray)):
input_path = os.path.join(runtime_root(), 'APPLE_EFI_ID_INPUT_BUFFER_CHECK.tmp') 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: with open(input_path, 'wb') as check_out:
check_out.write(input_buffer) check_out.write(self.input_buffer)
else:
return False input_buffer: bytes = self.input_buffer
try:
if PAT_INTEL_IBIOSI.search(input_buffer): if PAT_INTEL_IBIOSI.search(input_buffer):
return True return True
_ = subprocess.run([uefifind_path(), input_path, 'body', 'list', self.PAT_UEFIFIND], uefifind_cmd: list[str] = [uefifind_path(), input_path, 'body', 'list', self.PAT_UEFIFIND]
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return True uefifind_res: subprocess.CompletedProcess[bytes] = subprocess.run(
except Exception as error: # pylint: disable=broad-except uefifind_cmd, check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
logging.debug('Error: Could not check if input is Apple EFI image: %s', error)
return False
finally:
if input_path != self.input_object: if input_path != self.input_object:
delete_file(in_path=input_path) delete_file(in_path=input_path)
if uefifind_res.returncode == 0:
return True
return False
def parse_format(self) -> bool: def parse_format(self) -> bool:
""" Parse & Identify (or Rename) Apple EFI image """ """ 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): if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
input_path: str = self.input_object input_path: str = self.input_object
else: 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: 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: if bios_id_match:
bios_id_res: str = f'0x{bios_id_match.start():X}' 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) class_object=IntelBiosId)
else: else:
# The $IBIOSI$ pattern is within EFI compressed modules so we need to use UEFIFind and UEFIExtract # 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', bios_id_res = subprocess.check_output([uefifind_path(), input_path, 'body', 'list',
self.PAT_UEFIFIND], text=True)[:36] self.PAT_UEFIFIND], text=True)[:36]
# UEFIExtract must create its output folder itself make_dirs(in_path=self.extract_path)
delete_dirs(in_path=self.extract_path)
_ = subprocess.run([uefiextract_path(), input_path, bios_id_res, '-o', self.extract_path, '-m', 'body'], uefiextract_dir: str = os.path.join(self.extract_path, 'uefiextract_temp')
# UEFIExtract must create its output folder itself
delete_dirs(in_path=uefiextract_dir)
_ = subprocess.run([uefiextract_path(), input_path, bios_id_res, '-o', uefiextract_dir, '-m', 'body'],
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
with open(os.path.join(self.extract_path, 'body.bin'), 'rb') as raw_body: with open(os.path.join(uefiextract_dir, 'body.bin'), 'rb') as raw_body:
body_buffer: bytes = raw_body.read() body_buffer: bytes = raw_body.read()
# Detect decompressed $IBIOSI$ pattern # Detect decompressed $IBIOSI$ pattern
@ -207,11 +206,7 @@ class AppleEfiIdentify(BIOSUtility):
bios_id_hdr = ctypes_struct(buffer=body_buffer, start_offset=bios_id_match.start(), bios_id_hdr = ctypes_struct(buffer=body_buffer, start_offset=bios_id_match.start(),
class_object=IntelBiosId) class_object=IntelBiosId)
delete_dirs(in_path=self.extract_path) # Successful UEFIExtract extraction, remove its output folder delete_dirs(in_path=uefiextract_dir) # 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)
return False
if not self.silent: if not self.silent:
printer(message=f'Detected Intel BIOS Info at {bios_id_res}\n', padding=self.padding) 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.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]}') 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: if input_path != self.input_object:
delete_file(in_path=input_path) 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.patterns import PAT_APPLE_IM4P, PAT_INTEL_FD
from biosutilities.common.system import printer from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class AppleEfiIm4pSplit(BIOSUtility): class AppleEfiIm4pSplit(BIOSUtility):
@ -33,9 +32,7 @@ class AppleEfiIm4pSplit(BIOSUtility):
if isinstance(self.input_object, str) and not self.input_object.lower().endswith('.im4p'): if isinstance(self.input_object, str) and not self.input_object.lower().endswith('.im4p'):
return False return False
input_buffer: bytes = file_to_bytes(in_object=self.input_object) if PAT_APPLE_IM4P.search(self.input_buffer) and PAT_INTEL_FD.search(self.input_buffer):
if PAT_APPLE_IM4P.search(input_buffer) and PAT_INTEL_FD.search(input_buffer):
return True return True
return False return False
@ -45,12 +42,10 @@ class AppleEfiIm4pSplit(BIOSUtility):
parse_success: bool = True parse_success: bool = True
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)
# Detect IM4P EFI pattern # 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: if not im4p_match:
return False 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. # However, _MEFIBIN is not required for splitting SPI images due to Intel Flash Descriptor Components Density.
# IM4P mefi payload start offset # 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 # 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') byteorder='big')
# Check if mefi is followed by _MEFIBIN # 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 # Actual multi EFI payloads start after _MEFIBIN
efi_data_bgn: int = mefi_data_bgn + 0x100 if mefibin_exist else mefi_data_bgn 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 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 # 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 # Parse Intel Flash Descriptor pattern matches
for ifd in PAT_INTEL_FD.finditer(input_buffer): for ifd in PAT_INTEL_FD.finditer(input_buffer):

View file

@ -15,12 +15,11 @@ import os
from typing import Any, Final from typing import Any, Final
from biosutilities.common.compression import is_szip_supported, szip_decompress 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.patterns import PAT_APPLE_PBZX
from biosutilities.common.structs import ctypes_struct, UINT32 from biosutilities.common.structs import ctypes_struct, UINT32
from biosutilities.common.system import printer from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class PbzxChunk(ctypes.BigEndianStructure): class PbzxChunk(ctypes.BigEndianStructure):
@ -54,16 +53,12 @@ class AppleEfiPbzxExtract(BIOSUtility):
def check_format(self) -> bool: def check_format(self) -> bool:
""" Check if input is Apple PBZX image """ """ Check if input is Apple PBZX image """
input_buffer: bytes = file_to_bytes(in_object=self.input_object) return bool(PAT_APPLE_PBZX.search(self.input_buffer, 0, 4))
return bool(PAT_APPLE_PBZX.search(input_buffer, 0, 4))
def parse_format(self) -> bool: def parse_format(self) -> bool:
""" Parse & Extract Apple PBZX image """ """ Parse & Extract Apple PBZX 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)
cpio_bin: bytes = b'' # Initialize PBZX > CPIO Buffer 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 chunk_off: int = 0xC # First PBZX Chunk starts at 0xC
while chunk_off < len(input_buffer): while chunk_off < len(self.input_buffer):
chunk_hdr: Any = ctypes_struct(buffer=input_buffer, start_offset=chunk_off, class_object=PbzxChunk) 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) 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 # 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_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: try:
# Attempt XZ decompression, if applicable to Chunk data # Attempt XZ decompression, if applicable to Chunk data
@ -117,10 +112,10 @@ class AppleEfiPbzxExtract(BIOSUtility):
cpio_object.write(cpio_bin) cpio_object.write(cpio_bin)
# Decompress PBZX > CPIO archive with 7-Zip # 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', if szip_decompress(in_path=cpio_path, out_path=self.extract_path, in_name='CPIO',
padding=self.padding, args=['-tCPIO']): 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: else:
return False return False
else: else:

View file

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

View file

@ -10,11 +10,10 @@ Copyright (C) 2018-2024 Plato Mavropoulos
import os import os
from biosutilities.common.compression import szip_decompress 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.patterns import PAT_AWARD_LZH
from biosutilities.common.system import printer from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class AwardBiosExtract(BIOSUtility): class AwardBiosExtract(BIOSUtility):
@ -25,18 +24,14 @@ class AwardBiosExtract(BIOSUtility):
def check_format(self) -> bool: def check_format(self) -> bool:
""" Check if input is Award BIOS image """ """ Check if input is Award BIOS image """
in_buffer: bytes = file_to_bytes(in_object=self.input_object) return bool(PAT_AWARD_LZH.search(self.input_buffer))
return bool(PAT_AWARD_LZH.search(in_buffer))
def parse_format(self) -> bool: def parse_format(self) -> bool:
""" Parse & Extract Award BIOS image """ """ 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(self.input_buffer):
for lzh_match in PAT_AWARD_LZH.finditer(input_buffer):
lzh_type: str = lzh_match.group(0).decode('utf-8') lzh_type: str = lzh_match.group(0).decode('utf-8')
lzh_text: str = f'LZH-{lzh_type.strip("-").upper()}' lzh_text: str = f'LZH-{lzh_type.strip("-").upper()}'
@ -44,11 +39,11 @@ class AwardBiosExtract(BIOSUtility):
lzh_bgn: int = lzh_match.start() lzh_bgn: int = lzh_match.start()
mod_bgn: int = lzh_bgn - 0x2 mod_bgn: int = lzh_bgn - 0x2
hdr_len: int = input_buffer[mod_bgn] hdr_len: int = self.input_buffer[mod_bgn]
mod_len: int = int.from_bytes(input_buffer[mod_bgn + 0x7:mod_bgn + 0xB], byteorder='little') 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_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: if len(mod_bin) != 0x2 + hdr_len + mod_len:
printer(message=f'Error: Skipped incomplete LZH stream at 0x{mod_bgn:X}!', 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): if is_file(in_path=mod_path):
clear_readonly(in_path=lzh_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( award_bios_extract: AwardBiosExtract = AwardBiosExtract(
input_object=mod_path, extract_path=extract_folder(mod_path), padding=self.padding + 8) 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 Copyright (C) 2022-2024 Plato Mavropoulos
""" """
import os
import subprocess import subprocess
from typing import Final from typing import Final
from biosutilities.common.externals import szip_path, tiano_path 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.system import printer
from biosutilities.common.texts import file_to_bytes
# 7-Zip switches to auto rename, ignore passwords, ignore prompts, ignore wildcards, # 7-Zip switches to auto rename, ignore passwords, ignore prompts, ignore wildcards,
# eliminate root duplication, set UTF-8 charset, suppress stdout, suppress stderr, # 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', SZIP_COMMON: Final[list[str]] = ['-aou', '-p', '-y', '-spd', '-spe', '-sccUTF-8',
'-bso0', '-bse0', '-bsp0', '-ba', '-bd', '-bb0'] '-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] 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]: def szip_switches(in_switches: list[str]) -> list[str]:
""" Generate 7-Zip command line switches """ """ 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), '--'] 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 if file is 7-Zip supported """ """ Check 7-Zip success exit codes """
try: if exit_code in SZIP_SUCCESS:
if args is None: return True
args = []
szip_c: list[str] = [szip_path(), 't', *szip_switches(in_switches=[*args]), in_path]
szip_t: subprocess.CompletedProcess[bytes] = subprocess.run(szip_c, check=False)
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 False
return True
def is_szip_supported(in_path: str, args: list | None = None) -> bool:
""" Check if file is 7-Zip supported """
szip_a: list[str] = [] if args is None else args
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, stdout=subprocess.DEVNULL)
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, 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: check: bool = False, silent: bool = False) -> bool:
""" Archive decompression via 7-Zip """ """ Archive decompression via 7-Zip """
try: szip_a: list[str] = [] if args is None else args
if args is None:
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_s: bool = is_szip_successful(exit_code=szip_x.returncode) if check else True
szip_code_assert(exit_code=szip_x.returncode)
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 not silent:
printer(message=f'Error: 7-Zip could not extract {in_name} file {in_path}: {error}!', padding=padding)
return False
if szip_s and is_dir(in_path=out_path) and not is_empty_dir(in_path=out_path):
if not silent: if not silent:
printer(message=f'Successful {in_name} decompression via 7-Zip!', padding=padding) printer(message=f'Successful {in_name} decompression via 7-Zip!', padding=padding)
return True return True
return False
def efi_compress_sizes(data: bytes | bytearray) -> tuple[int, int]:
""" Get EFI compression sizes """
size_compress: int = int.from_bytes(data[0x0:0x4], byteorder='little')
size_original: int = int.from_bytes(data[0x4:0x8], byteorder='little')
return size_compress, size_original
def is_efi_compressed(data: bytes | bytearray, strict: bool = True) -> bool: def efi_header_info(in_object: str | bytes | bytearray) -> dict[str, int]:
""" Get EFI compression sizes from header """
efi_data: bytes = file_to_bytes(in_object=in_object)
size_compressed: int = int.from_bytes(efi_data[0x0:0x4], byteorder='little')
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(in_object: str | bytes | bytearray, strict: bool = True) -> bool:
""" Check if data is EFI compressed, controlling EOF padding """ """ 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: if strict:
check_size: bool = size_comp + 0x8 == len(data) check_size: bool = efi_sizes['size_compressed'] + 0x8 == len(efi_data)
else: else:
check_size = size_comp + 0x8 <= len(data) check_size = efi_sizes['size_compressed'] + 0x8 <= len(efi_data)
return check_diff and check_size 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: comp_type: str = '--uefi') -> bool:
""" EFI/Tiano Decompression via TianoCompress """ """ EFI/Tiano Decompression via TianoCompress """
try: tiano_c: list[str] = [tiano_path(), '-d', in_path, '-o', out_path, '-q', comp_type]
subprocess.run([tiano_path(), '-d', in_path, '-o', out_path, '-q', comp_type],
check=True, stdout=subprocess.DEVNULL)
with open(in_path, 'rb') as file: tiano_x: subprocess.CompletedProcess[bytes] = subprocess.run(tiano_c, check=False, stdout=subprocess.DEVNULL)
_, size_orig = efi_compress_sizes(data=file.read())
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)
return False
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: if not silent:
printer(message='Successful EFI decompression via TianoCompress!', padding=padding) printer(message='Successful EFI decompression via TianoCompress!', padding=padding)
return True return True
return False

View file

@ -113,13 +113,19 @@ def path_stem(in_path: str) -> str:
return PurePath(in_path).stem 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]: def path_suffixes(in_path: str) -> list[str]:
""" Get list of path file extensions """ """ Get list of path file extensions """
return PurePath(in_path).suffixes or [''] 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 """ """ Create folder(s), controlling parents, existence and prior deletion """
if delete: if delete:
@ -138,12 +144,28 @@ def delete_dirs(in_path: str) -> None:
def delete_file(in_path: str) -> None: def delete_file(in_path: str) -> None:
""" Delete file, if present """ """ Delete file, if present """
if Path(in_path).is_file(): if is_file(in_path=in_path):
clear_readonly(in_path=in_path) clear_readonly(in_path=in_path)
os.remove(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: def copy_file(in_path: str, out_path: str, metadata: bool = False) -> None:
""" Copy file to path with or w/o metadata """ """ Copy file to path with or w/o metadata """

View file

@ -5,9 +5,11 @@
Copyright (C) 2022-2024 Plato Mavropoulos Copyright (C) 2022-2024 Plato Mavropoulos
""" """
from biosutilities.common.texts import file_to_bytes
class BIOSUtility: class BIOSUtility:
""" Base utility class for BIOSUtilities """ """ Base class for BIOSUtilities """
TITLE: str = 'BIOS Utility' TITLE: str = 'BIOS Utility'
@ -16,6 +18,8 @@ class BIOSUtility:
self.extract_path: str = extract_path self.extract_path: str = extract_path
self.padding: int = padding self.padding: int = padding
self.input_buffer: bytes = file_to_bytes(in_object=self.input_object)
def check_format(self) -> bool: def check_format(self) -> bool:
""" Check if input object is of specific supported format """ """ 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.checksums import checksum_8_xor
from biosutilities.common.compression import is_szip_supported, szip_decompress 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) 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.patterns import PAT_DELL_FTR, PAT_DELL_HDR, PAT_DELL_PKG
from biosutilities.common.structs import CHAR, ctypes_struct, UINT8, UINT16, UINT32, UINT64 from biosutilities.common.structs import CHAR, ctypes_struct, UINT8, UINT16, UINT32, UINT64
@ -244,12 +244,10 @@ class DellPfsExtract(BIOSUtility):
def check_format(self) -> bool: def check_format(self) -> bool:
""" Check if input is Dell PFS/PKG image """ """ 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=self.input_buffer):
if self._is_pfs_pkg(input_object=input_buffer):
return True 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 True
return False return False
@ -257,18 +255,16 @@ class DellPfsExtract(BIOSUtility):
def parse_format(self) -> bool: def parse_format(self) -> bool:
""" Parse & Extract Dell PFS Update image """ """ 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=self.input_buffer)
is_dell_pkg: bool = self._is_pfs_pkg(input_object=input_buffer)
if is_dell_pkg: if is_dell_pkg:
pfs_results: dict[str, bytes] = self._thinos_pkg_extract( 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: else:
pfs_results = {path_stem(in_path=self.input_object) if isinstance(self.input_object, str) and is_file( 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 # Parse each Dell PFS image contained in the input file
for pfs_index, (pfs_name, pfs_buffer) in enumerate(pfs_results.items(), start=1): 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: with open(pkg_tar_path, 'wb') as pkg_payload:
pkg_payload.write(lzma.decompress(lzma_bin_dat)) 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, if szip_decompress(in_path=pkg_tar_path, out_path=working_path, in_name='TAR', padding=0,
args=['-tTAR'], check=True, silent=True): args=['-tTAR'], check=True, silent=True):
os.remove(pkg_tar_path) delete_file(in_path=pkg_tar_path)
else: else:
return pfs_results return pfs_results
else: else:
@ -423,7 +419,7 @@ class DellPfsExtract(BIOSUtility):
section_path: str = os.path.join(extract_path, safe_name(in_name=section_name)) 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) # 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 # Store the compressed zlib stream start offset
compressed_start: int = zlib_start + 0xB compressed_start: int = zlib_start + 0xB

View file

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

View file

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

View file

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

View file

@ -20,7 +20,7 @@ import pefile
from dissect.util.compression import lznt1 from dissect.util.compression import lznt1
from biosutilities.common.compression import is_szip_supported, szip_decompress 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.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.patterns import PAT_MICROSOFT_CAB
from biosutilities.common.system import printer 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) 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, upd_pe_path: str = self._panasonic_cab_extract(input_object=self.input_object,
extract_path=self.extract_path, padding=self.padding + 8) 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) 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, 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) 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: with open(cab_path, 'wb') as cab_file_object:
cab_file_object.write(input_data[cab_bgn:cab_end]) 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) 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', if szip_decompress(in_path=cab_path, out_path=extract_path, in_name='CAB',
padding=padding + 4, check=True): 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): 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): 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.structs import CHAR, ctypes_struct, UINT32
from biosutilities.common.system import printer from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class PhoenixTdkHeader(ctypes.LittleEndianStructure): class PhoenixTdkHeader(ctypes.LittleEndianStructure):
@ -106,25 +105,21 @@ class PhoenixTdkExtract(BIOSUtility):
def check_format(self) -> bool: def check_format(self) -> bool:
""" Check if input contains valid Phoenix TDK image """ """ 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=self.input_buffer)[1] is not None)
return bool(self._get_phoenix_tdk(in_buffer=input_buffer)[1] is not None)
def parse_format(self) -> bool: def parse_format(self) -> bool:
""" Parse & Extract Phoenix Tools Development Kit (TDK) Packer """ """ Parse & Extract Phoenix Tools Development Kit (TDK) Packer """
exit_code: int = 0 exit_code: int = 0
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)
printer(message='Phoenix Tools Development Kit Packer', padding=self.padding) 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 # 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 # Print TDK Header structure info
printer(message='Phoenix TDK Header:\n', padding=self.padding + 4) printer(message='Phoenix TDK Header:\n', padding=self.padding + 4)
@ -143,8 +138,10 @@ class PhoenixTdkExtract(BIOSUtility):
# Parse and extract each TDK Header Entry # Parse and extract each TDK Header Entry
for entry_index in range(tdk_hdr.Count): for entry_index in range(tdk_hdr.Count):
# Parse TDK Entry structure # Parse TDK Entry structure
tdk_mod: Any = ctypes_struct(buffer=input_buffer, start_offset=entries_off + entry_index * self.TDK_MOD_LEN, tdk_mod: Any = ctypes_struct(
class_object=PhoenixTdkEntry, param_list=[base_off]) 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 # Print TDK Entry structure info
printer(message=f'Phoenix TDK Entry ({entry_index + 1}/{tdk_hdr.Count}):\n', padding=self.padding + 8) 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() mod_off: int = tdk_mod.get_offset()
# Check if TDK Entry raw data Offset is valid # 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) printer(message='Error: Phoenix TDK Entry > Offset is out of bounds!\n', padding=self.padding + 12)
exit_code = 2 exit_code = 2
# Store TDK Entry raw data (relative to TDK Base, not TDK Header) # 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 # Check if TDK Entry raw data is complete
if len(mod_data) != tdk_mod.Size: if len(mod_data) != tdk_mod.Size:

View file

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

View file

@ -11,11 +11,10 @@ import os
import subprocess import subprocess
from biosutilities.common.externals import comextract_path 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.patterns import PAT_TOSHIBA_COM
from biosutilities.common.system import printer from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class ToshibaComExtract(BIOSUtility): class ToshibaComExtract(BIOSUtility):
@ -26,14 +25,12 @@ class ToshibaComExtract(BIOSUtility):
def check_format(self) -> bool: def check_format(self) -> bool:
""" Check if input is Toshiba BIOS COM image """ """ 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(self.input_buffer, 0, 0x100))
return bool(PAT_TOSHIBA_COM.search(input_buffer, 0, 0x100))
def parse_format(self) -> bool: def parse_format(self) -> bool:
""" Parse & Extract Toshiba BIOS COM image """ """ 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): if isinstance(self.input_object, str) and is_file(in_path=self.input_object):
input_path: str = 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') input_path = os.path.join(self.extract_path, 'toshiba_bios.com')
with open(input_path, 'wb') as input_buffer: 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) comextract_res: subprocess.CompletedProcess[bytes] = subprocess.run(
[comextract_path(), input_path, output_path], check=False, stdout=subprocess.DEVNULL)
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
if input_path != self.input_object: if input_path != self.input_object:
os.remove(input_path) delete_file(in_path=input_path)
if comextract_res.returncode == 0 and is_file(in_path=output_path):
printer(message='Successful extraction via ToshibaComExtractor!', padding=self.padding) 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 re import Match
from biosutilities.common.compression import is_szip_supported, szip_decompress 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.patterns import PAT_VAIO_CAB, PAT_VAIO_CFG, PAT_VAIO_CHK, PAT_VAIO_EXT
from biosutilities.common.system import printer from biosutilities.common.system import printer
from biosutilities.common.templates import BIOSUtility from biosutilities.common.templates import BIOSUtility
from biosutilities.common.texts import file_to_bytes
class VaioPackageExtract(BIOSUtility): class VaioPackageExtract(BIOSUtility):
@ -27,23 +26,19 @@ class VaioPackageExtract(BIOSUtility):
def check_format(self) -> bool: def check_format(self) -> bool:
""" Check if input is VAIO Packaging Manager """ """ Check if input is VAIO Packaging Manager """
input_buffer: bytes = file_to_bytes(in_object=self.input_object) return bool(PAT_VAIO_CFG.search(self.input_buffer))
return bool(PAT_VAIO_CFG.search(input_buffer))
def parse_format(self) -> bool: def parse_format(self) -> bool:
""" Parse & Extract or Unlock VAIO Packaging Manager """ """ 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=self.input_buffer, extract_path=self.extract_path,
if self._vaio_cabinet(name=input_name, buffer=input_buffer, extract_path=self.extract_path,
padding=self.padding) == 0: padding=self.padding) == 0:
printer(message='Successfully Extracted!', padding=self.padding) 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: padding=self.padding) == 0:
printer(message='Successfully Unlocked!', padding=self.padding) printer(message='Successfully Unlocked!', padding=self.padding)
else: else:
@ -93,10 +88,10 @@ class VaioPackageExtract(BIOSUtility):
with open(cab_path, 'wb') as cab_file: with open(cab_path, 'wb') as cab_file:
cab_file.write(raw_data) 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', if szip_decompress(in_path=cab_path, out_path=extract_path, in_name='VAIO CAB',
padding=padding + 8, check=True): padding=padding + 8, check=True):
os.remove(cab_path) delete_file(in_path=cab_path)
else: else:
return 3 return 3
else: else:

41
main.py
View file

@ -6,6 +6,8 @@ Copyright (C) 2018-2024 Plato Mavropoulos
""" """
import os import os
import sys
import traceback
from argparse import ArgumentParser, Namespace from argparse import ArgumentParser, Namespace
from typing import Any, Final from typing import Any, Final
@ -89,8 +91,8 @@ class BIOSUtilities:
else: else:
self._output_path = runtime_root() self._output_path = runtime_root()
def _check_sys_py(self) -> None: def _check_system_support(self) -> None:
""" Check Python Version """ """ Check Python Version and OS Platform """
sys_py: tuple = python_version() sys_py: tuple = python_version()
@ -100,21 +102,33 @@ class BIOSUtilities:
raise RuntimeError(f'Python >= {min_py_str} required, not {sys_py_str}') 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() os_tag, is_win, is_lnx = system_platform()
if not (is_win or is_lnx): if not (is_win or is_lnx):
raise OSError(f'Unsupported operating system: {os_tag}') raise OSError(f'Unsupported operating system: {os_tag}')
def run_main(self, padding: int = 0) -> bool: def _exit_main(self, exit_code: int = 0) -> None:
""" Run main """ 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) self._setup_input_files(padding=padding)
@ -131,7 +145,7 @@ class BIOSUtilities:
for input_file in self._input_files: for input_file in self._input_files:
input_name: str = path_name(in_path=input_file, limit=True) 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: for utility_class in utilities_classes:
extract_path: str = os.path.join(self._output_path, extract_folder(in_path=input_name)) 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) printer(message=None, new_line=False)
if not self.main_arguments.auto_exit: self._exit_main(exit_code=exit_code)
input('Press any key to exit...')
return exit_code == 0
if __name__ == '__main__': if __name__ == '__main__':

View file

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