diff --git a/.gitignore b/.gitignore index 5346b91..64b3e97 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ latest *.egg-info build dist +/.idea diff --git a/asahi_firmware/bluetooth.py b/asahi_firmware/bluetooth.py index 1c21ee1..5c77090 100644 --- a/asahi_firmware/bluetooth.py +++ b/asahi_firmware/bluetooth.py @@ -1,5 +1,9 @@ # SPDX-License-Identifier: MIT -import logging, os, os.path, re, sys +import logging +import os +import os.path +import re +import sys from collections import namedtuple, defaultdict from .core import FWFile diff --git a/asahi_firmware/core.py b/asahi_firmware/core.py index 544f283..80db61e 100644 --- a/asahi_firmware/core.py +++ b/asahi_firmware/core.py @@ -1,7 +1,10 @@ # SPDX-License-Identifier: MIT -import tarfile, io, logging +import io +import logging +import tarfile from hashlib import sha256 + class FWFile(object): def __init__(self, name, data): self.name = name @@ -19,6 +22,7 @@ def __eq__(self, other): def __hash__(self): return hash(self.sha) + class FWPackage(object): def __init__(self, target): self.path = target diff --git a/asahi_firmware/img4.py b/asahi_firmware/img4.py index f9a9ebd..431c174 100644 --- a/asahi_firmware/img4.py +++ b/asahi_firmware/img4.py @@ -2,6 +2,7 @@ from . import asn1 + def img4p_extract(data): decoder = asn1.Decoder() decoder.start(data) diff --git a/asahi_firmware/multitouch.py b/asahi_firmware/multitouch.py index e38bb11..4688b7d 100644 --- a/asahi_firmware/multitouch.py +++ b/asahi_firmware/multitouch.py @@ -1,15 +1,21 @@ # SPDX-License-Identifier: MIT +import logging +import os +import plistlib +import struct import xml.etree.ElementTree as ET -import plistlib, base64, struct, os, logging -from .img4 import img4p_extract + from .core import FWFile +from .img4 import img4p_extract log = logging.getLogger("asahi_firmware.multitouch") + def load_plist_xml(d): root = ET.fromstring(d.decode("ascii")) idmap = {} + def unmunge(el, idmap): if "ID" in el.attrib: idmap[el.attrib["ID"]] = el @@ -29,6 +35,7 @@ def unmunge(el, idmap): return plistlib.loads(ET.tostring(pl)) + def plist_to_bin(plist): iface_offset = None @@ -123,6 +130,7 @@ def add_padding(l): return hdr + blob + class MultitouchFWCollection(object): def __init__(self, source_path): self.fwfiles = [] diff --git a/asahi_firmware/update.py b/asahi_firmware/update.py index 8d4c480..b72897d 100644 --- a/asahi_firmware/update.py +++ b/asahi_firmware/update.py @@ -1,10 +1,13 @@ # SPDX-License-Identifier: MIT -import pathlib, tempfile, subprocess +import pathlib +import subprocess +import tempfile -from .core import FWPackage -from .wifi import WiFiFWCollection from .bluetooth import BluetoothFWCollection +from .core import FWPackage from .multitouch import MultitouchFWCollection +from .wifi import WiFiFWCollection + def update_firmware(source, dest, manifest): raw_fw = source.joinpath("all_firmware.tar.gz") @@ -30,6 +33,7 @@ def update_firmware(source, dest, manifest): pkg.save_manifest(manifest) + if __name__ == "__main__": import argparse import logging diff --git a/asahi_firmware/wifi.py b/asahi_firmware/wifi.py index 2cbe962..3abf53d 100644 --- a/asahi_firmware/wifi.py +++ b/asahi_firmware/wifi.py @@ -1,9 +1,14 @@ # SPDX-License-Identifier: MIT -import sys, os, os.path, pprint, statistics, logging +import logging +import os +import os.path +import sys + from .core import FWFile log = logging.getLogger("asahi_firmware.wifi") + class FWNode(object): def __init__(self, this=None, leaves=None): if leaves is None: @@ -25,6 +30,7 @@ def print(self, depth=0, tag=""): for k, v in self.leaves.items(): v.print(depth + 1, k) + class WiFiFWCollection(object): EXTMAP = { "trx": "bin", @@ -33,6 +39,7 @@ class WiFiFWCollection(object): "txcb": "txcap_blob", } DIMS = ["C", "s", "P", "M", "V", "m", "A"] + def __init__(self, source_path): self.root = FWNode() self.load(source_path) @@ -138,6 +145,7 @@ def process_nvram(self, data): def print(self): self.root.print() + if __name__ == "__main__": col = WiFiFWCollection(sys.argv[1]) if len(sys.argv) > 2: diff --git a/src/diskutil.py b/src/diskutil.py index ba4fa9c..8f6ae54 100644 --- a/src/diskutil.py +++ b/src/diskutil.py @@ -1,7 +1,11 @@ # SPDX-License-Identifier: MIT -import plistlib, subprocess, sys, logging +import logging +import plistlib +import subprocess +import sys from dataclasses import dataclass + @dataclass class Partition: name: str @@ -16,8 +20,10 @@ class Partition: container: object = None os: object = None + class DiskUtil: FREE_THRESHOLD = 16 * 1024 * 1024 + def __init__(self): self.verbose = "-v" in sys.argv diff --git a/src/main.py b/src/main.py index 61f4f6e..ca9e143 100644 --- a/src/main.py +++ b/src/main.py @@ -1,9 +1,20 @@ #!/usr/bin/python3 # SPDX-License-Identifier: MIT -import os, os.path, shlex, subprocess, sys, time, termios, json, getpass +import getpass +import json +import os.path +import shlex +import subprocess +import termios +import time from dataclasses import dataclass -import system, osenum, stub, diskutil, osinstall, asahi_firmware +import asahi_firmware +import diskutil +import osenum +import osinstall +import stub +import system from util import * PART_ALIGN = psize("1MiB") @@ -21,6 +32,7 @@ MIN_MACOS_VERSION = "12.3" MIN_MACOS_VERSION_EXPERT = "12.1" + @dataclass class IPSW: version: str @@ -31,11 +43,13 @@ class IPSW: expert_only: bool url: str + @dataclass class Device: min_ver: str expert_only: bool + CHIP_MIN_VER = { 0x8103: "11.0", # T8103, M1 0x6000: "12.0", # T6000, M1 Pro @@ -85,6 +99,7 @@ class Device: "https://updates.cdn-apple.com/2022SpringFCS/fullrestores/071-08757/74A4F2A1-C747-43F9-A22A-C0AD5FB4ECB6/UniversalMac_12.3_21E230_Restore.ipsw"), ] + class InstallerMain: def __init__(self): self.data = json.load(open("installer_data.json")) @@ -828,6 +843,7 @@ def main_loop(self): elif act == "q": return False + if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', diff --git a/src/osenum.py b/src/osenum.py index 6d729fc..16afdcc 100644 --- a/src/osenum.py +++ b/src/osenum.py @@ -1,5 +1,7 @@ # SPDX-License-Identifier: MIT -import os, os.path, plistlib, subprocess, logging +import os.path +import plistlib +import subprocess from dataclasses import dataclass from util import * @@ -7,6 +9,7 @@ UUID_SROS = "3D3287DE-280D-4619-AAAB-D97469CA9C71" UUID_FROS = "C8858560-55AC-400F-BBB9-C9220A8DAC0D" + @dataclass class OSInfo: partition: object @@ -48,6 +51,7 @@ def __str__(self): else: return f"[{lbl}] {col(BRIGHT, RED)}incomplete install{col()} (macOS {self.version} stub) [{self.sys_volume}, {self.vgid}]" + class OSEnum: def __init__(self, sysinfo, dutil, sysdsk): self.sysinfo = sysinfo diff --git a/src/osinstall.py b/src/osinstall.py index bed60b8..abbfd12 100644 --- a/src/osinstall.py +++ b/src/osinstall.py @@ -1,10 +1,14 @@ # SPDX-License-Identifier: MIT -import os, shutil, sys, stat, subprocess, urlcache, zipfile, logging +import subprocess +import urlcache +import zipfile from util import * + class OSInstaller(PackageInstaller): PART_ALIGNMENT = 1024 * 1024 + def __init__(self, dutil, data, template): super().__init__() self.dutil = dutil @@ -18,12 +22,15 @@ def __init__(self, dutil, data, template): @property def default_os_name(self): return self.template["default_os_name"] + @property def min_size(self): return sum(self.align(psize(part["size"])) for part in self.template["partitions"]) + @property def expandable(self): return any(part.get("expand", False) for part in self.template["partitions"]) + @property def needs_firmware(self): return any(p.get("copy_firmware", False) for p in self.template["partitions"]) diff --git a/src/stub.py b/src/stub.py index 9d14a95..c40a6cc 100644 --- a/src/stub.py +++ b/src/stub.py @@ -1,11 +1,17 @@ # SPDX-License-Identifier: MIT -import os, os.path, plistlib, shutil, sys, stat, subprocess, urlcache, zipfile, logging, json -import osenum -from asahi_firmware.wifi import WiFiFWCollection +import json +import os.path +import plistlib +import subprocess +import urlcache +import zipfile + from asahi_firmware.bluetooth import BluetoothFWCollection from asahi_firmware.multitouch import MultitouchFWCollection +from asahi_firmware.wifi import WiFiFWCollection from util import * + class StubInstaller(PackageInstaller): def __init__(self, sysinfo, dutil, osinfo, ipsw_info): super().__init__() diff --git a/src/system.py b/src/system.py index 295705f..8d284e4 100644 --- a/src/system.py +++ b/src/system.py @@ -1,8 +1,11 @@ # SPDX-License-Identifier: MIT -import base64, plistlib, struct, subprocess, logging +import plistlib +import struct +import subprocess from util import * + class SystemInfo: def __init__(self): self.fetch() diff --git a/src/urlcache.py b/src/urlcache.py index b2cf404..1f1dfa0 100644 --- a/src/urlcache.py +++ b/src/urlcache.py @@ -1,15 +1,18 @@ # SPDX-License-Identifier: MIT -import os, sys, os.path, time, logging +import os.path +import time from dataclasses import dataclass - from urllib import request + from util import * + @dataclass class CacheBlock: idx: int data: bytes + class URLCache: CACHESIZE = 128 BLOCKSIZE = 1 * 1024 * 1024 @@ -137,6 +140,7 @@ def flush_progress(self): else: return False + if __name__ == "__main__": import sys, zipfile diff --git a/src/util.py b/src/util.py index 621f755..5f9c60c 100644 --- a/src/util.py +++ b/src/util.py @@ -1,5 +1,10 @@ # SPDX-License-Identifier: MIT -import re, logging, sys, os, stat, shutil +import logging +import os +import re +import stat +import sys + def ssize(v): suffixes = ["B", "KB", "MB", "GB", "TB"] @@ -11,6 +16,7 @@ def ssize(v): return f"{v:.2f} {i}" v /= 1000 + def psize(v, align=None): v = v.upper().replace(" ", "") base = 1000 @@ -31,6 +37,7 @@ def psize(v, align=None): val = align_up(val, align) return val + def split_ver(s): parts = re.split(r"[-,. ]", s) parts2 = [] @@ -44,11 +51,14 @@ def split_ver(s): parts2[-2] = 99 return tuple(parts2) + def align_up(v, a=16384): return (v + a - 1) & ~(a - 1) + align = align_up + def align_down(v, a=16384): return v & ~(a - 1) @@ -66,10 +76,12 @@ def align_down(v, a=16384): NORMAL = 22 RESET_ALL = 0 + def col(*color): color = ";".join(map(str, color)) return f"\033[{color}m" + def p_style(*args, color=[], **kwargs): if isinstance(color, int): color = [color] @@ -79,36 +91,47 @@ def p_style(*args, color=[], **kwargs): text += col() logging.info(f"MSG: {text}") + def p_plain(*args): p_style(*args) + def p_info(*args): p_style(*args, color=(BRIGHT, BLUE)) + def p_progress(*args): p_style(*args, color=(BRIGHT, MAGENTA)) + def p_message(*args): p_style(*args, color=BRIGHT) + def p_error(*args): p_style(*args, color=(BRIGHT, RED)) + def p_warning(*args): p_style(*args, color=(BRIGHT, YELLOW)) + def p_question(*args): p_style(*args, color=(BRIGHT, CYAN)) + def p_success(*args): p_style(*args, color=(BRIGHT, GREEN)) + def p_prompt(*args): p_style(*args, color=(BRIGHT, CYAN)) + def p_choice(*args): p_style(*args) + def input_prompt(*args): while True: p_style(f"{col(BRIGHT, WHITE)}ยป{col(BRIGHT, CYAN)}", *args, end="") @@ -120,6 +143,7 @@ def input_prompt(*args): logging.info(f"INPUT: {val!r}") return val + class PackageInstaller: def __init__(self): self.verbose = "-v" in sys.argv