diff --git a/modules/nus.py b/modules/nus.py index 8a83aa6..bd0d292 100644 --- a/modules/nus.py +++ b/modules/nus.py @@ -2,6 +2,7 @@ # https://github.com/NinjaCheetah/WiiPy import os +import hashlib import pathlib import libWiiPy @@ -134,3 +135,55 @@ def handle_nus_title(args): file.close() print("Downloaded title with Title ID \"" + args.tid + "\"!") + + +def handle_nus_content(args): + tid = args.tid + cid = args.cid + version = args.version + if args.decrypt: + decrypt_content = True + else: + decrypt_content = False + + content_file_name = hex(cid)[2:] + while len(content_file_name) < 8: + content_file_name = "0" + content_file_name + + content_data = libWiiPy.title.download_content(tid, cid) + + if decrypt_content is True: + content_file_name = content_file_name + ".app" + tmd = libWiiPy.title.TMD() + tmd.load(libWiiPy.title.download_tmd(tid, version)) + try: + ticket = libWiiPy.title.Ticket() + ticket.load(libWiiPy.title.download_ticket(tid)) + except ValueError: + print(" - No Ticket is available!") + return + + content_hash = 'gggggggggggggggggggggggggggggggggggggggg' + content_size = 0 + content_index = 0 + for record in tmd.content_records: + if record.content_id == cid: + content_hash = record.content_hash.decode() + content_size = record.content_size + content_index = record.index + + content_dec = libWiiPy.title.decrypt_content(content_data, ticket.get_title_key(), content_index, content_size) + content_dec_hash = hashlib.sha1(content_dec).hexdigest() + if content_hash != content_dec_hash: + raise ValueError("The decrypted content provided does not match the record at the provided index. \n" + "Expected hash is: {}\n".format(content_hash) + + "Actual hash is: {}".format(content_dec_hash)) + file = open(content_file_name, "wb") + file.write(content_dec) + file.close() + else: + file = open(content_file_name, "wb") + file.write(content_data) + file.close() + + print("Downloaded content with Content ID \"" + str(cid) + "\"!") diff --git a/wiipy.py b/wiipy.py index 7bb2095..be58790 100644 --- a/wiipy.py +++ b/wiipy.py @@ -48,6 +48,15 @@ if __name__ == "__main__": 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", action="store_true") + # Content NUS subcommand. + nus_content_parser = nus_subparsers.add_parser("content", help="download a specific content from the NUS", + description="download a specific content from the NUS") + nus_content_parser.set_defaults(func=handle_nus_content) + nus_content_parser.add_argument("tid", metavar="TID", type=str, help="Title ID the content belongs to") + nus_content_parser.add_argument("cid", metavar="CID", type=int, help="Content ID to download") + nus_content_parser.add_argument("-v", "--version", metavar="VERSION", type=int, + help="version this content belongs to (required for decryption)") + nus_content_parser.add_argument("-d", "--decrypt", action="store_true", help="decrypt this content") # Argument parser for the U8 subcommand. u8_parser = subparsers.add_parser("u8", help="pack/unpack a U8 archive",