From 99a55a3de5a440e52de79273ab26b74036bcc3c5 Mon Sep 17 00:00:00 2001 From: NinjaCheetah <58050615+NinjaCheetah@users.noreply.github.com> Date: Wed, 1 May 2024 22:22:30 -0400 Subject: [PATCH] Added the ability to use the Wii U NUS servers to nus.py --- pyproject.toml | 2 +- src/libWiiPy/nus.py | 66 ++++++++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8dbbe8e..5205905 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "libWiiPy" -version = "0.2.2" +version = "0.2.3" authors = [ { name="NinjaCheetah", email="ninjacheetah@ncxprogramming.com" }, { name="Lillian Skinner", email="lillian@randommeaninglesscharacters.com" } diff --git a/src/libWiiPy/nus.py b/src/libWiiPy/nus.py index bc76008..51881cf 100644 --- a/src/libWiiPy/nus.py +++ b/src/libWiiPy/nus.py @@ -11,8 +11,10 @@ from .title import Title from .tmd import TMD 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 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. title_version : int, option 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 ------- @@ -33,17 +37,17 @@ def download_title(title_id: str, title_version: int = None) -> Title: # First, create the new title. title = Title() # Download and load the TMD, Ticket, and certs. - title.load_tmd(download_tmd(title_id, title_version)) - title.load_ticket(download_ticket(title_id)) - title.wad.set_cert_data(download_cert()) + title.load_tmd(download_tmd(title_id, title_version, fallback_endpoint)) + title.load_ticket(download_ticket(title_id, fallback_endpoint)) + title.wad.set_cert_data(download_cert(fallback_endpoint)) # Download all contents 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 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 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. title_version : int, option 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 ------- @@ -62,7 +68,10 @@ def download_tmd(title_id: str, title_version: int = None) -> bytes: """ # Build the download URL. The structure is download//tmd for latest and download//tmd. for # 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. if title_version is not None: tmd_url += "." + str(title_version) @@ -81,7 +90,7 @@ def download_tmd(title_id: str, title_version: int = None) -> bytes: 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 a free title. @@ -90,6 +99,8 @@ def download_ticket(title_id: str) -> bytes: ---------- title_id : str 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 ------- @@ -98,7 +109,10 @@ def download_ticket(title_id: str) -> bytes: """ # Build the download URL. The structure is download//cetk, and cetk will only exist if this is a free # 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. ticket_request = requests.get(url=ticket_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True) if ticket_request.status_code != 200: @@ -113,20 +127,29 @@ def download_ticket(title_id: str) -> bytes: 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. + Parameters + ---------- + fallback_endpoint : bool, option + Whether the fallback Wii U endpoint for the NUS should be used or not. Defaults to False. + Returns ------- bytes The cert file. """ # 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', - headers={'User-Agent': 'wii libnup/1.0'}, stream=True).content - cetk = requests.get(url='http://nus.cdn.shop.wii.com/ccs/download/0000000100000002/cetk', - headers={'User-Agent': 'wii libnup/1.0'}, stream=True).content + if fallback_endpoint is False: + tmd_url = nus_endpoint[0] + "0000000100000002/tmd.513" + cetk_url = nus_endpoint[0] + "0000000100000002/cetk" + 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. with io.BytesIO() as cert_data: # Certificate Authority data. @@ -143,7 +166,7 @@ def download_cert() -> bytes: 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. @@ -153,6 +176,8 @@ def download_content(title_id: str, content_id: int) -> bytes: The Title ID of the title to download content from. content_id : int 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 ------- @@ -163,7 +188,10 @@ def download_content(title_id: str, content_id: int) -> bytes: content_id_hex = hex(content_id)[2:] if len(content_id_hex) < 2: 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. content_request = requests.get(url=content_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True) if content_request.status_code != 200: @@ -174,7 +202,7 @@ def download_content(title_id: str, content_id: int) -> bytes: 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 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. tmd : TMD 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 ------- @@ -201,6 +231,6 @@ def download_contents(title_id: str, tmd: TMD) -> List[bytes]: content_list = [] for content_id in content_ids: # 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) return content_list