Added the ability to use the Wii U NUS servers to nus.py

This commit is contained in:
Campbell 2024-05-01 22:22:30 -04:00
parent 4a3e9f8e7f
commit 99a55a3de5
Signed by: NinjaCheetah
GPG Key ID: B547958AF96ED344
2 changed files with 49 additions and 19 deletions

View File

@ -1,6 +1,6 @@
[project] [project]
name = "libWiiPy" name = "libWiiPy"
version = "0.2.2" version = "0.2.3"
authors = [ authors = [
{ name="NinjaCheetah", email="ninjacheetah@ncxprogramming.com" }, { name="NinjaCheetah", email="ninjacheetah@ncxprogramming.com" },
{ name="Lillian Skinner", email="lillian@randommeaninglesscharacters.com" } { name="Lillian Skinner", email="lillian@randommeaninglesscharacters.com" }

View File

@ -11,8 +11,10 @@ from .title import Title
from .tmd import TMD from .tmd import TMD
from .ticket import Ticket from .ticket import Ticket
nus_endpoint = ["http://nus.cdn.shop.wii.com/ccs/download/", "http://ccs.cdn.wup.shop.nintendo.net/ccs/download/"]
def download_title(title_id: str, title_version: int = None) -> Title:
def download_title(title_id: str, title_version: int = None, fallback_endpoint: bool = False) -> Title:
""" """
Download an entire title and all of its contents, then load the downloaded components into a Title object for Download an entire title and all of its contents, then load the downloaded components into a Title object for
further use. This method is NOT recommended for general use, as it has absolutely no verbosity. It is instead further use. This method is NOT recommended for general use, as it has absolutely no verbosity. It is instead
@ -24,6 +26,8 @@ def download_title(title_id: str, title_version: int = None) -> Title:
The Title ID of the title to download. The Title ID of the title to download.
title_version : int, option title_version : int, option
The version of the title to download. Defaults to latest if not set. The version of the title to download. Defaults to latest if not set.
fallback_endpoint : bool, option
Whether the fallback Wii U endpoint for the NUS should be used or not. Defaults to False.
Returns Returns
------- -------
@ -33,17 +37,17 @@ def download_title(title_id: str, title_version: int = None) -> Title:
# First, create the new title. # First, create the new title.
title = Title() title = Title()
# Download and load the TMD, Ticket, and certs. # Download and load the TMD, Ticket, and certs.
title.load_tmd(download_tmd(title_id, title_version)) title.load_tmd(download_tmd(title_id, title_version, fallback_endpoint))
title.load_ticket(download_ticket(title_id)) title.load_ticket(download_ticket(title_id, fallback_endpoint))
title.wad.set_cert_data(download_cert()) title.wad.set_cert_data(download_cert(fallback_endpoint))
# Download all contents # Download all contents
title.load_content_records() title.load_content_records()
title.content.content_list = download_contents(title_id, title.tmd) title.content.content_list = download_contents(title_id, title.tmd, fallback_endpoint)
# Return the completed title. # Return the completed title.
return title return title
def download_tmd(title_id: str, title_version: int = None) -> bytes: def download_tmd(title_id: str, title_version: int = None, fallback_endpoint: bool = False) -> bytes:
""" """
Downloads the TMD of the Title specified in the object. Will download the latest version by default, or another Downloads the TMD of the Title specified in the object. Will download the latest version by default, or another
version if it was manually specified in the object. version if it was manually specified in the object.
@ -54,6 +58,8 @@ def download_tmd(title_id: str, title_version: int = None) -> bytes:
The Title ID of the title to download the TMD for. The Title ID of the title to download the TMD for.
title_version : int, option title_version : int, option
The version of the TMD to download. Defaults to latest if not set. The version of the TMD to download. Defaults to latest if not set.
fallback_endpoint : bool, option
Whether the fallback Wii U endpoint for the NUS should be used or not. Defaults to False.
Returns Returns
------- -------
@ -62,7 +68,10 @@ def download_tmd(title_id: str, title_version: int = None) -> bytes:
""" """
# Build the download URL. The structure is download/<TID>/tmd for latest and download/<TID>/tmd.<version> for # Build the download URL. The structure is download/<TID>/tmd for latest and download/<TID>/tmd.<version> for
# when a specific version is requested. # when a specific version is requested.
tmd_url = "http://nus.cdn.shop.wii.com/ccs/download/" + title_id + "/tmd" if fallback_endpoint is False:
tmd_url = nus_endpoint[0] + title_id + "/tmd"
else:
tmd_url = nus_endpoint[1] + title_id + "/tmd"
# Add the version to the URL if one was specified. # Add the version to the URL if one was specified.
if title_version is not None: if title_version is not None:
tmd_url += "." + str(title_version) tmd_url += "." + str(title_version)
@ -81,7 +90,7 @@ def download_tmd(title_id: str, title_version: int = None) -> bytes:
return tmd return tmd
def download_ticket(title_id: str) -> bytes: def download_ticket(title_id: str, fallback_endpoint: bool = False) -> bytes:
""" """
Downloads the Ticket of the Title specified in the object. This will only work if the Title ID specified is for Downloads the Ticket of the Title specified in the object. This will only work if the Title ID specified is for
a free title. a free title.
@ -90,6 +99,8 @@ def download_ticket(title_id: str) -> bytes:
---------- ----------
title_id : str title_id : str
The Title ID of the title to download the Ticket for. The Title ID of the title to download the Ticket for.
fallback_endpoint : bool, option
Whether the fallback Wii U endpoint for the NUS should be used or not. Defaults to False.
Returns Returns
------- -------
@ -98,7 +109,10 @@ def download_ticket(title_id: str) -> bytes:
""" """
# Build the download URL. The structure is download/<TID>/cetk, and cetk will only exist if this is a free # Build the download URL. The structure is download/<TID>/cetk, and cetk will only exist if this is a free
# title. # title.
ticket_url = "http://nus.cdn.shop.wii.com/ccs/download/" + title_id + "/cetk" if fallback_endpoint is False:
ticket_url = nus_endpoint[0] + title_id + "/cetk"
else:
ticket_url = nus_endpoint[1] + title_id + "/cetk"
# Make the request. # Make the request.
ticket_request = requests.get(url=ticket_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True) ticket_request = requests.get(url=ticket_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True)
if ticket_request.status_code != 200: if ticket_request.status_code != 200:
@ -113,20 +127,29 @@ def download_ticket(title_id: str) -> bytes:
return ticket return ticket
def download_cert() -> bytes: def download_cert(fallback_endpoint: bool = False) -> bytes:
""" """
Downloads the signing certificate used by all WADs. This uses System Menu 4.3U as the source. Downloads the signing certificate used by all WADs. This uses System Menu 4.3U as the source.
Parameters
----------
fallback_endpoint : bool, option
Whether the fallback Wii U endpoint for the NUS should be used or not. Defaults to False.
Returns Returns
------- -------
bytes bytes
The cert file. The cert file.
""" """
# Download the TMD and cetk for the System Menu 4.3U. # Download the TMD and cetk for the System Menu 4.3U.
tmd = requests.get(url='http://nus.cdn.shop.wii.com/ccs/download/0000000100000002/tmd.513', if fallback_endpoint is False:
headers={'User-Agent': 'wii libnup/1.0'}, stream=True).content tmd_url = nus_endpoint[0] + "0000000100000002/tmd.513"
cetk = requests.get(url='http://nus.cdn.shop.wii.com/ccs/download/0000000100000002/cetk', cetk_url = nus_endpoint[0] + "0000000100000002/cetk"
headers={'User-Agent': 'wii libnup/1.0'}, stream=True).content else:
tmd_url = nus_endpoint[1] + "0000000100000002/tmd.513"
cetk_url = nus_endpoint[1] + "0000000100000002/cetk"
tmd = requests.get(url=tmd_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True).content
cetk = requests.get(url=cetk_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True).content
# Assemble the certificate. # Assemble the certificate.
with io.BytesIO() as cert_data: with io.BytesIO() as cert_data:
# Certificate Authority data. # Certificate Authority data.
@ -143,7 +166,7 @@ def download_cert() -> bytes:
return cert return cert
def download_content(title_id: str, content_id: int) -> bytes: def download_content(title_id: str, content_id: int, fallback_endpoint: bool = False) -> bytes:
""" """
Downloads a specified content for the title specified in the object. Downloads a specified content for the title specified in the object.
@ -153,6 +176,8 @@ def download_content(title_id: str, content_id: int) -> bytes:
The Title ID of the title to download content from. The Title ID of the title to download content from.
content_id : int content_id : int
The Content ID of the content you wish to download. The Content ID of the content you wish to download.
fallback_endpoint : bool, option
Whether the fallback Wii U endpoint for the NUS should be used or not. Defaults to False.
Returns Returns
------- -------
@ -163,7 +188,10 @@ def download_content(title_id: str, content_id: int) -> bytes:
content_id_hex = hex(content_id)[2:] content_id_hex = hex(content_id)[2:]
if len(content_id_hex) < 2: if len(content_id_hex) < 2:
content_id_hex = "0" + content_id_hex content_id_hex = "0" + content_id_hex
content_url = "http://nus.cdn.shop.wii.com/ccs/download/" + title_id + "/000000" + content_id_hex if fallback_endpoint is False:
content_url = nus_endpoint[0] + title_id + "/000000" + content_id_hex
else:
content_url = nus_endpoint[1] + title_id + "/000000" + content_id_hex
# Make the request. # Make the request.
content_request = requests.get(url=content_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True) content_request = requests.get(url=content_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True)
if content_request.status_code != 200: if content_request.status_code != 200:
@ -174,7 +202,7 @@ def download_content(title_id: str, content_id: int) -> bytes:
return content_data return content_data
def download_contents(title_id: str, tmd: TMD) -> List[bytes]: def download_contents(title_id: str, tmd: TMD, fallback_endpoint: bool = False) -> List[bytes]:
""" """
Downloads all the contents for the title specified in the object. This requires a TMD to already be available Downloads all the contents for the title specified in the object. This requires a TMD to already be available
so that the content records can be accessed. so that the content records can be accessed.
@ -185,6 +213,8 @@ def download_contents(title_id: str, tmd: TMD) -> List[bytes]:
The Title ID of the title to download content from. The Title ID of the title to download content from.
tmd : TMD tmd : TMD
The TMD that matches the title that the contents being downloaded are from. The TMD that matches the title that the contents being downloaded are from.
fallback_endpoint : bool, option
Whether the fallback Wii U endpoint for the NUS should be used or not. Defaults to False.
Returns Returns
------- -------
@ -201,6 +231,6 @@ def download_contents(title_id: str, tmd: TMD) -> List[bytes]:
content_list = [] content_list = []
for content_id in content_ids: for content_id in content_ids:
# Call self.download_content() for each Content ID. # Call self.download_content() for each Content ID.
content = download_content(title_id, content_id) content = download_content(title_id, content_id, fallback_endpoint)
content_list.append(content) content_list.append(content)
return content_list return content_list