mirror of
https://github.com/NinjaCheetah/WiiPy.git
synced 2025-04-26 13:21:01 -04:00
Allow for custom endpoints for NUS downloads
This commit is contained in:
parent
1410dd6c36
commit
71c0726c4f
@ -43,6 +43,9 @@ def _print_tmd_info(tmd: libWiiPy.title.TMD, signing_cert=None):
|
|||||||
elif tmd.signature_issuer.find("CP00000007") != -1:
|
elif tmd.signature_issuer.find("CP00000007") != -1:
|
||||||
print(f" Certificate: CP00000007 (Development)")
|
print(f" Certificate: CP00000007 (Development)")
|
||||||
print(f" Certificate Issuer: Root-CA00000002 (Development)")
|
print(f" Certificate Issuer: Root-CA00000002 (Development)")
|
||||||
|
elif tmd.signature_issuer.find("CP00000005") != -1:
|
||||||
|
print(f" Certificate: CP00000005 (Development/Unknown)")
|
||||||
|
print(f" Certificate Issuer: Root-CA00000002 (Development)")
|
||||||
elif tmd.signature_issuer.find("CP10000000") != -1:
|
elif tmd.signature_issuer.find("CP10000000") != -1:
|
||||||
print(f" Certificate: CP10000000 (Arcade)")
|
print(f" Certificate: CP10000000 (Arcade)")
|
||||||
print(f" Certificate Issuer: Root-CA10000000 (Arcade)")
|
print(f" Certificate Issuer: Root-CA10000000 (Arcade)")
|
||||||
@ -128,6 +131,9 @@ def _print_ticket_info(ticket: libWiiPy.title.Ticket, signing_cert=None):
|
|||||||
elif ticket.signature_issuer.find("XS00000006") != -1:
|
elif ticket.signature_issuer.find("XS00000006") != -1:
|
||||||
print(f" Certificate: XS00000006 (Development)")
|
print(f" Certificate: XS00000006 (Development)")
|
||||||
print(f" Certificate Issuer: Root-CA00000002 (Development)")
|
print(f" Certificate Issuer: Root-CA00000002 (Development)")
|
||||||
|
elif ticket.signature_issuer.find("XS00000004") != -1:
|
||||||
|
print(f" Certificate: XS00000004 (Development/Unknown)")
|
||||||
|
print(f" Certificate Issuer: Root-CA00000002 (Development)")
|
||||||
else:
|
else:
|
||||||
print(f" Certificate Info: {ticket.signature_issuer} (Unknown)")
|
print(f" Certificate Info: {ticket.signature_issuer} (Unknown)")
|
||||||
match ticket.common_key_index:
|
match ticket.common_key_index:
|
||||||
|
@ -8,119 +8,6 @@ import libWiiPy
|
|||||||
from modules.core import fatal_error
|
from modules.core import fatal_error
|
||||||
|
|
||||||
|
|
||||||
def handle_nus_title(args):
|
|
||||||
title_version = None
|
|
||||||
wad_file = None
|
|
||||||
output_dir = None
|
|
||||||
can_decrypt = False
|
|
||||||
tid = args.tid
|
|
||||||
if args.wii:
|
|
||||||
wiiu_nus_enabled = False
|
|
||||||
else:
|
|
||||||
wiiu_nus_enabled = True
|
|
||||||
|
|
||||||
# Check if --version was passed, because it'll be None if it wasn't.
|
|
||||||
if args.version is not None:
|
|
||||||
try:
|
|
||||||
title_version = int(args.version)
|
|
||||||
except ValueError:
|
|
||||||
fatal_error("The specified Title Version must be a valid integer!")
|
|
||||||
|
|
||||||
# If --wad was passed, check to make sure the path is okay.
|
|
||||||
if args.wad is not None:
|
|
||||||
wad_file = pathlib.Path(args.wad)
|
|
||||||
if wad_file.suffix != ".wad":
|
|
||||||
wad_file = wad_file.with_suffix(".wad")
|
|
||||||
|
|
||||||
# If --output was passed, make sure the directory either doesn't exist or is empty.
|
|
||||||
if args.output is not None:
|
|
||||||
output_dir = pathlib.Path(args.output)
|
|
||||||
if output_dir.exists():
|
|
||||||
if output_dir.is_file():
|
|
||||||
fatal_error("A file already exists with the provided directory name!")
|
|
||||||
else:
|
|
||||||
output_dir.mkdir()
|
|
||||||
|
|
||||||
# Download the title from the NUS. This is done "manually" (as opposed to using download_title()) so that we can
|
|
||||||
# provide verbose output.
|
|
||||||
title = libWiiPy.title.Title()
|
|
||||||
|
|
||||||
# Announce the title being downloaded, and the version if applicable.
|
|
||||||
if title_version is not None:
|
|
||||||
print(f"Downloading title {tid} v{title_version}, please wait...")
|
|
||||||
else:
|
|
||||||
print(f"Downloading title {tid} vLatest, please wait...")
|
|
||||||
print(" - Downloading and parsing TMD...")
|
|
||||||
# Download a specific TMD version if a version was specified, otherwise just download the latest TMD.
|
|
||||||
if title_version is not None:
|
|
||||||
title.load_tmd(libWiiPy.title.download_tmd(tid, title_version, wiiu_endpoint=wiiu_nus_enabled))
|
|
||||||
else:
|
|
||||||
title.load_tmd(libWiiPy.title.download_tmd(tid, wiiu_endpoint=wiiu_nus_enabled))
|
|
||||||
title_version = title.tmd.title_version
|
|
||||||
# Write out the TMD to a file.
|
|
||||||
if output_dir is not None:
|
|
||||||
output_dir.joinpath(f"tmd.{title_version}").write_bytes(title.tmd.dump())
|
|
||||||
|
|
||||||
# Download the ticket, if we can.
|
|
||||||
print(" - Downloading and parsing Ticket...")
|
|
||||||
try:
|
|
||||||
title.load_ticket(libWiiPy.title.download_ticket(tid, wiiu_endpoint=wiiu_nus_enabled))
|
|
||||||
can_decrypt = True
|
|
||||||
if output_dir is not None:
|
|
||||||
output_dir.joinpath("tik").write_bytes(title.ticket.dump())
|
|
||||||
except ValueError:
|
|
||||||
# If libWiiPy returns an error, then no ticket is available. Log this, and disable options requiring a
|
|
||||||
# ticket so that they aren't attempted later.
|
|
||||||
print(" - No Ticket is available!")
|
|
||||||
if wad_file is not None and output_dir is None:
|
|
||||||
fatal_error("--wad was passed, but this title has no common ticket and cannot be packed into a WAD!")
|
|
||||||
|
|
||||||
# Load the content records from the TMD, and begin iterating over the records.
|
|
||||||
title.load_content_records()
|
|
||||||
content_list = []
|
|
||||||
for content in range(len(title.tmd.content_records)):
|
|
||||||
# Generate the content file name by converting the Content ID to hex and then removing the 0x.
|
|
||||||
content_file_name = hex(title.tmd.content_records[content].content_id)[2:]
|
|
||||||
while len(content_file_name) < 8:
|
|
||||||
content_file_name = "0" + content_file_name
|
|
||||||
print(f" - Downloading content {content + 1} of {len(title.tmd.content_records)} "
|
|
||||||
f"(Content ID: {title.tmd.content_records[content].content_id}, "
|
|
||||||
f"Size: {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))
|
|
||||||
print(" - Done!")
|
|
||||||
# If we're supposed to be outputting to a folder, then write these files out.
|
|
||||||
if output_dir is not None:
|
|
||||||
output_dir.joinpath(content_file_name).write_bytes(content_list[content])
|
|
||||||
title.content.content_list = content_list
|
|
||||||
|
|
||||||
# Try to decrypt the contents for this title if a ticket was available.
|
|
||||||
if output_dir is not None:
|
|
||||||
if can_decrypt is True:
|
|
||||||
for content in range(len(title.tmd.content_records)):
|
|
||||||
print(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}".lower() + ".app"
|
|
||||||
output_dir.joinpath(content_file_name).write_bytes(dec_content)
|
|
||||||
else:
|
|
||||||
print("Title has no Ticket, so content will not be decrypted!")
|
|
||||||
|
|
||||||
# If --wad was passed, pack a WAD and output that.
|
|
||||||
if wad_file is not None:
|
|
||||||
# Get the WAD certificate chain.
|
|
||||||
print(" - Building certificate...")
|
|
||||||
title.load_cert_chain(libWiiPy.title.download_cert_chain(wiiu_endpoint=wiiu_nus_enabled))
|
|
||||||
# Ensure that the path ends in .wad, and add that if it doesn't.
|
|
||||||
print("Packing WAD...")
|
|
||||||
if wad_file.suffix != ".wad":
|
|
||||||
wad_file = wad_file.with_suffix(".wad")
|
|
||||||
# Have libWiiPy dump the WAD, and write that data out.
|
|
||||||
pathlib.Path(wad_file).write_bytes(title.dump_wad())
|
|
||||||
|
|
||||||
print(f"Downloaded title with Title ID \"{args.tid}\"!")
|
|
||||||
|
|
||||||
|
|
||||||
def handle_nus_content(args):
|
def handle_nus_content(args):
|
||||||
tid = args.tid
|
tid = args.tid
|
||||||
cid = args.cid
|
cid = args.cid
|
||||||
@ -198,6 +85,122 @@ def handle_nus_content(args):
|
|||||||
print(f"Downloaded content with Content ID \"{cid}\"!")
|
print(f"Downloaded content with Content ID \"{cid}\"!")
|
||||||
|
|
||||||
|
|
||||||
|
def handle_nus_title(args):
|
||||||
|
title_version = None
|
||||||
|
wad_file = None
|
||||||
|
output_dir = None
|
||||||
|
can_decrypt = False
|
||||||
|
tid = args.tid
|
||||||
|
wiiu_nus_enabled = False if args.wii else True
|
||||||
|
endpoint_override = args.endpoint if args.endpoint else None
|
||||||
|
|
||||||
|
# Check if --version was passed, because it'll be None if it wasn't.
|
||||||
|
if args.version is not None:
|
||||||
|
try:
|
||||||
|
title_version = int(args.version)
|
||||||
|
except ValueError:
|
||||||
|
fatal_error("The specified Title Version must be a valid integer!")
|
||||||
|
|
||||||
|
# If --wad was passed, check to make sure the path is okay.
|
||||||
|
if args.wad is not None:
|
||||||
|
wad_file = pathlib.Path(args.wad)
|
||||||
|
if wad_file.suffix != ".wad":
|
||||||
|
wad_file = wad_file.with_suffix(".wad")
|
||||||
|
|
||||||
|
# If --output was passed, make sure the directory either doesn't exist or is empty.
|
||||||
|
if args.output is not None:
|
||||||
|
output_dir = pathlib.Path(args.output)
|
||||||
|
if output_dir.exists():
|
||||||
|
if output_dir.is_file():
|
||||||
|
fatal_error("A file already exists with the provided directory name!")
|
||||||
|
else:
|
||||||
|
output_dir.mkdir()
|
||||||
|
|
||||||
|
# Download the title from the NUS. This is done "manually" (as opposed to using download_title()) so that we can
|
||||||
|
# provide verbose output.
|
||||||
|
title = libWiiPy.title.Title()
|
||||||
|
|
||||||
|
# Announce the title being downloaded, and the version if applicable.
|
||||||
|
if title_version is not None:
|
||||||
|
print(f"Downloading title {tid} v{title_version}, please wait...")
|
||||||
|
else:
|
||||||
|
print(f"Downloading title {tid} vLatest, please wait...")
|
||||||
|
print(" - Downloading and parsing TMD...")
|
||||||
|
# Download a specific TMD version if a version was specified, otherwise just download the latest TMD.
|
||||||
|
if title_version is not None:
|
||||||
|
title.load_tmd(libWiiPy.title.download_tmd(tid, title_version, wiiu_endpoint=wiiu_nus_enabled,
|
||||||
|
endpoint_override=endpoint_override))
|
||||||
|
else:
|
||||||
|
title.load_tmd(libWiiPy.title.download_tmd(tid, wiiu_endpoint=wiiu_nus_enabled,
|
||||||
|
endpoint_override=endpoint_override))
|
||||||
|
title_version = title.tmd.title_version
|
||||||
|
# Write out the TMD to a file.
|
||||||
|
if output_dir is not None:
|
||||||
|
output_dir.joinpath(f"tmd.{title_version}").write_bytes(title.tmd.dump())
|
||||||
|
|
||||||
|
# Download the ticket, if we can.
|
||||||
|
print(" - Downloading and parsing Ticket...")
|
||||||
|
try:
|
||||||
|
title.load_ticket(libWiiPy.title.download_ticket(tid, wiiu_endpoint=wiiu_nus_enabled,
|
||||||
|
endpoint_override=endpoint_override))
|
||||||
|
can_decrypt = True
|
||||||
|
if output_dir is not None:
|
||||||
|
output_dir.joinpath("tik").write_bytes(title.ticket.dump())
|
||||||
|
except ValueError:
|
||||||
|
# If libWiiPy returns an error, then no ticket is available. Log this, and disable options requiring a
|
||||||
|
# ticket so that they aren't attempted later.
|
||||||
|
print(" - No Ticket is available!")
|
||||||
|
if wad_file is not None and output_dir is None:
|
||||||
|
fatal_error("--wad was passed, but this title has no common ticket and cannot be packed into a WAD!")
|
||||||
|
|
||||||
|
# Load the content records from the TMD, and begin iterating over the records.
|
||||||
|
title.load_content_records()
|
||||||
|
content_list = []
|
||||||
|
for content in range(len(title.tmd.content_records)):
|
||||||
|
# Generate the content file name by converting the Content ID to hex and then removing the 0x.
|
||||||
|
content_file_name = hex(title.tmd.content_records[content].content_id)[2:]
|
||||||
|
while len(content_file_name) < 8:
|
||||||
|
content_file_name = "0" + content_file_name
|
||||||
|
print(f" - Downloading content {content + 1} of {len(title.tmd.content_records)} "
|
||||||
|
f"(Content ID: {title.tmd.content_records[content].content_id}, "
|
||||||
|
f"Size: {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,
|
||||||
|
endpoint_override=endpoint_override))
|
||||||
|
print(" - Done!")
|
||||||
|
# If we're supposed to be outputting to a folder, then write these files out.
|
||||||
|
if output_dir is not None:
|
||||||
|
output_dir.joinpath(content_file_name).write_bytes(content_list[content])
|
||||||
|
title.content.content_list = content_list
|
||||||
|
|
||||||
|
# Try to decrypt the contents for this title if a ticket was available.
|
||||||
|
if output_dir is not None:
|
||||||
|
if can_decrypt is True:
|
||||||
|
for content in range(len(title.tmd.content_records)):
|
||||||
|
print(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}".lower() + ".app"
|
||||||
|
output_dir.joinpath(content_file_name).write_bytes(dec_content)
|
||||||
|
else:
|
||||||
|
print("Title has no Ticket, so content will not be decrypted!")
|
||||||
|
|
||||||
|
# If --wad was passed, pack a WAD and output that.
|
||||||
|
if wad_file is not None:
|
||||||
|
# Get the WAD certificate chain.
|
||||||
|
print(" - Building certificate...")
|
||||||
|
title.load_cert_chain(libWiiPy.title.download_cert_chain(wiiu_endpoint=wiiu_nus_enabled,
|
||||||
|
endpoint_override=endpoint_override))
|
||||||
|
# Ensure that the path ends in .wad, and add that if it doesn't.
|
||||||
|
print("Packing WAD...")
|
||||||
|
if wad_file.suffix != ".wad":
|
||||||
|
wad_file = wad_file.with_suffix(".wad")
|
||||||
|
# Have libWiiPy dump the WAD, and write that data out.
|
||||||
|
pathlib.Path(wad_file).write_bytes(title.dump_wad())
|
||||||
|
|
||||||
|
print(f"Downloaded title with Title ID \"{args.tid}\"!")
|
||||||
|
|
||||||
|
|
||||||
def handle_nus_tmd(args):
|
def handle_nus_tmd(args):
|
||||||
tid = args.tid
|
tid = args.tid
|
||||||
|
|
||||||
|
@ -81,7 +81,6 @@ def handle_wad_convert(args):
|
|||||||
else:
|
else:
|
||||||
fatal_error("No valid encryption target was specified!")
|
fatal_error("No valid encryption target was specified!")
|
||||||
|
|
||||||
output_path = pathlib.Path(args.output)
|
|
||||||
if args.output is None:
|
if args.output is None:
|
||||||
match target:
|
match target:
|
||||||
case "development":
|
case "development":
|
||||||
@ -92,6 +91,8 @@ def handle_wad_convert(args):
|
|||||||
output_path = pathlib.Path(input_path.stem + "_vWii" + input_path.suffix)
|
output_path = pathlib.Path(input_path.stem + "_vWii" + input_path.suffix)
|
||||||
case _:
|
case _:
|
||||||
fatal_error("No valid encryption target was specified!")
|
fatal_error("No valid encryption target was specified!")
|
||||||
|
else:
|
||||||
|
output_path = pathlib.Path(args.output)
|
||||||
|
|
||||||
if not input_path.exists():
|
if not input_path.exists():
|
||||||
fatal_error(f"The specified WAD file \"{input_path}\" does not exist!")
|
fatal_error(f"The specified WAD file \"{input_path}\" does not exist!")
|
||||||
|
4
wiipy.py
4
wiipy.py
@ -186,8 +186,10 @@ if __name__ == "__main__":
|
|||||||
help="download the title to a folder")
|
help="download the title to a folder")
|
||||||
nus_title_out_group.add_argument("-w", "--wad", metavar="WAD", type=str,
|
nus_title_out_group.add_argument("-w", "--wad", metavar="WAD", type=str,
|
||||||
help="pack a wad with the provided name")
|
help="pack a wad with the provided name")
|
||||||
nus_title_parser.add_argument("--wii", help="use original Wii NUS instead of the Wii U servers",
|
nus_title_parser.add_argument("--wii", help="use the original Wii NUS endpoint instead of the Wii U endpoint",
|
||||||
action="store_true")
|
action="store_true")
|
||||||
|
nus_title_parser.add_argument("-e", "--endpoint", metavar="ENDPOINT", type=str,
|
||||||
|
help="use the specified NUS endpoint instead of the official one")
|
||||||
# Content NUS subcommand.
|
# Content NUS subcommand.
|
||||||
nus_content_parser = nus_subparsers.add_parser("content", help="download a specific content from the NUS",
|
nus_content_parser = nus_subparsers.add_parser("content", help="download a specific content from the NUS",
|
||||||
description="download a specific content from the NUS")
|
description="download a specific content from the NUS")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user