diff --git a/Utilities/Clover Configurator.zip b/Utilities/Clover Configurator.zip
deleted file mode 100644
index ad6f437..0000000
Binary files a/Utilities/Clover Configurator.zip and /dev/null differ
diff --git a/Utilities/MountEFI/Mount EFI Automator Quick Action.zip b/Utilities/MountEFI/Mount EFI Automator Quick Action.zip
new file mode 100644
index 0000000..6513428
Binary files /dev/null and b/Utilities/MountEFI/Mount EFI Automator Quick Action.zip differ
diff --git a/Utilities/MountEFI/MountEFI.command b/Utilities/MountEFI/MountEFI.command
new file mode 100755
index 0000000..1bdaa97
--- /dev/null
+++ b/Utilities/MountEFI/MountEFI.command
@@ -0,0 +1,345 @@
+#!/usr/bin/env python
+# 0.0.0
+from Scripts import *
+import os, tempfile, datetime, shutil, time, plistlib, json, sys, argparse
+
+class MountEFI:
+ def __init__(self, **kwargs):
+ self.r = run.Run()
+ self.d = disk.Disk()
+ self.dl = downloader.Downloader()
+ self.u = utils.Utils("MountEFI")
+ self.re = reveal.Reveal()
+ # Get the tools we need
+ self.script_folder = "Scripts"
+ self.update_url = "https://raw.githubusercontent.com/corpnewt/MountEFIv2/master/MountEFI.command"
+
+ self.settings_file = kwargs.get("settings", None)
+ cwd = os.getcwd()
+ os.chdir(os.path.dirname(os.path.realpath(__file__)))
+ if self.settings_file and os.path.exists(self.settings_file):
+ self.settings = json.load(open(self.settings_file))
+ else:
+ self.settings = {
+ # Default settings here
+ "default_disk" : None,
+ "after_mount" : None,
+ "full_layout" : False,
+ "skip_countdown" : False,
+ }
+ os.chdir(cwd)
+ self.full = self.settings.get("full_layout", False)
+
+ def check_update(self):
+ # Checks against https://raw.githubusercontent.com/corpnewt/MountEFIv2/master/MountEFI.command to see if we need to update
+ self.u.head("Checking for Updates")
+ print(" ")
+ with open(os.path.realpath(__file__), "r") as f:
+ # Our version should always be the second line
+ version = get_version(f.read())
+ print(version)
+ try:
+ new_text = _get_string(url)
+ new_version = get_version(new_text)
+ except:
+ # Not valid json data
+ print("Error checking for updates (network issue)")
+ return
+
+ if version == new_version:
+ # The same - return
+ print("v{} is already current.".format(version))
+ return
+ # Split the version number
+ try:
+ v = version.split(".")
+ cv = new_version.split(".")
+ except:
+ # not formatted right - bail
+ print("Error checking for updates (version string malformed)")
+ return
+
+ if not need_update(cv, v):
+ print("v{} is already current.".format(version))
+ return
+
+ # Update
+ with open(os.path.realpath(__file__), "w") as f:
+ f.write(new_text)
+
+ # chmod +x, then restart
+ run_command(["chmod", "+x", __file__])
+ os.execv(__file__, sys.argv)
+
+ def flush_settings(self):
+ if self.settings_file:
+ cwd = os.getcwd()
+ os.chdir(os.path.dirname(os.path.realpath(__file__)))
+ json.dump(self.settings, open(self.settings_file, "w"))
+ os.chdir(cwd)
+
+ def after_mount(self):
+ self.u.resize(80, 24)
+ self.u.head("After Mount Action")
+ print(" ")
+ print("1. Return to Menu")
+ print("2. Quit")
+ print("3. Open EFI and Return to Menu")
+ print("4. Open EFI and Quit")
+ if not self.settings.get("skip_countdown", False):
+ print("5. Skip After-Mount Countdown")
+ else:
+ print("5. Use After-Mount Countdown")
+ print(" ")
+ print("M. Main Menu")
+ print("Q. Quit")
+ print(" ")
+ menu = self.u.grab("Please pick an option: ")
+ if not len(menu):
+ self.after_mount()
+ return
+ menu = menu.lower()
+ if menu in ["1","2","3","4"]:
+ self.settings["after_mount"] = [
+ "Return to Menu",
+ "Quit",
+ "Reveal and Return to Menu",
+ "Reveal and Quit"
+ ][int(menu)-1]
+ self.flush_settings()
+ return
+ elif menu == "5":
+ cd = self.settings.get("skip_countdown", False)
+ cd ^= True
+ self.settings["skip_countdown"] = cd
+ self.flush_settings()
+ self.after_mount()
+ return
+ elif menu == "m":
+ return
+ elif menu == "q":
+ self.u.custom_quit()
+ self.after_mount()
+
+ def default_disk(self):
+ self.d.update()
+ clover = bdmesg.get_bootloader_uuid()
+ print(clover)
+ self.u.resize(80, 24)
+ self.u.head("Select Default Disk")
+ print(" ")
+ print("1. None")
+ print("2. Boot Disk")
+ if clover:
+ print("3. Booted EFI (Clover/OC)")
+ print(" ")
+ print("M. Main Menu")
+ print("Q. Quit")
+ print(" ")
+ menu = self.u.grab("Please pick a default disk: ")
+ if not len(menu):
+ self.default_disk()
+ menu = menu.lower()
+ if menu in ["1","2"]:
+ self.settings["default_disk"] = [None, "boot"][int(menu)-1]
+ self.flush_settings()
+ return
+ elif menu == "3" and clover:
+ self.settings["default_disk"] = "clover"
+ self.flush_settings()
+ return
+ elif menu == "m":
+ return
+ elif menu == "q":
+ self.u.custom_quit()
+ self.default_disk()
+
+ def get_efi(self):
+ self.d.update()
+ clover = bdmesg.get_bootloader_uuid()
+ i = 0
+ disk_string = ""
+ if not self.full:
+ clover_disk = self.d.get_parent(clover)
+ mounts = self.d.get_mounted_volume_dicts()
+ for d in mounts:
+ i += 1
+ disk_string += "{}. {} ({})".format(i, d["name"], d["identifier"])
+ if self.d.get_parent(d["identifier"]) == clover_disk:
+ # if d["disk_uuid"] == clover:
+ disk_string += " *"
+ disk_string += "\n"
+ else:
+ mounts = self.d.get_disks_and_partitions_dict()
+ disks = mounts.keys()
+ for d in disks:
+ i += 1
+ disk_string+= "{}. {}:\n".format(i, d)
+ parts = mounts[d]["partitions"]
+ part_list = []
+ for p in parts:
+ p_text = " - {} ({})".format(p["name"], p["identifier"])
+ if p["disk_uuid"] == clover:
+ # Got Clover
+ p_text += " *"
+ part_list.append(p_text)
+ if len(part_list):
+ disk_string += "\n".join(part_list) + "\n"
+ height = len(disk_string.split("\n"))+16
+ if height < 24:
+ height = 24
+ self.u.resize(80, height)
+ self.u.head()
+ print(" ")
+ print(disk_string)
+ if not self.full:
+ print("S. Switch to Full Output")
+ else:
+ print("S. Switch to Slim Output")
+ lay = self.settings.get("full_layout", False)
+ l_str = "Slim"
+ if lay:
+ l_str = "Full"
+ print("L. Set As Default Layout (Current: {})".format(l_str))
+ print("B. Mount the Boot Drive's EFI")
+ if clover:
+ print("C. Mount the Booted EFI (Clover/OC)")
+ print("")
+
+ dd = self.settings.get("default_disk", None)
+ if dd == "clover":
+ dd = clover
+ elif dd == "boot":
+ dd = "/"
+ di = self.d.get_identifier(dd)
+ if di:
+ print("D. Pick Default Disk ({} - {})".format(self.d.get_volume_name(di), di))
+ else:
+ print("D. Pick Default Disk (None Set)")
+
+ am = self.settings.get("after_mount", None)
+ if not am:
+ am = "Return to Menu"
+ print("M. After Mounting: "+am)
+ print("Q. Quit")
+ print(" ")
+ print("(* denotes the booted EFI (Clover/OC))")
+
+ menu = self.u.grab("Pick the drive containing your EFI: ")
+ if not len(menu):
+ if not di:
+ return self.get_efi()
+ return self.d.get_efi(di)
+ menu = menu.lower()
+ if menu == "q":
+ self.u.resize(80,24)
+ self.u.custom_quit()
+ elif menu == "s":
+ self.full ^= True
+ return self.get_efi()
+ elif menu == "b":
+ return self.d.get_efi("/")
+ elif menu == "c" and clover:
+ return self.d.get_efi(clover)
+ elif menu == "m":
+ self.after_mount()
+ return
+ elif menu == "d":
+ self.default_disk()
+ return
+ elif menu == "l":
+ self.settings["full_layout"] = self.full
+ self.flush_settings()
+ return
+ try:
+ disk_iden = int(menu)
+ if not (disk_iden > 0 and disk_iden <= len(mounts)):
+ # out of range!
+ self.u.grab("Invalid disk!", timeout=3)
+ return self.get_efi()
+ if type(mounts) is list:
+ # We have the small list
+ disk = mounts[disk_iden-1]["identifier"]
+ else:
+ # We have the dict
+ disk = mounts.keys()[disk_iden-1]
+ except:
+ disk = menu
+ iden = self.d.get_identifier(disk)
+ name = self.d.get_volume_name(disk)
+ if not iden:
+ self.u.grab("Invalid disk!", timeout=3)
+ return self.get_efi()
+ # Valid disk!
+ return self.d.get_efi(iden)
+
+ def main(self):
+ while True:
+ efi = self.get_efi()
+ if not efi:
+ # Got nothing back
+ continue
+ # Mount the EFI partition
+ self.u.head("Mounting {}".format(efi))
+ print(" ")
+ out = self.d.mount_partition(efi)
+ if out[2] == 0:
+ print(out[0])
+ else:
+ print(out[1])
+ # Check our settings
+ am = self.settings.get("after_mount", None)
+ if not am:
+ continue
+ if "reveal" in am.lower():
+ # Reveal
+ mp = self.d.get_mount_point(efi)
+ if mp:
+ self.r.run({"args":["open", mp]})
+ # Hang out for a couple seconds
+ if not self.settings.get("skip_countdown", False):
+ self.u.grab("", timeout=3)
+ if "quit" in am.lower():
+ # Quit
+ self.u.resize(80,24)
+ self.u.custom_quit()
+
+ def quiet_mount(self, disk_list, unmount=False):
+ ret = 0
+ for disk in disk_list:
+ ident = self.d.get_identifier(disk)
+ if not ident:
+ continue
+ efi = self.d.get_efi(ident)
+ if not efi:
+ continue
+ if unmount:
+ out = self.d.unmount_partition(efi)
+ else:
+ out = self.d.mount_partition(efi)
+ if not out[2] == 0:
+ ret = out[2]
+ exit(ret)
+
+if __name__ == '__main__':
+ # Setup the cli args
+ parser = argparse.ArgumentParser(prog="MountEFI.command", description="MountEFI - an EFI Mounting Utility by CorpNewt")
+ parser.add_argument("-u", "--unmount", help="unmount instead of mount the passed EFIs", action="store_true")
+ parser.add_argument("-p", "--print-efi", help="prints the disk#s# of the EFI attached to the passed var")
+ parser.add_argument("disks",nargs="*")
+
+ args = parser.parse_args()
+
+ m = MountEFI(settings="./Scripts/settings.json")
+ # Gather defaults
+ unmount = False
+ if args.unmount:
+ unmount = True
+ if args.print_efi:
+ print("{}".format(m.d.get_efi(args.print_efi)))
+ # Check for args
+ if len(args.disks):
+ # We got command line args!
+ m.quiet_mount(args.disks, unmount)
+ elif not args.print_efi:
+ m.main()
diff --git a/Utilities/MountEFI/README.md b/Utilities/MountEFI/README.md
new file mode 100644
index 0000000..ba71c9d
--- /dev/null
+++ b/Utilities/MountEFI/README.md
@@ -0,0 +1,18 @@
+# MountEFI
+An *even* more robust edition of my previous MountEFI scripts.
+
+Other scripts can call this script to do a silent mount - and receive a 0 on succes, or 1 (or higher) on failure.
+
+For example: If another script calls `MountEFI.command disk0` then my script would mount without user interaction, and return a 0 on success, or a 1 on failure. This can also take multiple EFIs to mount - `MountEFI.command disk0 / disk3` would mount the EFIs connected to `disk0`, the boot drive (`/`), and `disk3` if they exist.
+
+***
+
+## To install:
+
+Do the following one line at a time in Terminal:
+
+ git clone https://github.com/corpnewt/MountEFI
+ cd MountEFI
+ chmod +x MountEFI.command
+
+Then run with either `./MountEFI.command` or by double-clicking *MountEFI.command*
diff --git a/Utilities/MountEFI/Scripts/__init__.py b/Utilities/MountEFI/Scripts/__init__.py
new file mode 100644
index 0000000..962c1d3
--- /dev/null
+++ b/Utilities/MountEFI/Scripts/__init__.py
@@ -0,0 +1,4 @@
+from os.path import dirname, basename, isfile
+import glob
+modules = glob.glob(dirname(__file__)+"/*.py")
+__all__ = [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]
\ No newline at end of file
diff --git a/Utilities/MountEFI/Scripts/__init__.pyc b/Utilities/MountEFI/Scripts/__init__.pyc
new file mode 100644
index 0000000..40c826d
Binary files /dev/null and b/Utilities/MountEFI/Scripts/__init__.pyc differ
diff --git a/Utilities/MountEFI/Scripts/bdmesg.py b/Utilities/MountEFI/Scripts/bdmesg.py
new file mode 100755
index 0000000..bd7edbc
--- /dev/null
+++ b/Utilities/MountEFI/Scripts/bdmesg.py
@@ -0,0 +1,70 @@
+import binascii, subprocess, sys
+
+def get_clover_uuid():
+ bd = bdmesg()
+ if not len(bd):
+ return ""
+ # Get bdmesg output - then parse for SelfDevicePath
+ if not "SelfDevicePath=" in bd:
+ # Not found
+ return ""
+ try:
+ # Split to just the contents of that line
+ line = bd.split("SelfDevicePath=")[1].split("\n")[0]
+ # Get the HD section
+ hd = line.split("HD(")[1].split(")")[0]
+ # Get the UUID
+ uuid = hd.split(",")[2]
+ return uuid
+ except:
+ pass
+ return ""
+
+def get_oc_uuid():
+ p = subprocess.Popen(["nvram","4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:boot-path"], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ oc, oe = p.communicate()
+ oc = _decode(oc)
+ try:
+ path = oc.split("GPT,")[1].split(",")[0]
+ except:
+ path = ""
+ return path
+
+def get_bootloader_uuid():
+ b_uuid = get_clover_uuid()
+ if not b_uuid:
+ b_uuid = get_oc_uuid()
+ return b_uuid
+
+def bdmesg(just_clover = True):
+ b = "" if just_clover else _bdmesg(["ioreg","-l","-p","IOService","-w0"])
+ if b == "":
+ b = _bdmesg(["ioreg","-l","-p","IODeviceTree","-w0"])
+ return b
+
+def _decode(var):
+ if sys.version_info >= (3,0) and isinstance(var, bytes):
+ var = var.decode("utf-8","ignore")
+ return var
+
+def _bdmesg(comm):
+ # Runs ioreg -l -p IODeviceTree -w0 and searches for "boot-log"
+ p = subprocess.Popen(comm, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ bd, be = p.communicate()
+ bd = _decode(bd)
+ for line in bd.split("\n"):
+ # We're just looking for the "boot-log" property, then we need to format it
+ if not '"boot-log"' in line:
+ # Skip it!
+ continue
+ # Must have found it - let's try to split it, then get the hex data and process it
+ try:
+ # Split it up, then convert from hex to ascii
+ ascii_bytes = binascii.unhexlify(line.split("<")[1].split(">")[0].encode("utf-8"))
+ ascii_bytes = _decode(ascii_bytes)
+ return ascii_bytes
+ except:
+ # Failed to convert
+ return ""
+ # Didn't find it
+ return ""
diff --git a/Utilities/MountEFI/Scripts/bdmesg.pyc b/Utilities/MountEFI/Scripts/bdmesg.pyc
new file mode 100644
index 0000000..99b45b4
Binary files /dev/null and b/Utilities/MountEFI/Scripts/bdmesg.pyc differ
diff --git a/Utilities/MountEFI/Scripts/disk.py b/Utilities/MountEFI/Scripts/disk.py
new file mode 100755
index 0000000..e384ec0
--- /dev/null
+++ b/Utilities/MountEFI/Scripts/disk.py
@@ -0,0 +1,469 @@
+import subprocess, plistlib, sys, os, time, json
+sys.path.append(os.path.abspath(os.path.dirname(os.path.realpath(__file__))))
+import run
+if sys.version_info < (3,0):
+ # Force use of StringIO instead of cStringIO as the latter
+ # has issues with Unicode strings
+ from StringIO import StringIO
+
+class Disk:
+
+ def __init__(self):
+ self.r = run.Run()
+ self.diskutil = self.get_diskutil()
+ self.os_version = ".".join(
+ self.r.run({"args":["sw_vers", "-productVersion"]})[0].split(".")[:2]
+ )
+ self.full_os_version = self.r.run({"args":["sw_vers", "-productVersion"]})[0]
+ if len(self.full_os_version.split(".")) < 3:
+ # Add .0 in case of 10.14
+ self.full_os_version += ".0"
+ self.sudo_mount_version = "10.13.6"
+ self.sudo_mount_types = ["efi"]
+ self.apfs = {}
+ self._update_disks()
+
+ def _get_str(self, val):
+ # Helper method to return a string value based on input type
+ if (sys.version_info < (3,0) and isinstance(val, unicode)) or (sys.version_info >= (3,0) and isinstance(val, bytes)):
+ return val.encode("utf-8")
+ return str(val)
+
+ def _get_plist(self, s):
+ p = {}
+ try:
+ if sys.version_info >= (3, 0):
+ p = plistlib.loads(s.encode("utf-8"))
+ else:
+ # p = plistlib.readPlistFromString(s)
+ # We avoid using readPlistFromString() as that uses
+ # cStringIO and fails when Unicode strings are detected
+ # Don't subclass - keep the parser local
+ from xml.parsers.expat import ParserCreate
+ # Create a new PlistParser object - then we need to set up
+ # the values and parse.
+ pa = plistlib.PlistParser()
+ # We also monkey patch this to encode unicode as utf-8
+ def end_string():
+ d = pa.getData()
+ if isinstance(d,unicode):
+ d = d.encode("utf-8")
+ pa.addObject(d)
+ pa.end_string = end_string
+ parser = ParserCreate()
+ parser.StartElementHandler = pa.handleBeginElement
+ parser.EndElementHandler = pa.handleEndElement
+ parser.CharacterDataHandler = pa.handleData
+ if isinstance(s, unicode):
+ # Encode unicode -> string; use utf-8 for safety
+ s = s.encode("utf-8")
+ # Parse the string
+ parser.Parse(s, 1)
+ p = pa.root
+ except Exception as e:
+ print(e)
+ pass
+ return p
+
+ def _compare_versions(self, vers1, vers2, pad = -1):
+ # Helper method to compare ##.## strings
+ #
+ # vers1 < vers2 = True
+ # vers1 = vers2 = None
+ # vers1 > vers2 = False
+ #
+ # Must be separated with a period
+
+ # Sanitize the pads
+ pad = -1 if not type(pad) is int else pad
+
+ # Cast as strings
+ vers1 = str(vers1)
+ vers2 = str(vers2)
+
+ # Split to lists
+ v1_parts = vers1.split(".")
+ v2_parts = vers2.split(".")
+
+ # Equalize lengths
+ if len(v1_parts) < len(v2_parts):
+ v1_parts.extend([str(pad) for x in range(len(v2_parts) - len(v1_parts))])
+ elif len(v2_parts) < len(v1_parts):
+ v2_parts.extend([str(pad) for x in range(len(v1_parts) - len(v2_parts))])
+
+ # Iterate and compare
+ for i in range(len(v1_parts)):
+ # Remove non-numeric
+ v1 = ''.join(c for c in v1_parts[i] if c.isdigit())
+ v2 = ''.join(c for c in v2_parts[i] if c.isdigit())
+ # If empty - make it a pad var
+ v1 = pad if not len(v1) else v1
+ v2 = pad if not len(v2) else v2
+ # Compare
+ if int(v1) < int(v2):
+ return True
+ elif int(v1) > int(v2):
+ return False
+ # Never differed - return None, must be equal
+ return None
+
+ def update(self):
+ self._update_disks()
+
+ def _update_disks(self):
+ self.disks = self.get_disks()
+ self.disk_text = self.get_disk_text()
+ if self._compare_versions("10.12", self.os_version):
+ self.apfs = self.get_apfs()
+ else:
+ self.apfs = {}
+
+ def get_diskutil(self):
+ # Returns the path to the diskutil binary
+ return self.r.run({"args":["which", "diskutil"]})[0].split("\n")[0].split("\r")[0]
+
+ def get_disks(self):
+ # Returns a dictionary object of connected disks
+ disk_list = self.r.run({"args":[self.diskutil, "list", "-plist"]})[0]
+ return self._get_plist(disk_list)
+
+ def get_disk_text(self):
+ # Returns plain text listing connected disks
+ return self.r.run({"args":[self.diskutil, "list"]})[0]
+
+ def get_disk_info(self, disk):
+ disk_id = self.get_identifier(disk)
+ if not disk_id:
+ return None
+ disk_list = self.r.run({"args":[self.diskutil, "info", "-plist", disk_id]})[0]
+ return self._get_plist(disk_list)
+
+ def get_disk_fs(self, disk):
+ disk_id = self.get_identifier(disk)
+ if not disk_id:
+ return None
+ return self.get_disk_info(disk_id).get("FilesystemName", None)
+
+ def get_disk_fs_type(self, disk):
+ disk_id = self.get_identifier(disk)
+ if not disk_id:
+ return None
+ return self.get_disk_info(disk_id).get("FilesystemType", None)
+
+ def get_apfs(self):
+ # Returns a dictionary object of apfs disks
+ output = self.r.run({"args":"echo y | " + self.diskutil + " apfs list -plist", "shell" : True})
+ if not output[2] == 0:
+ # Error getting apfs info - return an empty dict
+ return {}
+ disk_list = output[0]
+ p_list = disk_list.split(" 1:
+ # We had text before the start - get only the plist info
+ disk_list = "= (3, 0):
+ from urllib.request import urlopen, Request
+else:
+ # Import urllib2 to catch errors
+ import urllib2
+ from urllib2 import urlopen, Request
+
+class Downloader:
+
+ def __init__(self,**kwargs):
+ self.ua = kwargs.get("useragent",{"User-Agent":"Mozilla"})
+ self.chunk = 1048576 # 1024 x 1024 i.e. 1MiB
+
+ # Provide reasonable default logic to workaround macOS CA file handling
+ cafile = ssl.get_default_verify_paths().openssl_cafile
+ try:
+ # If default OpenSSL CA file does not exist, use that from certifi
+ if not os.path.exists(cafile):
+ import certifi
+ cafile = certifi.where()
+ self.ssl_context = ssl.create_default_context(cafile=cafile)
+ except:
+ # None of the above worked, disable certificate verification for now
+ self.ssl_context = ssl._create_unverified_context()
+ return
+
+ def _decode(self, value, encoding="utf-8", errors="ignore"):
+ # Helper method to only decode if bytes type
+ if sys.version_info >= (3,0) and isinstance(value, bytes):
+ return value.decode(encoding,errors)
+ return value
+
+ def open_url(self, url, headers = None):
+ # Fall back on the default ua if none provided
+ headers = self.ua if headers == None else headers
+ # Wrap up the try/except block so we don't have to do this for each function
+ try:
+ response = urlopen(Request(url, headers=headers), context=self.ssl_context)
+ except Exception as e:
+ # No fixing this - bail
+ return None
+ return response
+
+ def get_size(self, size, suffix=None, use_1024=False, round_to=2, strip_zeroes=False):
+ # size is the number of bytes
+ # suffix is the target suffix to locate (B, KB, MB, etc) - if found
+ # use_2014 denotes whether or not we display in MiB vs MB
+ # round_to is the number of dedimal points to round our result to (0-15)
+ # strip_zeroes denotes whether we strip out zeroes
+
+ # Failsafe in case our size is unknown
+ if size == -1:
+ return "Unknown"
+ # Get our suffixes based on use_1024
+ ext = ["B","KiB","MiB","GiB","TiB","PiB"] if use_1024 else ["B","KB","MB","GB","TB","PB"]
+ div = 1024 if use_1024 else 1000
+ s = float(size)
+ s_dict = {} # Initialize our dict
+ # Iterate the ext list, and divide by 1000 or 1024 each time to setup the dict {ext:val}
+ for e in ext:
+ s_dict[e] = s
+ s /= div
+ # Get our suffix if provided - will be set to None if not found, or if started as None
+ suffix = next((x for x in ext if x.lower() == suffix.lower()),None) if suffix else suffix
+ # Get the largest value that's still over 1
+ biggest = suffix if suffix else next((x for x in ext[::-1] if s_dict[x] >= 1), "B")
+ # Determine our rounding approach - first make sure it's an int; default to 2 on error
+ try:round_to=int(round_to)
+ except:round_to=2
+ round_to = 0 if round_to < 0 else 15 if round_to > 15 else round_to # Ensure it's between 0 and 15
+ bval = round(s_dict[biggest], round_to)
+ # Split our number based on decimal points
+ a,b = str(bval).split(".")
+ # Check if we need to strip or pad zeroes
+ b = b.rstrip("0") if strip_zeroes else b.ljust(round_to,"0") if round_to > 0 else ""
+ return "{:,}{} {}".format(int(a),"" if not b else "."+b,biggest)
+
+ def _progress_hook(self, response, bytes_so_far, total_size):
+ if total_size > 0:
+ percent = float(bytes_so_far) / total_size
+ percent = round(percent*100, 2)
+ t_s = self.get_size(total_size)
+ try: b_s = self.get_size(bytes_so_far, t_s.split(" ")[1])
+ except: b_s = self.get_size(bytes_so_far)
+ sys.stdout.write("Downloaded {} of {} ({:.2f}%)\r".format(b_s, t_s, percent))
+ else:
+ b_s = self.get_size(bytes_so_far)
+ sys.stdout.write("Downloaded {}\r".format(b_s))
+
+ def get_string(self, url, progress = True, headers = None, expand_gzip = True):
+ response = self.get_bytes(url,progress,headers,expand_gzip)
+ if response == None: return None
+ return self._decode(response)
+
+ def get_bytes(self, url, progress = True, headers = None, expand_gzip = True):
+ response = self.open_url(url, headers)
+ if response == None: return None
+ bytes_so_far = 0
+ try: total_size = int(response.headers['Content-Length'])
+ except: total_size = -1
+ chunk_so_far = b""
+ while True:
+ chunk = response.read(self.chunk)
+ bytes_so_far += len(chunk)
+ if progress: self._progress_hook(response, bytes_so_far, total_size)
+ if not chunk: break
+ chunk_so_far += chunk
+ if expand_gzip and response.headers.get("Content-Encoding","unknown").lower() == "gzip":
+ fileobj = BytesIO(chunk_so_far)
+ gfile = gzip.GzipFile(fileobj=fileobj)
+ return gfile.read()
+ return chunk_so_far
+
+ def stream_to_file(self, url, file_path, progress = True, headers = None):
+ response = self.open_url(url, headers)
+ if response == None: return None
+ bytes_so_far = 0
+ try: total_size = int(response.headers['Content-Length'])
+ except: total_size = -1
+ with open(file_path, 'wb') as f:
+ while True:
+ chunk = response.read(self.chunk)
+ bytes_so_far += len(chunk)
+ if progress: self._progress_hook(response, bytes_so_far, total_size)
+ if not chunk: break
+ f.write(chunk)
+ return file_path if os.path.exists(file_path) else None
diff --git a/Utilities/MountEFI/Scripts/downloader.pyc b/Utilities/MountEFI/Scripts/downloader.pyc
new file mode 100644
index 0000000..b7de6bc
Binary files /dev/null and b/Utilities/MountEFI/Scripts/downloader.pyc differ
diff --git a/Utilities/MountEFI/Scripts/rebuildcache.py b/Utilities/MountEFI/Scripts/rebuildcache.py
new file mode 100644
index 0000000..009eb84
--- /dev/null
+++ b/Utilities/MountEFI/Scripts/rebuildcache.py
@@ -0,0 +1,39 @@
+import os, sys
+sys.path.append(os.path.abspath(os.path.dirname(os.path.realpath(__file__))))
+import run, utils
+
+class Rebuild:
+ def __init__(self):
+ self.r = run.Run()
+ return
+
+ def _compare_versions(self, vers1, vers2):
+ # Helper method to compare ##.## strings
+ #
+ # vers1 < vers2 = True
+ # vers1 = vers2 = None
+ # vers1 > vers2 = False
+ #
+ try:
+ v1_parts = vers1.split(".")
+ v2_parts = vers2.split(".")
+ except:
+ # Formatted wrong - return None
+ return None
+ for i in range(len(v1_parts)):
+ if int(v1_parts[i]) < int(v2_parts[i]):
+ return True
+ elif int(v1_parts[i]) > int(v2_parts[i]):
+ return False
+ # Never differed - return None, must be equal
+ return None
+
+ def rebuild(self, stream = True):
+ # Get os version
+ os_vers = self.r.run({"args":["sw_vers", "-productVersion"]})[0]
+ if self._compare_versions(os_vers, "10.11.0") == True:
+ # We're on an OS version prior to 10.11
+ return self.r.run({"args":"sudo touch /System/Library/Extensions && sudo kextcache -u /", "stream" : stream, "shell" : True})
+ else:
+ # 10.11 or above
+ return self.r.run({"args":"sudo kextcache -i / && sudo kextcache -u /", "stream" : stream, "shell" : True})
\ No newline at end of file
diff --git a/Utilities/MountEFI/Scripts/rebuildcache.pyc b/Utilities/MountEFI/Scripts/rebuildcache.pyc
new file mode 100644
index 0000000..80f95e9
Binary files /dev/null and b/Utilities/MountEFI/Scripts/rebuildcache.pyc differ
diff --git a/Utilities/MountEFI/Scripts/reveal.py b/Utilities/MountEFI/Scripts/reveal.py
new file mode 100755
index 0000000..f0c7516
--- /dev/null
+++ b/Utilities/MountEFI/Scripts/reveal.py
@@ -0,0 +1,70 @@
+import sys, os
+sys.path.append(os.path.abspath(os.path.dirname(os.path.realpath(__file__))))
+import run
+
+class Reveal:
+
+ def __init__(self):
+ self.r = run.Run()
+ return
+
+ def get_parent(self, path):
+ return os.path.normpath(os.path.join(path, os.pardir))
+
+ def reveal(self, path, new_window = False):
+ # Reveals the passed path in Finder - only works on macOS
+ if not sys.platform == "darwin":
+ return ("", "macOS Only", 1)
+ if not path:
+ # No path sent - nothing to reveal
+ return ("", "No path specified", 1)
+ # Build our script - then convert it to a single line task
+ if not os.path.exists(path):
+ # Not real - bail
+ return ("", "{} - doesn't exist".format(path), 1)
+ # Get the absolute path
+ path = os.path.abspath(path)
+ command = ["osascript"]
+ if new_window:
+ command.extend([
+ "-e", "set p to \"{}\"".format(path.replace("\"", "\\\"")),
+ "-e", "tell application \"Finder\"",
+ "-e", "reveal POSIX file p as text",
+ "-e", "activate",
+ "-e", "end tell"
+ ])
+ else:
+ if path == self.get_parent(path):
+ command.extend([
+ "-e", "set p to \"{}\"".format(path.replace("\"", "\\\"")),
+ "-e", "tell application \"Finder\"",
+ "-e", "reopen",
+ "-e", "activate",
+ "-e", "set target of window 1 to (POSIX file p as text)",
+ "-e", "end tell"
+ ])
+ else:
+ command.extend([
+ "-e", "set o to \"{}\"".format(self.get_parent(path).replace("\"", "\\\"")),
+ "-e", "set p to \"{}\"".format(path.replace("\"", "\\\"")),
+ "-e", "tell application \"Finder\"",
+ "-e", "reopen",
+ "-e", "activate",
+ "-e", "set target of window 1 to (POSIX file o as text)",
+ "-e", "select (POSIX file p as text)",
+ "-e", "end tell"
+ ])
+ return self.r.run({"args" : command})
+
+ def notify(self, title = None, subtitle = None, sound = None):
+ # Sends a notification
+ if not title:
+ return ("", "Malformed dict", 1)
+ # Build our notification
+ n_text = "display notification with title \"{}\"".format(title.replace("\"", "\\\""))
+ if subtitle:
+ n_text += " subtitle \"{}\"".format(subtitle.replace("\"", "\\\""))
+ if sound:
+ n_text += " sound name \"{}\"".format(sound.replace("\"", "\\\""))
+ command = ["osascript", "-e", n_text]
+ return self.r.run({"args" : command})
\ No newline at end of file
diff --git a/Utilities/MountEFI/Scripts/reveal.pyc b/Utilities/MountEFI/Scripts/reveal.pyc
new file mode 100644
index 0000000..aa93b6d
Binary files /dev/null and b/Utilities/MountEFI/Scripts/reveal.pyc differ
diff --git a/Utilities/MountEFI/Scripts/run.py b/Utilities/MountEFI/Scripts/run.py
new file mode 100755
index 0000000..b586adc
--- /dev/null
+++ b/Utilities/MountEFI/Scripts/run.py
@@ -0,0 +1,151 @@
+import sys, subprocess, time, threading, shlex
+try:
+ from Queue import Queue, Empty
+except:
+ from queue import Queue, Empty
+
+ON_POSIX = 'posix' in sys.builtin_module_names
+
+class Run:
+
+ def __init__(self):
+ return
+
+ def _read_output(self, pipe, q):
+ try:
+ for line in iter(lambda: pipe.read(1), b''):
+ q.put(line)
+ except ValueError:
+ pass
+ pipe.close()
+
+ def _create_thread(self, output):
+ # Creates a new queue and thread object to watch based on the output pipe sent
+ q = Queue()
+ t = threading.Thread(target=self._read_output, args=(output, q))
+ t.daemon = True
+ return (q,t)
+
+ def _stream_output(self, comm, shell = False):
+ output = error = ""
+ p = None
+ try:
+ if shell and type(comm) is list:
+ comm = " ".join(shlex.quote(x) for x in comm)
+ if not shell and type(comm) is str:
+ comm = shlex.split(comm)
+ p = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, universal_newlines=True, close_fds=ON_POSIX)
+ # Setup the stdout thread/queue
+ q,t = self._create_thread(p.stdout)
+ qe,te = self._create_thread(p.stderr)
+ # Start both threads
+ t.start()
+ te.start()
+
+ while True:
+ c = z = ""
+ try: c = q.get_nowait()
+ except Empty: pass
+ else:
+ sys.stdout.write(c)
+ output += c
+ sys.stdout.flush()
+ try: z = qe.get_nowait()
+ except Empty: pass
+ else:
+ sys.stderr.write(z)
+ error += z
+ sys.stderr.flush()
+ if not c==z=="": continue # Keep going until empty
+ # No output - see if still running
+ p.poll()
+ if p.returncode != None:
+ # Subprocess ended
+ break
+ # No output, but subprocess still running - stall for 20ms
+ time.sleep(0.02)
+
+ o, e = p.communicate()
+ return (output+o, error+e, p.returncode)
+ except:
+ if p:
+ try: o, e = p.communicate()
+ except: o = e = ""
+ return (output+o, error+e, p.returncode)
+ return ("", "Command not found!", 1)
+
+ def _decode(self, value, encoding="utf-8", errors="ignore"):
+ # Helper method to only decode if bytes type
+ if sys.version_info >= (3,0) and isinstance(value, bytes):
+ return value.decode(encoding,errors)
+ return value
+
+ def _run_command(self, comm, shell = False):
+ c = None
+ try:
+ if shell and type(comm) is list:
+ comm = " ".join(shlex.quote(x) for x in comm)
+ if not shell and type(comm) is str:
+ comm = shlex.split(comm)
+ p = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ c = p.communicate()
+ except:
+ if c == None:
+ return ("", "Command not found!", 1)
+ return (self._decode(c[0]), self._decode(c[1]), p.returncode)
+
+ def run(self, command_list, leave_on_fail = False):
+ # Command list should be an array of dicts
+ if type(command_list) is dict:
+ # We only have one command
+ command_list = [command_list]
+ output_list = []
+ for comm in command_list:
+ args = comm.get("args", [])
+ shell = comm.get("shell", False)
+ stream = comm.get("stream", False)
+ sudo = comm.get("sudo", False)
+ stdout = comm.get("stdout", False)
+ stderr = comm.get("stderr", False)
+ mess = comm.get("message", None)
+ show = comm.get("show", False)
+
+ if not mess == None:
+ print(mess)
+
+ if not len(args):
+ # nothing to process
+ continue
+ if sudo:
+ # Check if we have sudo
+ out = self._run_command(["which", "sudo"])
+ if "sudo" in out[0]:
+ # Can sudo
+ if type(args) is list:
+ args.insert(0, out[0].replace("\n", "")) # add to start of list
+ elif type(args) is str:
+ args = out[0].replace("\n", "") + " " + args # add to start of string
+
+ if show:
+ print(" ".join(args))
+
+ if stream:
+ # Stream it!
+ out = self._stream_output(args, shell)
+ else:
+ # Just run and gather output
+ out = self._run_command(args, shell)
+ if stdout and len(out[0]):
+ print(out[0])
+ if stderr and len(out[1]):
+ print(out[1])
+ # Append output
+ output_list.append(out)
+ # Check for errors
+ if leave_on_fail and out[2] != 0:
+ # Got an error - leave
+ break
+ if len(output_list) == 1:
+ # We only ran one command - just return that output
+ return output_list[0]
+ return output_list
diff --git a/Utilities/MountEFI/Scripts/run.pyc b/Utilities/MountEFI/Scripts/run.pyc
new file mode 100644
index 0000000..fdfb50c
Binary files /dev/null and b/Utilities/MountEFI/Scripts/run.pyc differ
diff --git a/Utilities/MountEFI/Scripts/settings.json b/Utilities/MountEFI/Scripts/settings.json
new file mode 100644
index 0000000..9eff7fa
--- /dev/null
+++ b/Utilities/MountEFI/Scripts/settings.json
@@ -0,0 +1 @@
+{"after_mount": "Reveal and Quit", "default_disk": null, "full_layout": false, "skip_countdown": false}
\ No newline at end of file
diff --git a/Utilities/MountEFI/Scripts/utils.py b/Utilities/MountEFI/Scripts/utils.py
new file mode 100755
index 0000000..9ee4b30
--- /dev/null
+++ b/Utilities/MountEFI/Scripts/utils.py
@@ -0,0 +1,255 @@
+import sys, os, time, re, json, datetime, ctypes, subprocess
+
+if os.name == "nt":
+ # Windows
+ import msvcrt
+else:
+ # Not Windows \o/
+ import select
+
+class Utils:
+
+ def __init__(self, name = "Python Script"):
+ self.name = name
+ # Init our colors before we need to print anything
+ cwd = os.getcwd()
+ os.chdir(os.path.dirname(os.path.realpath(__file__)))
+ if os.path.exists("colors.json"):
+ self.colors_dict = json.load(open("colors.json"))
+ else:
+ self.colors_dict = {}
+ os.chdir(cwd)
+
+ def check_admin(self):
+ # Returns whether or not we're admin
+ try:
+ is_admin = os.getuid() == 0
+ except AttributeError:
+ is_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0
+ return is_admin
+
+ def elevate(self, file):
+ # Runs the passed file as admin
+ if self.check_admin():
+ return
+ if os.name == "nt":
+ ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, file, None, 1)
+ else:
+ try:
+ p = subprocess.Popen(["which", "sudo"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ c = p.communicate()[0].decode("utf-8", "ignore").replace("\n", "")
+ os.execv(c, [ sys.executable, 'python'] + sys.argv)
+ except:
+ exit(1)
+
+ def compare_versions(self, vers1, vers2, **kwargs):
+ # Helper method to compare ##.## strings
+ #
+ # vers1 < vers2 = True
+ # vers1 = vers2 = None
+ # vers1 > vers2 = False
+
+ # Sanitize the pads
+ pad = str(kwargs.get("pad", ""))
+ sep = str(kwargs.get("separator", "."))
+
+ ignore_case = kwargs.get("ignore_case", True)
+
+ # Cast as strings
+ vers1 = str(vers1)
+ vers2 = str(vers2)
+
+ if ignore_case:
+ vers1 = vers1.lower()
+ vers2 = vers2.lower()
+
+ # Split and pad lists
+ v1_parts, v2_parts = self.pad_length(vers1.split(sep), vers2.split(sep))
+
+ # Iterate and compare
+ for i in range(len(v1_parts)):
+ # Remove non-numeric
+ v1 = ''.join(c.lower() for c in v1_parts[i] if c.isalnum())
+ v2 = ''.join(c.lower() for c in v2_parts[i] if c.isalnum())
+ # Equalize the lengths
+ v1, v2 = self.pad_length(v1, v2)
+ # Compare
+ if str(v1) < str(v2):
+ return True
+ elif str(v1) > str(v2):
+ return False
+ # Never differed - return None, must be equal
+ return None
+
+ def pad_length(self, var1, var2, pad = "0"):
+ # Pads the vars on the left side to make them equal length
+ pad = "0" if len(str(pad)) < 1 else str(pad)[0]
+ if not type(var1) == type(var2):
+ # Type mismatch! Just return what we got
+ return (var1, var2)
+ if len(var1) < len(var2):
+ if type(var1) is list:
+ var1.extend([str(pad) for x in range(len(var2) - len(var1))])
+ else:
+ var1 = "{}{}".format((pad*(len(var2)-len(var1))), var1)
+ elif len(var2) < len(var1):
+ if type(var2) is list:
+ var2.extend([str(pad) for x in range(len(var1) - len(var2))])
+ else:
+ var2 = "{}{}".format((pad*(len(var1)-len(var2))), var2)
+ return (var1, var2)
+
+ def check_path(self, path):
+ # Let's loop until we either get a working path, or no changes
+ test_path = path
+ last_path = None
+ while True:
+ # Bail if we've looped at least once and the path didn't change
+ if last_path != None and last_path == test_path: return None
+ last_path = test_path
+ # Check if we stripped everything out
+ if not len(test_path): return None
+ # Check if we have a valid path
+ if os.path.exists(test_path):
+ return os.path.abspath(test_path)
+ # Check for quotes
+ if test_path[0] == test_path[-1] and test_path[0] in ('"',"'"):
+ test_path = test_path[1:-1]
+ continue
+ # Check for a tilde and expand if needed
+ if test_path[0] == "~":
+ tilde_expanded = os.path.expanduser(test_path)
+ if tilde_expanded != test_path:
+ # Got a change
+ test_path = tilde_expanded
+ continue
+ # Let's check for spaces - strip from the left first, then the right
+ if test_path[0] in (" ","\t"):
+ test_path = test_path[1:]
+ continue
+ if test_path[-1] in (" ","\t"):
+ test_path = test_path[:-1]
+ continue
+ # Maybe we have escapes to handle?
+ test_path = "\\".join([x.replace("\\", "") for x in test_path.split("\\\\")])
+
+ def grab(self, prompt, **kwargs):
+ # Takes a prompt, a default, and a timeout and shows it with that timeout
+ # returning the result
+ timeout = kwargs.get("timeout", 0)
+ default = kwargs.get("default", None)
+ # If we don't have a timeout - then skip the timed sections
+ if timeout <= 0:
+ if sys.version_info >= (3, 0):
+ return input(prompt)
+ else:
+ return str(raw_input(prompt))
+ # Write our prompt
+ sys.stdout.write(prompt)
+ sys.stdout.flush()
+ if os.name == "nt":
+ start_time = time.time()
+ i = ''
+ while True:
+ if msvcrt.kbhit():
+ c = msvcrt.getche()
+ if ord(c) == 13: # enter_key
+ break
+ elif ord(c) >= 32: #space_char
+ i += c
+ if len(i) == 0 and (time.time() - start_time) > timeout:
+ break
+ else:
+ i, o, e = select.select( [sys.stdin], [], [], timeout )
+ if i:
+ i = sys.stdin.readline().strip()
+ print('') # needed to move to next line
+ if len(i) > 0:
+ return i
+ else:
+ return default
+
+ def cls(self):
+ os.system('cls' if os.name=='nt' else 'clear')
+
+ def cprint(self, message, **kwargs):
+ strip_colors = kwargs.get("strip_colors", False)
+ if os.name == "nt":
+ strip_colors = True
+ reset = u"\u001b[0m"
+ # Requires sys import
+ for c in self.colors:
+ if strip_colors:
+ message = message.replace(c["find"], "")
+ else:
+ message = message.replace(c["find"], c["replace"])
+ if strip_colors:
+ return message
+ sys.stdout.write(message)
+ print(reset)
+
+ # Needs work to resize the string if color chars exist
+ '''# Header drawing method
+ def head(self, text = None, width = 55):
+ if text == None:
+ text = self.name
+ self.cls()
+ print(" {}".format("#"*width))
+ len_text = self.cprint(text, strip_colors=True)
+ mid_len = int(round(width/2-len(len_text)/2)-2)
+ middle = " #{}{}{}#".format(" "*mid_len, len_text, " "*((width - mid_len - len(len_text))-2))
+ if len(middle) > width+1:
+ # Get the difference
+ di = len(middle) - width
+ # Add the padding for the ...#
+ di += 3
+ # Trim the string
+ middle = middle[:-di]
+ newlen = len(middle)
+ middle += "...#"
+ find_list = [ c["find"] for c in self.colors ]
+
+ # Translate colored string to len
+ middle = middle.replace(len_text, text + self.rt_color) # always reset just in case
+ self.cprint(middle)
+ print("#"*width)'''
+
+ # Header drawing method
+ def head(self, text = None, width = 55):
+ if text == None:
+ text = self.name
+ self.cls()
+ print(" {}".format("#"*width))
+ mid_len = int(round(width/2-len(text)/2)-2)
+ middle = " #{}{}{}#".format(" "*mid_len, text, " "*((width - mid_len - len(text))-2))
+ if len(middle) > width+1:
+ # Get the difference
+ di = len(middle) - width
+ # Add the padding for the ...#
+ di += 3
+ # Trim the string
+ middle = middle[:-di] + "...#"
+ print(middle)
+ print("#"*width)
+
+ def resize(self, width, height):
+ print('\033[8;{};{}t'.format(height, width))
+
+ def custom_quit(self):
+ self.head()
+ print("by CorpNewt\n")
+ print("Thanks for testing it out, for bugs/comments/complaints")
+ print("send me a message on Reddit, or check out my GitHub:\n")
+ print("www.reddit.com/u/corpnewt")
+ print("www.github.com/corpnewt\n")
+ # Get the time and wish them a good morning, afternoon, evening, and night
+ hr = datetime.datetime.now().time().hour
+ if hr > 3 and hr < 12:
+ print("Have a nice morning!\n\n")
+ elif hr >= 12 and hr < 17:
+ print("Have a nice afternoon!\n\n")
+ elif hr >= 17 and hr < 21:
+ print("Have a nice evening!\n\n")
+ else:
+ print("Have a nice night!\n\n")
+ exit(0)
diff --git a/Utilities/MountEFI/Scripts/utils.pyc b/Utilities/MountEFI/Scripts/utils.pyc
new file mode 100644
index 0000000..7481685
Binary files /dev/null and b/Utilities/MountEFI/Scripts/utils.pyc differ
diff --git a/Utilities/README.md b/Utilities/README.md
index 0e119e0..6824555 100644
--- a/Utilities/README.md
+++ b/Utilities/README.md
@@ -1,14 +1,13 @@
-# Utilities for T450s
+# Utilities for T450/T450s
This is where I'll store useful but optional hacks for the laptop
## alc_fix
This is a fix for Headphone jack not working after sleep. To install this, cd into the repository, then:
+- `sudo spctl --master-disable`
- `cd alc_fix`
-- `chmod +x install.sh`
- `./install.sh`
## DW1820A
This folder contains instructions and kexts needed to run DW1820A which enables native WiFi/Bluetooth as well as Airdrop, Instant Hotspot, Hand-off and Continuity.
-
diff --git a/Utilities/alc_fix/ALCPlugFix b/Utilities/alc_fix/ALCPlugFix
index f7cbcfe..549956f 100755
Binary files a/Utilities/alc_fix/ALCPlugFix and b/Utilities/alc_fix/ALCPlugFix differ
diff --git a/Utilities/alc_fix/com.echo.ALCPlugFix.plist b/Utilities/alc_fix/com.echo.ALCPlugFix.plist
deleted file mode 100644
index 435f996..0000000
--- a/Utilities/alc_fix/com.echo.ALCPlugFix.plist
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
- KeepAlive
-
- Label
- com.echo.ALCPlugFix
- ProgramArguments
-
- /usr/bin/ALCPlugFix
-
- RunAtLoad
-
- ServiceIPC
-
- WorkingDirectory
- /usr/bin/
-
-
diff --git a/Utilities/alc_fix/good.win.ALCPlugFix.plist b/Utilities/alc_fix/good.win.ALCPlugFix.plist
new file mode 100644
index 0000000..47928b5
--- /dev/null
+++ b/Utilities/alc_fix/good.win.ALCPlugFix.plist
@@ -0,0 +1,21 @@
+
+
+
+
+ KeepAlive
+
+ Label
+ good.win.ALCPlugFix
+ ProgramArguments
+
+ /usr/local/bin/ALCPlugFix
+
+ RunAtLoad
+
+ ServiceIPC
+
+
+ WorkingDirectory
+ /usr/local/bin/
+
+
diff --git a/Utilities/alc_fix/install.sh b/Utilities/alc_fix/install.sh
index 9d6d378..41a72eb 100755
--- a/Utilities/alc_fix/install.sh
+++ b/Utilities/alc_fix/install.sh
@@ -1,18 +1,17 @@
#!/bin/bash
DAEMON_PATH=/Library/LaunchDaemons/
-BIN_PATH=/usr/bin/
-DAEMON_FILE=com.echo.ALCPlugFix.plist
+BIN_PATH=/usr/local/bin/
+DAEMON_FILE=good.win.ALCPlugFix.plist
VERB_FILE=hda-verb
FIX_FILE=ALCPlugFix
-echo "Installing ALCPlugFix. Root user is required."
+echo "Installing ALCPlugFix v1.7"
-# check if the root filesystem is writeable (starting with macOS 10.15 Catalina, the root filesystem is read-only by default)
-if sudo test ! -w "/"; then
- echo "Root filesystem is not writeable. Remounting as read-write and restarting Finder."
- sudo mount -uw /
- sudo killall Finder
+# check if the directory "usr/local/bin" exist, if not then create the directory
+
+if [ ! -d "$BIN_PATH" ] ; then
+mkdir "$BIN_PATH" ;
fi
# stop the daemon if it's already running
@@ -22,9 +21,10 @@ if sudo launchctl list | grep --quiet ALCPlugFix; then
fi
# copy over the files to their respective locations (overwrite automatically if files exist)
+
sudo cp -f ALCPlugFix $BIN_PATH
sudo cp -f hda-verb $BIN_PATH
-sudo cp -f com.echo.ALCPlugFix.plist $DAEMON_PATH
+sudo cp -f good.win.ALCPlugFix.plist $DAEMON_PATH
# set permissions and ownership
sudo chmod 755 $BIN_PATH$FIX_FILE
diff --git a/Utilities/alc_fix/uninstall.sh b/Utilities/alc_fix/uninstall.sh
index f275693..1150069 100755
--- a/Utilities/alc_fix/uninstall.sh
+++ b/Utilities/alc_fix/uninstall.sh
@@ -2,18 +2,11 @@
echo "Uninstalling ALCPlugFix. Root user is required."
-# check if the root filesystem is writeable (starting with macOS 10.15 Catalina, the root filesystem is read-only by default)
-if sudo test ! -w "/"; then
- echo "Root filesystem is not writeable. Remounting as read-write and restarting Finder."
- sudo mount -uw /
- sudo killall Finder
-fi
-
-sudo rm /usr/bin/ALCPlugFix
-sudo rm /usr/bin/hda-verb
-sudo launchctl unload -w /Library/LaunchDaemons/com.echo.ALCPlugFix.plist
-sudo launchctl remove com.echo.ALCPlugFix
-sudo rm /Library/LaunchDaemons/com.echo.ALCPlugFix.plist
+sudo rm /usr/local/bin/ALCPlugFix
+sudo rm /usr/local/bin/hda-verb
+sudo launchctl unload -w /Library/LaunchDaemons/good.win.ALCPlugFix.plist
+sudo launchctl remove good.win.ALCPlugFix
+sudo rm /Library/LaunchDaemons/good.win.ALCPlugFix.plist
echo "Done!"
exit 0