mirror of
https://github.com/platomav/BIOSUtilities.git
synced 2025-05-12 22:26:13 -04:00
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:
parent
d8e23f9ef3
commit
f895fc208c
24 changed files with 327 additions and 380 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -5,4 +5,4 @@
|
||||||
Copyright (C) 2018-2024 Plato Mavropoulos
|
Copyright (C) 2018-2024 Plato Mavropoulos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = '24.10.23'
|
__version__ = '24.10.29'
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,87 +131,82 @@ 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
|
|
||||||
|
|
||||||
try:
|
input_buffer: bytes = self.input_buffer
|
||||||
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)
|
|
||||||
|
|
||||||
|
if PAT_INTEL_IBIOSI.search(input_buffer):
|
||||||
return True
|
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
|
uefifind_cmd: list[str] = [uefifind_path(), input_path, 'body', 'list', self.PAT_UEFIFIND]
|
||||||
finally:
|
|
||||||
if input_path != self.input_object:
|
uefifind_res: subprocess.CompletedProcess[bytes] = subprocess.run(
|
||||||
delete_file(in_path=input_path)
|
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:
|
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',
|
|
||||||
self.PAT_UEFIFIND], text=True)[:36]
|
|
||||||
|
|
||||||
# UEFIExtract must create its output folder itself
|
bios_id_res = subprocess.check_output([uefifind_path(), input_path, 'body', 'list',
|
||||||
delete_dirs(in_path=self.extract_path)
|
self.PAT_UEFIFIND], text=True)[:36]
|
||||||
|
|
||||||
_ = subprocess.run([uefiextract_path(), input_path, bios_id_res, '-o', self.extract_path, '-m', 'body'],
|
make_dirs(in_path=self.extract_path)
|
||||||
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
||||||
|
|
||||||
with open(os.path.join(self.extract_path, 'body.bin'), 'rb') as raw_body:
|
uefiextract_dir: str = os.path.join(self.extract_path, 'uefiextract_temp')
|
||||||
body_buffer: bytes = raw_body.read()
|
|
||||||
|
|
||||||
# Detect decompressed $IBIOSI$ pattern
|
# UEFIExtract must create its output folder itself
|
||||||
bios_id_match = PAT_INTEL_IBIOSI.search(body_buffer)
|
delete_dirs(in_path=uefiextract_dir)
|
||||||
|
|
||||||
if not bios_id_match:
|
_ = subprocess.run([uefiextract_path(), input_path, bios_id_res, '-o', uefiextract_dir, '-m', 'body'],
|
||||||
raise RuntimeError('Failed to detect decompressed $IBIOSI$ pattern!')
|
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
bios_id_hdr = ctypes_struct(buffer=body_buffer, start_offset=bios_id_match.start(),
|
with open(os.path.join(uefiextract_dir, 'body.bin'), 'rb') as raw_body:
|
||||||
class_object=IntelBiosId)
|
body_buffer: bytes = raw_body.read()
|
||||||
|
|
||||||
delete_dirs(in_path=self.extract_path) # Successful UEFIExtract extraction, remove its output folder
|
# Detect decompressed $IBIOSI$ pattern
|
||||||
except Exception as error: # pylint: disable=broad-except
|
bios_id_match = PAT_INTEL_IBIOSI.search(body_buffer)
|
||||||
printer(message=f'Error: Failed to parse compressed $IBIOSI$ pattern: {error}!', padding=self.padding)
|
|
||||||
|
|
||||||
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:
|
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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 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 """
|
""" Check if file is 7-Zip supported """
|
||||||
|
|
||||||
try:
|
szip_a: list[str] = [] if args is None else args
|
||||||
if args is None:
|
|
||||||
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)
|
return is_szip_successful(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
|
|
||||||
|
|
||||||
|
|
||||||
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)):
|
if szip_s and 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:
|
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:
|
return False
|
||||||
printer(message=f'Successful {in_name} decompression via 7-Zip!', padding=padding)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def efi_compress_sizes(data: bytes | bytearray) -> tuple[int, int]:
|
def efi_header_info(in_object: str | bytes | bytearray) -> dict[str, int]:
|
||||||
""" Get EFI compression sizes """
|
""" 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 """
|
""" 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:
|
if tiano_x.returncode == 0 and is_file(in_path=out_path) and is_access(in_path=out_path):
|
||||||
raise OSError('EFI decompressed file & header size mismatch!')
|
if efi_header_info(in_object=in_path)['size_decompressed'] == path_size(in_path=out_path):
|
||||||
except Exception as error: # pylint: disable=broad-except
|
if not silent:
|
||||||
if not silent:
|
printer(message='Successful EFI decompression via TianoCompress!', padding=padding)
|
||||||
printer(message=f'Error: TianoCompress could not extract file {in_path}: {error}!', padding=padding)
|
|
||||||
|
|
||||||
return False
|
return True
|
||||||
|
|
||||||
if not silent:
|
return False
|
||||||
printer(message='Successful EFI decompression via TianoCompress!', padding=padding)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
|
@ -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 """
|
||||||
|
|
||||||
|
|
|
@ -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 """
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -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
41
main.py
|
@ -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__':
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue