diff --git a/modules/title/wad.py b/modules/title/wad.py index 61f2698..7027e8e 100644 --- a/modules/title/wad.py +++ b/modules/title/wad.py @@ -332,3 +332,53 @@ def handle_wad_unpack(args): content_out.close() print("WAD file unpacked!") + + +def handle_wad_d2r(args): + input_path = pathlib.Path(args.input) + output_path = pathlib.Path(input_path.stem + "_retail" + input_path.suffix) + + if not input_path.exists(): + raise FileNotFoundError(input_path) + + title = libWiiPy.title.Title() + title.load_wad(input_path.read_bytes()) + # First, verify that this IS actually a dev WAD. + if not title.ticket.is_dev: + raise ValueError("This is not a development WAD!") + # Now, extract the Title Key, change the certs in the TMD/tik, and set the new retail-encrypted Title Key. The certs + # need to be changed so that this WAD can be identified as retail later. The WAD will also be fakesigned, since + # making this change invalidates the signature, so there's no downside. + title_key = title.ticket.get_title_key() + title.ticket.signature_issuer = "Root-CA00000001-XS00000003" + title.ticket.signature_issuer[26:] + title_key_retail = libWiiPy.title.encrypt_title_key(title_key, 0, title.ticket.title_id, False) + title.ticket.title_key_enc = title_key_retail + title.tmd.signature_issuer = "Root-CA00000001-CP00000004" + title.tmd.signature_issuer[26:] + title.fakesign() + open(output_path, "wb").write(title.dump_wad()) + print(f"Successfully converted development WAD to retail WAD \"{output_path.name}\"!") + + +def handle_wad_r2d(args): + input_path = pathlib.Path(args.input) + output_path = pathlib.Path(input_path.stem + "_dev" + input_path.suffix) + + if not input_path.exists(): + raise FileNotFoundError(input_path) + + title = libWiiPy.title.Title() + title.load_wad(input_path.read_bytes()) + # First, verify that this IS actually a retail WAD. + if title.ticket.is_dev: + raise ValueError("This is not a retail WAD!") + # Now, extract the Title Key, change the certs in the TMD/tik, and set the new dev-encrypted Title Key. The certs + # need to be changed so that this WAD can be identified as dev later. The WAD will also be fakesigned, since making + # this change invalidates the signature, so there's no downside. + title_key = title.ticket.get_title_key() + title.ticket.signature_issuer = "Root-CA00000002-XS00000006" + title.ticket.signature_issuer[26:] + title_key_dev = libWiiPy.title.encrypt_title_key(title_key, 0, title.ticket.title_id, True) + title.ticket.title_key_enc = title_key_dev + title.tmd.signature_issuer = "Root-CA00000002-CP00000007" + title.tmd.signature_issuer[26:] + title.fakesign() + open(output_path, "wb").write(title.dump_wad()) + print(f"Successfully converted retail WAD to development WAD \"{output_path.name}\"!") diff --git a/wiipy.py b/wiipy.py index f759594..5702278 100644 --- a/wiipy.py +++ b/wiipy.py @@ -207,6 +207,12 @@ if __name__ == "__main__": "(optional, will default to \"Normal\" if not specified)") wad_add_parser.add_argument("-o", "--output", metavar="OUT", type=str, help="file to output the updated WAD to (optional)") + # dev2retail WAD subcommand. + wad_d2r_parser = wad_subparsers.add_parser("dev2retail", help="re-encrypt a dev WAD for retail consoles", + description="re-encrypt a dev WAD for retail consoles, and update" + "the certs to match; this also fakesigns the WAD") + wad_d2r_parser.set_defaults(func=handle_wad_d2r) + wad_d2r_parser.add_argument("input", metavar="IN", type=str, help="dev WAD file to re-encrypt") # Pack WAD subcommand. wad_pack_parser = wad_subparsers.add_parser("pack", help="pack a directory to a WAD file", description="pack a directory to a WAD file") @@ -229,6 +235,12 @@ if __name__ == "__main__": help="Content ID of the content to remove") wad_remove_parser.add_argument("-o", "--output", metavar="OUT", type=str, help="file to output the updated WAD to (optional)") + # retail2dev WAD subcommand. + wad_r2d_parser = wad_subparsers.add_parser("retail2dev", help="re-encrypt a retail WAD for development consoles", + description="re-encrypt a retail WAD for development consoles, and " + "update the certs to match; this also fakesigns the WAD") + wad_r2d_parser.set_defaults(func=handle_wad_r2d) + wad_r2d_parser.add_argument("input", metavar="IN", type=str, help="retail WAD file to re-encrypt") # Set WAD subcommand. wad_set_parser = wad_subparsers.add_parser("set", help="set content in a WAD file", description="replace existing content in a WAD file with new decrypted "