gibMacOS/MakeInstall.py

883 lines
40 KiB
Python
Raw Normal View History

2019-10-02 22:58:07 +02:00
from Scripts import utils, diskwin, downloader, run
import os, sys, tempfile, shutil, zipfile, platform, json, time
2018-10-07 05:07:20 +02:00
class WinUSB:
def __init__(self):
self.u = utils.Utils("MakeInstall")
2020-03-14 19:41:23 +01:00
if not self.u.check_admin():
# Try to self-elevate
self.u.elevate(os.path.realpath(__file__))
exit()
self.min_plat = 9600
2018-10-07 05:07:20 +02:00
# Make sure we're on windows
self.verify_os()
2018-10-07 05:07:20 +02:00
# Setup initial vars
self.d = diskwin.Disk()
self.dl = downloader.Downloader()
self.r = run.Run()
self.scripts = "Scripts"
self.s_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.scripts)
2020-05-09 21:07:39 +02:00
# self.dd_url = "http://www.chrysocome.net/downloads/ddrelease64.exe"
self.dd_url = "https://github.com/corpnewt/gibMacOS/files/4573241/ddrelease64.exe.zip" # Rehost due to download issues
self.dd_name = ".".join(os.path.basename(self.dd_url).split(".")[:-1]) # Get the name without the last extension
2019-01-26 06:43:54 +01:00
self.z_json = "https://sourceforge.net/projects/sevenzip/best_release.json"
self.z_url2 = "https://www.7-zip.org/a/7z1806-x64.msi"
self.z_url = "https://www.7-zip.org/a/7z[[vers]]-x64.msi"
2018-10-07 05:07:20 +02:00
self.z_name = "7z.exe"
2019-07-01 16:00:49 +02:00
self.bi_url = "https://raw.githubusercontent.com/corpnewt/gibMacOS/master/Scripts/BOOTICEx64.exe"
self.bi_name = "BOOTICEx64.exe"
self.clover_url = "https://api.github.com/repos/CloverHackyColor/CloverBootloader/releases"
2019-11-14 23:02:45 +01:00
self.dids_url = "https://api.github.com/repos/dids/clover-builder/releases"
2020-01-18 17:29:42 +01:00
self.oc_url = "https://api.github.com/repos/acidanthera/OpenCorePkg/releases"
self.oc_boot = "boot"
self.oc_boot0 = "boot0af"
self.oc_boot1 = "boot1f32"
2020-05-16 15:27:53 +02:00
self.oc_boot_url = "https://github.com/acidanthera/DuetPkg/raw/master/BootLoader/bin/"
self.diskpart = os.path.join(os.environ['SYSTEMDRIVE'] + "\\", "Windows", "System32", "diskpart.exe")
2018-10-07 05:07:20 +02:00
# From Tim Sutton's brigadier: https://github.com/timsutton/brigadier/blob/master/brigadier
self.z_path = None
self.z_path64 = os.path.join(os.environ['SYSTEMDRIVE'] + "\\", "Program Files", "7-Zip", "7z.exe")
self.z_path32 = os.path.join(os.environ['SYSTEMDRIVE'] + "\\", "Program Files (x86)", "7-Zip", "7z.exe")
2018-10-07 05:07:20 +02:00
self.recovery_suffixes = (
"recoveryhdupdate.pkg",
"recoveryhdmetadmg.pkg"
)
self.dd_bootsector = True
self.boot0 = "boot0af"
self.boot1 = "boot1f32alt"
self.boot = "boot6"
self.efi_id = "c12a7328-f81f-11d2-ba4b-00a0c93ec93b" # EFI
self.bas_id = "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7" # Microsoft Basic Data
self.hfs_id = "48465300-0000-11AA-AA11-00306543ECAC" # HFS+
self.rec_id = "426F6F74-0000-11AA-AA11-00306543ECAC" # Apple Boot partition (Recovery HD)
2019-09-22 23:25:47 +02:00
self.show_all_disks = False
def verify_os(self):
self.u.head("Verifying OS")
print("")
print("Verifying OS name...")
if not os.name=="nt":
print("")
print("This script is only for Windows!")
print("")
self.u.grab("Press [enter] to exit...")
exit(1)
print(" - Name = NT")
print("Verifying OS version...")
# Verify we're at version 9600 or greater
try:
# Set plat to the last item of the output split by . - looks like:
# Windows-8.1-6.3.9600
# or this:
# Windows-10-10.0.17134-SP0
plat = int(platform.platform().split(".")[-1].split("-")[0])
except:
plat = 0
if plat < self.min_plat:
print("")
print("Currently running {}, this script requires version {} or newer.".format(platform.platform(), self.min_plat))
print("")
self.u.grab("Press [enter] to exit...")
exit(1)
print(" - Version = {}".format(plat))
print("")
print("{} >= {}, continuing...".format(plat, self.min_plat))
2018-10-07 05:07:20 +02:00
def get_disks_of_type(self, disk_list, disk_type=(0,2)):
disks = {}
for disk in disk_list:
if disk_list[disk].get("type",0) in disk_type:
disks[disk] = disk_list[disk]
return disks
def check_dd(self):
# Checks if ddrelease64.exe exists in our Scripts dir
2018-10-07 05:07:20 +02:00
# and if not - downloads it
#
# Returns True if exists/downloaded successfully
# or False if issues.
# Check for dd.exe in the current dir
if os.path.exists(os.path.join(self.s_path, self.dd_name)):
# print("Located {}!".format(self.dd_name))
2018-10-07 05:07:20 +02:00
# Got it
return True
print("Couldn't locate {} - downloading...".format(self.dd_name))
2020-05-09 21:07:39 +02:00
temp = tempfile.mkdtemp()
z_file = os.path.basename(self.dd_url)
2018-10-07 05:07:20 +02:00
# Now we need to download
2020-05-09 21:07:39 +02:00
self.dl.stream_to_file(self.dd_url, os.path.join(temp,z_file))
print(" - Extracting...")
# Extract with built-in tools \o/
cwd = os.getcwd()
os.chdir(temp)
with zipfile.ZipFile(os.path.join(temp,z_file)) as z:
z.extractall(temp)
for x in os.listdir(temp):
if self.dd_name.lower() == x.lower():
# Found it
print(" - Found {}".format(x))
print(" - Copying to {} directory...".format(self.scripts))
shutil.copy(os.path.join(temp,x), os.path.join(self.s_path,x))
# Return to prior cwd
os.chdir(cwd)
# Remove the temp folder
shutil.rmtree(temp,ignore_errors=True)
2018-10-07 05:07:20 +02:00
print("")
return os.path.exists(os.path.join(self.s_path, self.dd_name))
def check_7z(self):
2019-10-15 21:08:02 +02:00
self.z_path = self.z_path64 if os.path.exists(self.z_path64) else self.z_path32 if os.path.exists(self.z_path32) else None
if self.z_path:
2018-10-07 05:07:20 +02:00
return True
print("Didn't locate {} - downloading...".format(self.z_name))
# Didn't find it - let's do some stupid stuff
2019-01-26 06:43:54 +01:00
# First we get our json response - or rather, try to, then parse it
# looking for the current version
dl_url = None
try:
2019-09-07 20:23:46 +02:00
json_data = json.loads(self.dl.get_string(self.z_json))
v_num = json_data.get("release",{}).get("filename","").split("/")[-1].lower().split("-")[0].replace("7z","").replace(".exe","")
2019-01-26 06:43:54 +01:00
if len(v_num):
2019-09-07 20:23:46 +02:00
dl_url = self.z_url.replace("[[vers]]",v_num)
2019-01-26 06:43:54 +01:00
except:
pass
if not dl_url:
dl_url = self.z_url2
2018-10-07 05:07:20 +02:00
temp = tempfile.mkdtemp()
2019-10-15 21:08:02 +02:00
dl_file = self.dl.stream_to_file(dl_url, os.path.join(temp, self.z_name))
if not dl_file: # Didn't download right
shutil.rmtree(temp,ignore_errors=True)
return False
2018-10-07 05:07:20 +02:00
print("")
print("Installing 7zip...")
# From Tim Sutton's brigadier: https://github.com/timsutton/brigadier/blob/master/brigadier
2019-09-07 20:23:46 +02:00
out = self.r.run({"args":["msiexec", "/qn", "/i", os.path.join(temp, self.z_name)],"stream":True})
if out[2] != 0:
2019-10-15 21:08:02 +02:00
shutil.rmtree(temp,ignore_errors=True)
2019-09-07 20:23:46 +02:00
print("Error ({})".format(out[2]))
print("")
self.u.grab("Press [enter] to exit...")
exit(1)
2018-10-07 05:07:20 +02:00
print("")
2019-10-15 21:08:02 +02:00
self.z_path = self.z_path64 if os.path.exists(self.z_path64) else self.z_path32 if os.path.exists(self.z_path32) else None
return self.z_path and os.path.exists(self.z_path)
2018-10-07 05:07:20 +02:00
def check_bi(self):
# Checks for BOOTICEx64.exe in our scripts dir
# and downloads it if need be
if os.path.exists(os.path.join(self.s_path, self.bi_name)):
# print("Located {}!".format(self.bi_name))
# Got it
return True
print("Couldn't locate {} - downloading...".format(self.bi_name))
2019-07-01 16:00:49 +02:00
self.dl.stream_to_file(self.bi_url, os.path.join(self.s_path, self.bi_name))
print("")
return os.path.exists(os.path.join(self.s_path,self.bi_name))
2020-01-18 17:29:42 +01:00
def get_dl_url_from_json(self,json_data,suffix=".lzma"):
2019-11-14 23:02:45 +01:00
try: j_list = json.loads(json_data)
except: return None
j_list = j_list if isinstance(j_list,list) else [j_list]
for j in j_list:
2020-01-18 17:29:42 +01:00
dl_link = next((x.get("browser_download_url", None) for x in j.get("assets", []) if x.get("browser_download_url", "").lower().endswith(suffix.lower())), None)
if dl_link: break
if not dl_link:
return None
return { "url" : dl_link, "name" : os.path.basename(dl_link), "info" : j.get("body", None) }
2019-11-14 23:02:45 +01:00
def get_dl_info(self,clover_version=None):
# Returns the latest download package and info in a
# dictionary: { "url" : dl_url, "name" : name, "info" : update_info }
2019-12-21 05:03:21 +01:00
# Attempt Dids' repo first - falling back on Clover's official repo as needed
for url in (self.dids_url,self.clover_url):
2019-11-14 23:02:45 +01:00
# Tag is 5098 on Slice's repo, and v2.5k_r5098 on Dids' - accommodate as needed
search_url = url if clover_version == None else "{}/tags/{}".format(url,clover_version if url == self.clover_url else "v2.{}k_r{}".format(clover_version[0],clover_version))
print(" - Checking {}".format(search_url))
json_data = self.dl.get_string(search_url, False)
if not json_data: print(" --> Not found!")
else: return self.get_dl_url_from_json(json_data)
return None
2020-01-18 17:29:42 +01:00
def get_oc_dl_info(self):
json_data = self.dl.get_string(self.oc_url, False)
if not json_data: print(" --> Not found!")
else: return self.get_dl_url_from_json(json_data,"-RELEASE.zip")
def diskpart_flag(self, disk, as_efi=False):
# Sets and unsets the GUID needed for a GPT EFI partition ID
self.u.head("Changing ID With DiskPart")
print("")
print("Setting type as {}...".format("EFI" if as_efi else "Basic Data"))
print("")
# - EFI system partition: c12a7328-f81f-11d2-ba4b-00a0c93ec93b
# - Basic data partition: ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
dp_script = "\n".join([
"select disk {}".format(disk.get("index",-1)),
"sel part 1",
"set id={}".format(self.efi_id if as_efi else self.bas_id)
])
temp = tempfile.mkdtemp()
script = os.path.join(temp, "diskpart.txt")
try:
with open(script,"w") as f:
f.write(dp_script)
except:
shutil.rmtree(temp)
print("Error creating script!")
print("")
self.u.grab("Press [enter] to return...")
return
# Let's try to run it!
out = self.r.run({"args":[self.diskpart,"/s",script],"stream":True})
# Ditch our script regardless of whether diskpart worked or not
shutil.rmtree(temp)
print("")
if out[2] != 0:
# Error city!
print("DiskPart exited with non-zero status ({}). Aborting.".format(out[2]))
else:
print("Done - You may need to replug your drive for the")
print("changes to take effect.")
print("")
self.u.grab("Press [enter] to return...")
2019-11-14 23:02:45 +01:00
def diskpart_erase(self, disk, gpt=False, clover_version = None):
2018-10-07 05:07:20 +02:00
# Generate a script that we can pipe to diskpart to erase our disk
self.u.head("Erasing With DiskPart")
2018-10-07 05:07:20 +02:00
print("")
# Then we'll re-gather our disk info on success and move forward
# Using MBR to effectively set the individual partition types
# Keeps us from having issues mounting the EFI on Windows -
# and also lets us explicitly set the partition id for the main
# data partition.
if not gpt:
print("Using MBR...")
dp_script = "\n".join([
"select disk {}".format(disk.get("index",-1)),
"clean",
"convert mbr",
"create partition primary size=200",
2020-01-18 17:29:42 +01:00
"format quick fs=fat32 label='BOOT'",
"active",
2020-04-01 22:44:21 +02:00
"create partition primary",
"select part 2",
"set id=AB", # AF = HFS, AB = Recovery
"select part 1",
"assign"
])
else:
print("Using GPT...")
dp_script = "\n".join([
"select disk {}".format(disk.get("index",-1)),
"clean",
"convert gpt",
"create partition primary size=200",
2020-01-18 17:29:42 +01:00
"format quick fs=fat32 label='BOOT'",
"create partition primary id={}".format(self.hfs_id)
])
2018-10-07 05:07:20 +02:00
temp = tempfile.mkdtemp()
script = os.path.join(temp, "diskpart.txt")
try:
with open(script,"w") as f:
f.write(dp_script)
except:
shutil.rmtree(temp)
print("Error creating script!")
print("")
self.u.grab("Press [enter] to return...")
return
# Let's try to run it!
out = self.r.run({"args":[self.diskpart,"/s",script],"stream":True})
2018-10-07 05:07:20 +02:00
# Ditch our script regardless of whether diskpart worked or not
shutil.rmtree(temp)
if out[2] != 0:
# Error city!
print("")
print("DiskPart exited with non-zero status ({}). Aborting.".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
# We should now have a fresh drive to work with
# Let's write an image or something
self.u.head("Updating Disk Information")
print("")
print("Re-populating list...")
self.d.update()
print("Relocating disk {}".format(disk["index"]))
disk = self.d.disks[str(disk["index"])]
2019-11-14 23:02:45 +01:00
self.select_package(disk, clover_version)
2018-10-07 05:07:20 +02:00
2019-11-14 23:02:45 +01:00
def select_package(self, disk, clover_version = None):
2018-10-07 05:07:20 +02:00
self.u.head("Select Recovery Package")
print("")
print("{}. {} - {} ({})".format(
disk.get("index",-1),
disk.get("model","Unknown"),
self.dl.get_size(disk.get("size",-1),strip_zeroes=True),
2018-10-07 05:07:20 +02:00
["Unknown","No Root Dir","Removable","Local","Network","Disc","RAM Disk"][disk.get("type",0)]
))
print("")
print("M. Main Menu")
print("Q. Quit")
print("")
menu = self.u.grab("Please paste the recovery update pkg path to extract: ")
if menu.lower() == "q":
self.u.custom_quit()
if menu.lower() == "m":
return
path = self.u.check_path(menu)
if not path:
2020-01-18 17:29:42 +01:00
self.select_package(disk, clover_version)
2018-10-07 05:07:20 +02:00
return
# Got the package - let's make sure it's named right - just in case
2019-02-01 05:47:55 +01:00
if os.path.basename(path).lower().endswith(".hfs"):
2018-10-07 05:07:20 +02:00
# We have an hfs image already - bypass extraction
2020-02-10 04:07:40 +01:00
self.dd_image(disk, path, clover_version)
2018-10-07 05:07:20 +02:00
return
# If it's a directory, find the first recovery hit
if os.path.isdir(path):
for f in os.listdir(path):
if f.lower().endswith(self.recovery_suffixes):
path = os.path.join(path, f)
break
# Make sure it's named right for recovery stuffs
if not path.lower().endswith(self.recovery_suffixes):
self.u.head("Invalid Package")
print("")
print("{} is not in the available recovery package names:\n{}".format(os.path.basename(path), ", ".join(self.recovery_suffixes)))
print("")
print("Ensure you're passing a proper recovery package.")
print("")
self.u.grab("Press [enter] to return to package selection...")
2020-01-18 17:29:42 +01:00
self.select_package(disk, clover_version)
2018-10-07 05:07:20 +02:00
return
self.u.head("Extracting Package")
print("")
2018-10-07 05:07:20 +02:00
temp = tempfile.mkdtemp()
cwd = os.getcwd()
os.chdir(temp)
# Extract in sections and remove any files we run into
print("Extracting Recovery dmg...")
out = self.r.run({"args":[self.z_path, "e", "-txar", path, "*.dmg"]})
2018-10-07 05:07:20 +02:00
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print("An error occurred extracting: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
print("Extracting BaseSystem.dmg...")
2018-10-07 05:07:20 +02:00
# No files to delete here - let's extract the next part
out = self.r.run({"args":[self.z_path, "e", "*.dmg", "*/Base*.dmg"]})
2018-10-07 05:07:20 +02:00
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print("An error occurred extracting: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
# If we got here - we should delete everything in the temp folder except
# for a .dmg that starts with Base
del_list = [x for x in os.listdir(temp) if not (x.lower().startswith("base") and x.lower().endswith(".dmg"))]
for d in del_list:
os.remove(os.path.join(temp, d))
# Onto the last command
print("Extracting hfs...")
out = self.r.run({"args":[self.z_path, "e", "-tdmg", "Base*.dmg", "*.hfs"]})
2018-10-07 05:07:20 +02:00
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print("An error occurred extracting: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
# If we got here - we should delete everything in the temp folder except
# for a .dmg that starts with Base
del_list = [x for x in os.listdir(temp) if not x.lower().endswith(".hfs")]
for d in del_list:
os.remove(os.path.join(temp, d))
print("Extracted successfully!")
hfs = next((x for x in os.listdir(temp) if x.lower().endswith(".hfs")),None)
# Now to dd our image - if it exists
if not hfs:
print("Missing the .hfs file! Aborting.")
print("")
self.u.grab("Press [enter] to return...")
else:
2019-11-14 23:02:45 +01:00
self.dd_image(disk, os.path.join(temp, hfs), clover_version)
2018-10-07 05:07:20 +02:00
shutil.rmtree(temp,ignore_errors=True)
2019-11-14 23:02:45 +01:00
def dd_image(self, disk, image, clover_version = None):
2018-10-07 05:07:20 +02:00
# Let's dd the shit out of our disk
self.u.head("Copying Image To Drive")
print("")
print("Image: {}".format(image))
print("")
print("Disk {}. {} - {} ({})".format(
disk.get("index",-1),
disk.get("model","Unknown"),
self.dl.get_size(disk.get("size",-1),strip_zeroes=True),
2018-10-07 05:07:20 +02:00
["Unknown","No Root Dir","Removable","Local","Network","Disc","RAM Disk"][disk.get("type",0)]
))
print("")
args = [
os.path.join(self.s_path, self.dd_name),
"if={}".format(image),
"of=\\\\?\\Device\Harddisk{}\Partition2".format(disk.get("index",-1)),
"bs=8M",
"--progress"
]
print(" ".join(args))
print("")
print("This may take some time!")
print("")
out = self.r.run({"args":args})
if len(out[1].split("Error")) > 1:
# We had some error text - dd, even when failing likes to give us a 0
# status code. It also sends a ton of text through stderr - so we comb
# that for "Error" then split by that to skip the extra fluff and show only
# the error.
2018-10-07 05:07:20 +02:00
print("An error occurred:\n\n{}".format("Error"+out[1].split("Error")[1]))
print("")
self.u.grab("Press [enter] to return to the main menu...")
return
2020-01-18 17:29:42 +01:00
# Install Clover/OC to the target drive
if clover_version == "OpenCore": self.install_oc(disk)
else: self.install_clover(disk, clover_version)
def install_oc(self, disk):
self.u.head("Installing OpenCore")
print("")
print("Gathering info...")
o = self.get_oc_dl_info()
if o == None:
print(" - Error communicating with github!")
print("")
self.u.grab("Press [enter] to return...")
return
print(" - Got {}".format(o.get("name","Unknown Version")))
print("Downloading...")
temp = tempfile.mkdtemp()
os.chdir(temp)
oc_zip = o["name"]
self.dl.stream_to_file(o["url"], os.path.join(temp, o["name"]))
print("") # Empty space to clear the download progress
if not os.path.exists(os.path.join(temp, o["name"])):
shutil.rmtree(temp,ignore_errors=True)
print(" - Download failed. Aborting...")
print("")
self.u.grab("Press [enter] to return...")
return
# Got a valid file in our temp dir
print("Extracting {}...".format(oc_zip))
out = self.r.run({"args":[self.z_path, "x", os.path.join(temp,oc_zip)]})
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print(" - An error occurred extracting: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
# We need to also gather our boot, boot0af, and boot1f32 files
print("Gathering DUET boot files...")
for x in (self.oc_boot,self.oc_boot0,self.oc_boot1):
print(" - {}".format(x))
self.dl.stream_to_file(self.oc_boot_url + x, os.path.join(temp,x),False)
# At this point, we should have a boot0xx file and an EFI folder in the temp dir
# We need to udpate the disk list though - to reflect the current file system on part 1
# of our current disk
self.d.update() # assumes our disk number stays the same
# Some users are having issues with the "partitions" key not populating - possibly a 3rd party disk management soft?
# Possibly a bad USB?
# We'll see if the key exists - if not, we'll throw an error.
if self.d.disks[str(disk["index"])].get("partitions",None) == None:
# No partitions found.
shutil.rmtree(temp,ignore_errors=True)
print("No partitions located on disk!")
print("")
self.u.grab("Press [enter] to return...")
return
part = self.d.disks[str(disk["index"])]["partitions"].get("0",{}).get("letter",None) # get the first partition's letter
if part == None:
shutil.rmtree(temp,ignore_errors=True)
print("Lost original disk - or formatting failed!")
print("")
self.u.grab("Press [enter] to return...")
return
# Here we have our disk and partitions and such - the BOOT partition
# will be the first partition
# Let's copy over the EFI folder and then dd the boot0xx file
print("Copying EFI folder to {}/EFI...".format(part))
if os.path.exists("{}/EFI".format(part)):
print(" - EFI exists - removing...")
shutil.rmtree("{}/EFI".format(part),ignore_errors=True)
time.sleep(1) # Added because windows is dumb
shutil.copytree(os.path.join(temp,"EFI"), "{}/EFI".format(part))
# Copy boot over to the root of the EFI volume
print("Copying {} to {}/boot...".format(self.oc_boot,part))
shutil.copy(os.path.join(temp,self.oc_boot),"{}/boot".format(part))
# Use bootice to update the MBR and PBR - always on the first
# partition (which is 0 in bootice)
print("Updating the MBR with {}...".format(self.oc_boot0))
args = [
os.path.join(self.s_path,self.bi_name),
"/device={}".format(disk.get("index",-1)),
"/mbr",
"/restore",
"/file={}".format(os.path.join(temp,self.oc_boot0)),
"/keep_dpt",
"/quiet"
]
out = self.r.run({"args":args})
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print(" - An error occurred updating the MBR: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
print("Updating the PBR with {}...".format(self.oc_boot1))
args = [
os.path.join(self.s_path,self.bi_name),
"/device={}:0".format(disk.get("index",-1)),
"/pbr",
"/restore",
"/file={}".format(os.path.join(temp,self.oc_boot1)),
"/keep_bpb",
"/quiet"
]
out = self.r.run({"args":args})
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print(" - An error occurred updating the PBR: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
print("Cleaning up...")
shutil.rmtree(temp,ignore_errors=True)
print("")
print("Done.")
print("")
self.u.grab("Press [enter] to return to the main menu...")
2019-11-14 23:02:45 +01:00
def install_clover(self, disk, clover_version = None):
self.u.head("Installing Clover - {}".format("Latest" if not clover_version else "r"+clover_version))
print("")
print("Gathering info...")
2019-11-14 23:02:45 +01:00
c = self.get_dl_info(clover_version)
if c == None:
2019-11-14 23:02:45 +01:00
if clover_version == None: print(" - Error communicating with github!")
else: print(" - Error gathering info for Clover r{}".format(clover_version))
print("")
self.u.grab("Press [enter] to return...")
return
print(" - Got {}".format(c.get("name","Unknown Version")))
print("Downloading...")
temp = tempfile.mkdtemp()
os.chdir(temp)
clover_lzma = c["name"]
self.dl.stream_to_file(c["url"], os.path.join(temp, c["name"]))
print("") # Empty space to clear the download progress
if not os.path.exists(os.path.join(temp, c["name"])):
2019-10-15 21:08:02 +02:00
shutil.rmtree(temp,ignore_errors=True)
print(" - Download failed. Aborting...")
print("")
self.u.grab("Press [enter] to return...")
return
# Got a valid file in our temp dir
print("Extracting {}...".format(clover_lzma))
out = self.r.run({"args":[self.z_path, "e", os.path.join(temp,clover_lzma)]})
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print(" - An error occurred extracting: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
# Should result in a .tar file
clover_tar = next((x for x in os.listdir(temp) if x.lower().endswith(".tar")),None)
if not clover_tar:
2019-10-15 21:08:02 +02:00
shutil.rmtree(temp,ignore_errors=True)
print(" - No .tar archive found - aborting...")
print("")
self.u.grab("Press [enter] to return...")
return
# Got the .tar archive - get the .iso
print("Extracting {}...".format(clover_tar))
out = self.r.run({"args":[self.z_path, "e", os.path.join(temp,clover_tar)]})
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print(" - An error occurred extracting: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
# Should result in a .iso file
clover_iso = next((x for x in os.listdir(temp) if x.lower().endswith(".iso")),None)
if not clover_tar:
2019-10-15 21:08:02 +02:00
shutil.rmtree(temp,ignore_errors=True)
print(" - No .iso found - aborting...")
print("")
self.u.grab("Press [enter] to return...")
return
# Got the .iso - let's extract the needed parts
print("Extracting EFI from {}...".format(clover_iso))
out = self.r.run({"args":[self.z_path, "x", os.path.join(temp,clover_iso), "EFI*"]})
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print(" - An error occurred extracting: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
print("Extracting {} from {}...".format(self.boot0,clover_iso))
out = self.r.run({"args":[self.z_path, "e", os.path.join(temp,clover_iso), self.boot0, "-r"]})
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print(" - An error occurred extracting: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
print("Extracting {} from {}...".format(self.boot1,clover_iso))
out = self.r.run({"args":[self.z_path, "e", os.path.join(temp,clover_iso), self.boot1, "-r"]})
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print(" - An error occurred extracting: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
print("Extracting {} from {}...".format(self.boot,clover_iso))
out = self.r.run({"args":[self.z_path, "e", os.path.join(temp,clover_iso), self.boot, "-r"]})
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print(" - An error occurred extracting: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
# At this point, we should have a boot0xx file and an EFI folder in the temp dir
# We need to udpate the disk list though - to reflect the current file system on part 1
# of our current disk
self.d.update() # assumes our disk number stays the same
# Some users are having issues with the "partitions" key not populating - possibly a 3rd party disk management soft?
# Possibly a bad USB?
# We'll see if the key exists - if not, we'll throw an error.
if self.d.disks[str(disk["index"])].get("partitions",None) == None:
# No partitions found.
shutil.rmtree(temp,ignore_errors=True)
print("No partitions located on disk!")
print("")
self.u.grab("Press [enter] to return...")
return
part = self.d.disks[str(disk["index"])]["partitions"].get("0",{}).get("letter",None) # get the first partition's letter
if part == None:
shutil.rmtree(temp,ignore_errors=True)
print("Lost original disk - or formatting failed!")
print("")
self.u.grab("Press [enter] to return...")
return
# Here we have our disk and partitions and such - the CLOVER partition
# will be the first partition
# Let's copy over the EFI folder and then dd the boot0xx file
print("Copying EFI folder to {}/EFI...".format(part))
if os.path.exists("{}/EFI".format(part)):
print(" - EFI exists - removing...")
shutil.rmtree("{}/EFI".format(part),ignore_errors=True)
time.sleep(1) # Added because windows is dumb
shutil.copytree(os.path.join(temp,"EFI"), "{}/EFI".format(part))
# Copy boot6 over to the root of the EFI volume - and rename it to boot
print("Copying {} to {}/boot...".format(self.boot,part))
shutil.copy(os.path.join(temp,self.boot),"{}/boot".format(part))
# Use bootice to update the MBR and PBR - always on the first
# partition (which is 0 in bootice)
print("Updating the MBR with {}...".format(self.boot0))
args = [
os.path.join(self.s_path,self.bi_name),
"/device={}".format(disk.get("index",-1)),
"/mbr",
"/restore",
"/file={}".format(os.path.join(temp,self.boot0)),
"/keep_dpt",
"/quiet"
]
out = self.r.run({"args":args})
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print(" - An error occurred updating the MBR: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
print("Updating the PBR with {}...".format(self.boot1))
args = [
os.path.join(self.s_path,self.bi_name),
"/device={}:0".format(disk.get("index",-1)),
"/pbr",
"/restore",
"/file={}".format(os.path.join(temp,self.boot1)),
"/keep_bpb",
"/quiet"
]
out = self.r.run({"args":args})
if out[2] != 0:
shutil.rmtree(temp,ignore_errors=True)
print(" - An error occurred updating the PBR: {}".format(out[2]))
print("")
self.u.grab("Press [enter] to return...")
return
print("Cleaning up...")
shutil.rmtree(temp,ignore_errors=True)
print("")
print("Done.")
2018-10-07 05:07:20 +02:00
print("")
self.u.grab("Press [enter] to return to the main menu...")
def main(self):
# Start out with our cd in the right spot.
os.chdir(os.path.dirname(os.path.realpath(__file__)))
2018-10-07 05:07:20 +02:00
# Let's make sure we have the required files needed
self.u.head("Checking Required Tools")
print("")
if not self.check_dd():
2019-10-15 21:08:02 +02:00
print("Couldn't find or install {} - aborting!\n".format(self.dd_name))
self.u.grab("Press [enter] to exit...")
2018-10-07 05:07:20 +02:00
exit(1)
if not self.check_7z():
2019-10-15 21:08:02 +02:00
print("Couldn't find or install {} - aborting!\n".format(self.z_name))
self.u.grab("Press [enter] to exit...")
2018-10-07 05:07:20 +02:00
exit(1)
if not self.check_bi():
2019-10-15 21:08:02 +02:00
print("Couldn't find or install {} - aborting!\n".format(self.bi_name))
self.u.grab("Press [enter] to exit...")
exit(1)
2018-10-07 05:07:20 +02:00
# Let's just setup a real simple interface and try to write some data
self.u.head("Gathering Disk Info")
print("")
print("Populating list...")
self.d.update()
print("")
print("Done!")
# Let's serve up a list of *only* removable media
self.u.head("Potential Removable Media")
print("")
2019-09-22 23:25:47 +02:00
rem_disks = self.get_disks_of_type(self.d.disks) if not self.show_all_disks else self.d.disks
2018-10-07 05:07:20 +02:00
# Types: 0 = Unknown, 1 = No Root Dir, 2 = Removable, 3 = Local, 4 = Network, 5 = Disc, 6 = RAM disk
2019-09-22 23:25:47 +02:00
if self.show_all_disks:
print("!WARNING! This list includes ALL disk types.")
print("!WARNING! Be ABSOLUTELY sure before selecting")
print("!WARNING! a disk!")
else:
print("!WARNING! This list includes both Removable AND")
print("!WARNING! Unknown disk types. Be ABSOLUTELY sure")
print("!WARNING! before selecting a disk!")
2018-10-07 05:07:20 +02:00
print("")
2019-10-19 22:25:15 +02:00
for disk in sorted(rem_disks,key=lambda x:int(x)):
2018-10-07 05:07:20 +02:00
print("{}. {} - {} ({})".format(
disk,
rem_disks[disk].get("model","Unknown"),
self.dl.get_size(rem_disks[disk].get("size",-1),strip_zeroes=True),
2018-10-07 05:07:20 +02:00
["Unknown","No Root Dir","Removable","Local","Network","Disc","RAM Disk"][rem_disks[disk].get("type",0)]
))
if not len(rem_disks[disk].get("partitions",{})):
print(" No Mounted Partitions")
else:
parts = rem_disks[disk]["partitions"]
2019-10-19 22:25:15 +02:00
for p in sorted(parts,key=lambda x:int(x)):
2018-10-07 05:07:20 +02:00
print(" {}. {} ({}) {} - {}".format(
p,
parts[p].get("letter","No Letter"),
"No Name" if not parts[p].get("name",None) else parts[p].get("name","No Name"),
parts[p].get("file system","Unknown FS"),
self.dl.get_size(parts[p].get("size",-1),strip_zeroes=True)
2018-10-07 05:07:20 +02:00
))
print("")
print("Q. Quit")
print("")
2019-11-14 23:02:45 +01:00
print("Usage: [drive number][option (only one allowed)] r[Clover revision (optional)]\n (eg. 1C r5092)")
print(" Options are as follows with precedence C > E > U > G:")
print(" C = Only install Clover to the drive's first partition.")
2020-01-18 17:29:42 +01:00
print(" O = Use OpenCore instead of Clover.")
print(" I = Only install OpenCore to the drive's first partition.")
print(" E = Sets the type of the drive's first partition to EFI.")
print(" U = Similar to E, but sets the type to Basic Data (useful for editing).")
print(" G = Format as GPT (default is MBR).")
2019-09-22 23:25:47 +02:00
print(" D = Used without a drive number, toggles showing all disks.")
print("")
menu = self.u.grab("Please select a disk or press [enter] with no options to refresh: ")
2018-10-07 05:07:20 +02:00
if not len(menu):
self.main()
return
if menu.lower() == "q":
self.u.custom_quit()
2019-09-22 23:25:47 +02:00
if menu.lower() == "d":
self.show_all_disks ^= True
self.main()
return
2020-01-18 17:29:42 +01:00
only_clover = use_oc = set_efi = unset_efi = use_gpt = False
if "c" in menu.lower():
only_clover = True
menu = menu.lower().replace("c","")
2020-01-18 17:29:42 +01:00
if "i" in menu.lower():
only_clover = use_oc = True
menu = menu.lower().replace("i","")
if "o" in menu.lower():
use_oc = True
menu = menu.lower().replace("o","")
if "e" in menu.lower():
set_efi = True
menu = menu.lower().replace("e","")
if "u" in menu.lower():
unset_efi = True
menu = menu.lower().replace("u","")
if "g" in menu.lower():
use_gpt = True
menu = menu.lower().replace("g","")
2019-11-14 23:02:45 +01:00
# Extract Clover version from args if found
clover_list = [x for x in menu.split() if x.lower().startswith("r") and all(y in "0123456789" for y in x[1:])]
menu = " ".join([x for x in menu.split() if not x in clover_list])
clover_version = None if not len(clover_list) else clover_list[0][1:] # Skip the "r" prefix
2020-01-18 17:29:42 +01:00
# Prepare for OC if need be
if use_oc: clover_version = "OpenCore"
2019-11-14 23:02:45 +01:00
selected_disk = rem_disks.get(menu.strip(),None)
2018-10-07 05:07:20 +02:00
if not selected_disk:
self.u.head("Invalid Choice")
print("")
print("Disk {} is not an option.".format(menu))
print("")
self.u.grab("Returning in 5 seconds...", timeout=5)
self.main()
return
# Got a disk!
if only_clover:
2020-01-18 17:29:42 +01:00
if use_oc: self.install_oc(selected_disk)
else: self.install_clover(selected_disk, clover_version)
elif set_efi:
self.diskpart_flag(selected_disk, True)
elif unset_efi:
self.diskpart_flag(selected_disk, False)
else:
# Check erase
while True:
self.u.head("Erase {}".format(selected_disk.get("model","Unknown")))
print("")
print("{}. {} - {} ({})".format(
selected_disk.get("index",-1),
selected_disk.get("model","Unknown"),
self.dl.get_size(selected_disk.get("size",-1),strip_zeroes=True),
["Unknown","No Root Dir","Removable","Local","Network","Disc","RAM Disk"][selected_disk.get("type",0)]
))
print("")
print("If you continue - THIS DISK WILL BE ERASED")
print("ALL DATA WILL BE LOST AND ALL PARTITIONS WILL")
print("BE REMOVED!!!!!!!")
print("")
yn = self.u.grab("Continue? (y/n): ")
if yn.lower() == "n":
self.main()
return
if yn.lower() == "y":
break
# Got the OK to erase! Let's format a diskpart script!
2019-11-14 23:02:45 +01:00
self.diskpart_erase(selected_disk, use_gpt, clover_version)
2018-10-07 05:07:20 +02:00
self.main()
if __name__ == '__main__':
w = WinUSB()
w.main()