#!/usr/bin/env python3 -B # coding=utf-8 """ Copyright (C) 2018-2024 Plato Mavropoulos """ import os import sys import traceback from argparse import ArgumentParser, Namespace from typing import Any, Final from biosutilities import __version__ from biosutilities.ami_pfat_extract import AmiPfatExtract from biosutilities.ami_ucp_extract import AmiUcpExtract from biosutilities.apple_efi_id import AppleEfiIdentify from biosutilities.apple_efi_im4p import AppleEfiIm4pSplit from biosutilities.apple_efi_pbzx import AppleEfiPbzxExtract from biosutilities.apple_efi_pkg import AppleEfiPkgExtract from biosutilities.award_bios_extract import AwardBiosExtract from biosutilities.dell_pfs_extract import DellPfsExtract from biosutilities.fujitsu_sfx_extract import FujitsuSfxExtract from biosutilities.fujitsu_upc_extract import FujitsuUpcExtract from biosutilities.insyde_ifd_extract import InsydeIfdExtract from biosutilities.panasonic_bios_extract import PanasonicBiosExtract from biosutilities.phoenix_tdk_extract import PhoenixTdkExtract from biosutilities.portwell_efi_extract import PortwellEfiExtract from biosutilities.toshiba_com_extract import ToshibaComExtract from biosutilities.vaio_package_extract import VaioPackageExtract from biosutilities.common.paths import (delete_dirs, extract_folder, is_dir_read, is_empty_dir, is_file_read, path_files, path_name, path_parent, real_path, runtime_root) from biosutilities.common.system import python_version, printer, system_platform from biosutilities.common.texts import remove_quotes, to_boxed, to_ordinal class BIOSUtilities: """ Main BIOSUtilities class """ MAX_FAT32_ITEMS: Final[int] = 65535 MIN_PYTHON_VER: Final[tuple[int, int]] = (3, 10) def __init__(self) -> None: main_argparser: ArgumentParser = ArgumentParser(allow_abbrev=False) main_argparser.add_argument('paths', nargs='*') main_argparser.add_argument('-e', '--auto-exit', help='do not pause on exit', action='store_true') main_argparser.add_argument('-o', '--output-dir', help='extraction directory') self.main_arguments: Namespace = main_argparser.parse_args() self._input_files: list[str] = [] self._output_path: str = '' def _setup_input_files(self, padding: int = 0) -> None: self._input_files = [] input_paths: list[str] = self.main_arguments.paths if not input_paths: input_paths = [remove_quotes(in_text=input(f'\n{" " * padding}Enter input file or directory path: '))] for input_path in [input_path for input_path in input_paths if input_path]: input_path_real: str = real_path(in_path=input_path) if is_dir_read(in_path=input_path_real): for input_file in path_files(in_path=input_path_real): if is_file_read(in_path=input_file): self._input_files.append(input_file) elif is_file_read(in_path=input_path_real): self._input_files.append(input_path_real) def _setup_output_dir(self, padding: int = 0) -> None: self._output_path = '' output_path: str = self.main_arguments.output_dir if not output_path: output_path = remove_quotes(in_text=input(f'\n{" " * padding}Enter output directory path: ')) if not output_path and self._input_files: output_path = str(path_parent(in_path=self._input_files[0])) if output_path and is_dir_read(in_path=output_path): self._output_path = output_path else: self._output_path = runtime_root() def _check_system_support(self) -> None: """ Check Python Version and OS Platform """ sys_py: tuple = python_version() if sys_py < self.MIN_PYTHON_VER: min_py_str: str = '.'.join(map(str, self.MIN_PYTHON_VER)) sys_py_str: str = '.'.join(map(str, sys_py[:2])) raise RuntimeError(f'Python >= {min_py_str} required, not {sys_py_str}') os_tag, is_win, is_lnx = system_platform() if not (is_win or is_lnx): raise OSError(f'Unsupported operating system: {os_tag}') def _exit_main(self, exit_code: int = 0) -> None: if not self.main_arguments.auto_exit: input('\nPress any key to exit...') sys.exit(exit_code) 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_output_dir(padding=padding) exit_code: int = len(self._input_files) utilities_classes: list[Any] = [ AppleEfiPkgExtract, AmiUcpExtract, AmiPfatExtract, InsydeIfdExtract, DellPfsExtract, PhoenixTdkExtract, PanasonicBiosExtract, VaioPackageExtract, PortwellEfiExtract, ToshibaComExtract, FujitsuSfxExtract, FujitsuUpcExtract, AwardBiosExtract, AppleEfiPbzxExtract, AppleEfiIm4pSplit, AppleEfiIdentify ] for input_file in self._input_files: input_name: str = path_name(in_path=input_file, limit=True) printer(message=f'{to_boxed(in_text=input_name)}\n', padding=padding) for utility_class in utilities_classes: extract_path: str = os.path.join(self._output_path, extract_folder(in_path=input_name)) if is_dir_read(in_path=extract_path): for suffix in range(2, self.MAX_FAT32_ITEMS): renamed_path: str = f'{os.path.normpath(extract_path)}_{to_ordinal(in_number=suffix)}' if not is_dir_read(in_path=renamed_path): extract_path = renamed_path break utility: Any = utility_class(input_object=input_file, extract_path=extract_path, padding=padding + 8) if not utility.check_format(): continue printer(message=to_boxed(in_text=f'{utility.TITLE} v{__version__}'), new_line=False, padding=padding + 4) is_parsed_format: bool = utility.parse_format() is_empty_output: bool = is_empty_dir(in_path=extract_path) if is_empty_output: delete_dirs(in_path=extract_path) if is_parsed_format and not is_empty_output: exit_code -= 1 break printer(message=None, new_line=False) self._exit_main(exit_code=exit_code) if __name__ == '__main__': BIOSUtilities().run_main()