Merge remote-tracking branch 'upstream/main'

# Conflicts:
#	modules/download_wii.py
#	qt/py/ui_AboutDialog.py
This commit is contained in:
2025-05-25 01:09:32 -04:00
26 changed files with 1020 additions and 202 deletions

View File

@@ -11,6 +11,8 @@ from libWiiPy.title.ticket import _TitleLimit
def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_wad_chkbox: bool, keep_enc_chkbox: bool,
decrypt_contents_chkbox: bool, wiiu_nus_chkbox: bool, use_local_chkbox: bool,
repack_vwii_chkbox: bool, patch_ios: bool, wad_file_name: str, progress_callback=None):
def progress_update(done, total):
progress_callback.emit(done, total, None)
# Actual NUS download function that runs in a separate thread.
# Immediately knock out any invalidly formatted Title IDs.
if len(tid) != 16:
@@ -33,16 +35,16 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_
title_dir.mkdir(exist_ok=True)
# Announce the title being downloaded, and the version if applicable.
if title_version is not None:
progress_callback.emit(f"Downloading title {tid} v{title_version}, please wait...")
progress_callback.emit(0, 0, f"Downloading title {tid} v{title_version}, please wait...")
else:
progress_callback.emit(f"Downloading title {tid} vLatest, please wait...")
progress_callback.emit(" - Downloading and parsing TMD...")
progress_callback.emit(-1, -1, f"Downloading title {tid} vLatest, please wait...")
progress_callback.emit(-1, -1, " - Downloading and parsing TMD...")
# Download a specific TMD version if a version was specified, otherwise just download the latest TMD.
try:
if title_version is not None:
title.load_tmd(libWiiPy.title.download_tmd(tid, title_version, wiiu_endpoint=wiiu_nus_enabled))
title.load_tmd(libWiiPy.title.download_tmd(tid, title_version, wiiu_endpoint=wiiu_nus_enabled, progress=progress_update))
else:
title.load_tmd(libWiiPy.title.download_tmd(tid, wiiu_endpoint=wiiu_nus_enabled))
title.load_tmd(libWiiPy.title.download_tmd(tid, wiiu_endpoint=wiiu_nus_enabled, progress=progress_update))
title_version = title.tmd.title_version
# If libWiiPy returns an error, that means that either the TID or version doesn't exist, so return code -2.
except ValueError:
@@ -55,17 +57,17 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_
# Use a local ticket, if one exists and "use local files" is enabled.
forge_ticket = False
if use_local_chkbox and version_dir.joinpath("tik").exists():
progress_callback.emit(" - Parsing local copy of Ticket...")
progress_callback.emit(-1, -1, " - Parsing local copy of Ticket...")
title.load_ticket(version_dir.joinpath("tik").read_bytes())
else:
progress_callback.emit(" - Downloading and parsing Ticket...")
progress_callback.emit(-1, -1, " - Downloading and parsing Ticket...")
try:
title.load_ticket(libWiiPy.title.download_ticket(tid, wiiu_endpoint=wiiu_nus_enabled))
title.load_ticket(libWiiPy.title.download_ticket(tid, wiiu_endpoint=wiiu_nus_enabled, progress=progress_update))
version_dir.joinpath("tik").write_bytes(title.ticket.dump())
except ValueError:
# If libWiiPy returns an error, then no ticket is available. Try to forge a ticket after we download the
# content.
progress_callback.emit(" - No Ticket is available! Will try forging a Ticket.")
progress_callback.emit(0, 0, " - No Ticket is available! Will try forging a Ticket.")
forge_ticket = True
# Load the content records from the TMD, and begin iterating over the records.
title.load_content_records()
@@ -75,15 +77,15 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_
content_file_name = f"{title.tmd.content_records[content].content_id:08X}"
# Check for a local copy of the current content if "use local files" is enabled, and use it.
if use_local_chkbox is True and version_dir.joinpath(content_file_name).exists():
progress_callback.emit(f" - Using local copy of content {content + 1} of {len(title.tmd.content_records)}")
progress_callback.emit(-1, -1, f" - Using local copy of content {content + 1} of {len(title.tmd.content_records)}")
content_list.append(version_dir.joinpath(content_file_name).read_bytes())
else:
progress_callback.emit(f" - Downloading content {content + 1} of {len(title.tmd.content_records)} "
progress_callback.emit(0, 0, f" - Downloading content {content + 1} of {len(title.tmd.content_records)} "
f"(Content ID: {title.tmd.content_records[content].content_id}, Size: "
f"{title.tmd.content_records[content].content_size} bytes)...")
content_list.append(libWiiPy.title.download_content(tid, title.tmd.content_records[content].content_id,
wiiu_endpoint=wiiu_nus_enabled))
progress_callback.emit(" - Done!")
wiiu_endpoint=wiiu_nus_enabled, progress=progress_update))
progress_callback.emit(-1, -1, " - Done!")
# If keep encrypted contents is on, write out each content after its downloaded.
if keep_enc_chkbox is True:
version_dir.joinpath(content_file_name).write_bytes(content_list[content])
@@ -125,7 +127,7 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_
if decrypt_contents_enabled is True:
try:
for content in range(len(title.tmd.content_records)):
progress_callback.emit(f" - Decrypting content {content + 1} of {len(title.tmd.content_records)} "
progress_callback.emit(-1, -1, f" - Decrypting content {content + 1} of {len(title.tmd.content_records)} "
f"(Content ID: {title.tmd.content_records[content].content_id})...")
dec_content = title.get_content_by_index(content)
content_file_name = f"{title.tmd.content_records[content].content_id:08X}.app"
@@ -140,15 +142,15 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_
# re-encrypted with the common key instead of the vWii key, so that the title can be installed from within
# vWii mode. (vWii mode does not have access to the vWii key, only Wii U mode has that.)
if repack_vwii_chkbox is True and (tid[3] == "7" or tid[7] == "7"):
progress_callback.emit(" - Re-encrypting Title Key with the common key...")
progress_callback.emit(-1, -1, " - Re-encrypting Title Key with the common key...")
title_key_common = libWiiPy.title.encrypt_title_key(title.ticket.get_title_key(), 0, title.tmd.title_id)
title.ticket.common_key_index = 0
title.ticket.title_key_enc = title_key_common
# Get the WAD certificate chain, courtesy of libWiiPy.
progress_callback.emit(" - Building certificate...")
progress_callback.emit(-1, -1, " - Building certificate...")
title.load_cert_chain(libWiiPy.title.download_cert_chain(wiiu_endpoint=wiiu_nus_enabled))
# Use a typed WAD name if there is one, and auto generate one based on the TID and version if there isn't.
progress_callback.emit(" - Packing WAD...")
progress_callback.emit(-1, -1, " - Packing WAD...")
if wad_file_name != "" and wad_file_name is not None:
# Batch downloads may insert -vLatest, so if it did we can fill in the real number now.
wad_file_name = wad_file_name.replace("-vLatest", f"-v{title_version}")
@@ -158,20 +160,24 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_
wad_file_name = f"{tid}-v{title_version}.wad"
# If enabled (after we make sure it's an IOS), apply all main IOS patches.
if patch_ios and (tid[:8] == "00000001" and int(tid[-2:], 16) > 2):
progress_callback.emit(" - Patching IOS...")
progress_callback.emit(-1, -1, " - Patching IOS...")
ios_patcher = libWiiPy.title.IOSPatcher()
ios_patcher.load(title)
patch_count = ios_patcher.patch_all()
if patch_count > 0:
progress_callback.emit(f" - Applied {patch_count} patches!")
progress_callback.emit(-1, -1, f" - Applied {patch_count} patches!")
else:
progress_callback.emit(" - No patches could be applied! Is this a stub IOS?")
progress_callback.emit(-1, -1, " - No patches could be applied! Is this a stub IOS?")
title = ios_patcher.dump()
# Append "-PATCHED" to the end of the WAD file name to make it clear that it was modified.
wad_file_name = wad_file_name[:-4] + "-PATCHED" + wad_file_name[-4:]
# Certain special characters are prone to breaking things, so strip them from the file name before actually
# opening the file for writing. On some platforms (like macOS), invalid characters get replaced automatically,
# but on Windows the file will just fail to be written out at all.
wad_file_name = wad_file_name.translate({ord(c): None for c in '/\\:*"?<>|'})
# Have libWiiPy dump the WAD, and write that data out.
version_dir.joinpath(wad_file_name).write_bytes(title.dump_wad())
progress_callback.emit("Download complete!")
progress_callback.emit(0, 1, "Download complete!")
# This is where the variables come in. If the state of these variables doesn't match the user's choice by this
# point, it means that they enabled decryption or WAD packing for a title that doesn't have a ticket. Return
# code 1 so that a warning popup is shown informing them of this.