Joined all WAD re-encryption commands into "wad convert"

This commit is contained in:
Campbell 2024-10-27 11:33:12 -04:00
parent a4c06cae36
commit 530afd4189
Signed by: NinjaCheetah
GPG Key ID: 670C282B3291D63D
4 changed files with 99 additions and 98 deletions

View File

@ -55,9 +55,11 @@ def build_cios(args):
base_version = int(target_base.get("version"))
if title.tmd.title_version != base_version:
raise ValueError("The provided base IOS does not match the required version for this base!")
print(f"Building cIOS \"{args.cios_ver}\" from base IOS{target_base.get('ios')} v{base_version}...")
# We're ready to begin building the cIOS now. Find all the <content> tags that have <patch> tags, and then apply
# the patches listed in them to the content.
print("Patching existing modules...")
for content in target_base.findall("content"):
patches = content.findall("patch")
if patches:
@ -90,6 +92,7 @@ def build_cios(args):
title.set_content(dec_content, content_index, content_type=libWiiPy.title.ContentType.NORMAL)
# Next phase of cIOS building is to add the required extra modules.
print("Adding required additional modules...")
for content in target_base.findall("content"):
target_module = content.get("module")
if target_module is not None:
@ -122,6 +125,7 @@ def build_cios(args):
title.set_title_version(args.version)
except ValueError:
raise ValueError(f"The provided version \"{args.version}\" is not valid!")
print(f"Set cIOS slot to \"{slot}\" and cIOS version to \"{args.version}\"!")
# If this is a vWii cIOS, then we need to re-encrypt it with the Wii Common key so that it's installable from
# within Wii mode.
@ -134,8 +138,6 @@ def build_cios(args):
title.fakesign()
# Write the new cIOS to the specified output path.
out_file = open(output_path, "wb")
out_file.write(title.dump_wad())
out_file.close()
output_path.write_bytes(title.dump_wad())
print("success")
print(f"Successfully built cIOS \"{args.cios_ver}\"!")

View File

@ -88,7 +88,10 @@ def _print_ticket_info(ticket: libWiiPy.title.Ticket):
print(f" Certificate Info: {ticket.signature_issuer} (Unknown)")
match ticket.common_key_index:
case 0:
key = "Common"
if ticket.is_dev:
key = "Common (Development)"
else:
key = "Common (Retail)"
case 1:
key = "Korean"
case 2:

View File

@ -68,6 +68,75 @@ def handle_wad_add(args):
print(f"Successfully added new content with Content ID \"{target_cid:08X}\" and type \"{target_type.name}\"!")
def handle_wad_convert(args):
input_path = pathlib.Path(args.input)
if args.dev:
target = "development"
elif args.retail:
target = "retail"
elif args.vwii:
target = "vWii"
else:
raise ValueError("No valid target was provided!")
if args.output is None:
match target:
case "development":
output_path = pathlib.Path(input_path.stem + "_dev" + input_path.suffix)
case "retail":
output_path = pathlib.Path(input_path.stem + "_retail" + input_path.suffix)
case "vWii":
output_path = pathlib.Path(input_path.stem + "_vWii" + input_path.suffix)
case _:
raise ValueError("No valid target was provided!")
else:
output_path = pathlib.Path(args.output)
if not input_path.exists():
raise FileNotFoundError(input_path)
title = libWiiPy.title.Title()
title.load_wad(input_path.read_bytes())
# First, verify that this WAD isn't already the type we're trying to convert to.
if title.ticket.is_dev and target == "development":
raise ValueError("This is already a development WAD!")
elif not title.ticket.is_dev and not title.tmd.vwii and target == "retail":
raise ValueError("This is already a retail WAD!")
elif not title.ticket.is_dev and title.tmd.vwii and target == "vWii":
raise ValueError("This is already a vWii WAD!")
# Get the current type to display later.
if title.ticket.is_dev:
source = "development"
elif title.tmd.vwii:
source = "vWii"
else:
source = "retail"
# Extract the Title Key so it can be re-encrypted with the correct key later.
title_key = title.ticket.get_title_key()
title_key_new = None
if target == "development":
# Set the development signature info.
title.ticket.signature_issuer = "Root-CA00000002-XS00000006" + title.ticket.signature_issuer[26:]
title.tmd.signature_issuer = "Root-CA00000002-CP00000007" + title.tmd.signature_issuer[26:]
# Re-encrypt the title key with the dev key, and set that in the Ticket.
title_key_new = libWiiPy.title.encrypt_title_key(title_key, 0, title.ticket.title_id, True)
title.ticket.common_key_index = 0
else:
# Set the retail signature info.
title.ticket.signature_issuer = "Root-CA00000001-XS00000003" + title.ticket.signature_issuer[26:]
title.tmd.signature_issuer = "Root-CA00000001-CP00000004" + title.tmd.signature_issuer[26:]
if target == "retail":
title_key_new = libWiiPy.title.encrypt_title_key(title_key, 0, title.ticket.title_id, False)
title.ticket.common_key_index = 0
elif target == "vWii":
title_key_new = libWiiPy.title.encrypt_title_key(title_key, 2, title.ticket.title_id, False)
title.ticket.common_key_index = 2
title.ticket.title_key_enc = title_key_new
title.fakesign()
output_path.write_bytes(title.dump_wad())
print(f"Successfully converted {source} WAD to {target} WAD \"{output_path.name}\"!")
def handle_wad_pack(args):
input_path = pathlib.Path(args.input)
output_path = pathlib.Path(args.output)
@ -299,77 +368,3 @@ def handle_wad_unpack(args):
output_path.joinpath(content_file_name).write_bytes(title.get_content_by_index(content_file, skip_hash))
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()
output_path.write_bytes(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()
output_path.write_bytes(title.dump_wad())
print(f"Successfully converted retail WAD to development WAD \"{output_path.name}\"!")
def handle_wad_v2w(args):
input_path = pathlib.Path(args.input)
output_path = pathlib.Path(input_path.stem + "_Wii" + 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 vWii WAD.
if title.ticket.common_key_index != 2:
raise ValueError("This is not a vWii WAD!")
# Now, extract the Title Key, change the required flags in the TMD/tik, and set the new common key encrypted
# Title Key. The WAD will also be fakesigned, since making this change invalidates the signature, so there's no
# downside.
title_key_dec = title.ticket.get_title_key()
title_key_common = libWiiPy.title.encrypt_title_key(title_key_dec, 0, title.tmd.title_id)
title.ticket.title_key_enc = title_key_common
title.ticket.common_key_index = 0
title.fakesign()
output_path.write_bytes(title.dump_wad())
print(f"Successfully re-encrypted vWii WAD to regular WAD \"{output_path.name}\"!")

View File

@ -224,12 +224,26 @@ 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")
# Convert WAD subcommand.
wad_convert_parser = wad_subparsers.add_parser("convert", help="re-encrypt a WAD file with a different key",
description="re-encrypt a WAD file with a different key, making it "
"possible to use the WAD in a different environment; "
"this fakesigns the WAD by default")
wad_convert_parser.set_defaults(func=handle_wad_convert)
wad_convert_parser.add_argument("input", metavar="IN", type=str, help="WAD file to re-encrypt")
wad_convert_targets_lbl = wad_convert_parser.add_argument_group(title="target keys")
wad_convert_targets = wad_convert_targets_lbl.add_mutually_exclusive_group(required=True)
wad_convert_targets.add_argument("-d", "--dev", action="store_true",
help="re-encrypt the WAD with the development common key, allowing it to be "
"installed on development consoles")
wad_convert_targets.add_argument("-r", "--retail", action="store_true",
help="re-encrypt the WAD with the retail common key, allowing it to be installed "
"on retail consoles or inside of Dolphin")
wad_convert_targets.add_argument("-v", "--vwii", action="store_true",
help="re-encrypt the WAD with the vWii key, allowing it to theoretically be "
"installed from Wii U mode if a Wii U mode WAD installer is created")
wad_convert_parser.add_argument("-o", "--output", metavar="OUT", type=str,
help="file to output the new WAD to (optional, defaults to '<old_name>_<key>.wad')")
# 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")
@ -252,12 +266,6 @@ 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 "
@ -284,13 +292,6 @@ if __name__ == "__main__":
wad_unpack_parser.add_argument("output", metavar="OUT", type=str, help="output directory")
wad_unpack_parser.add_argument("-s", "--skip-hash", help="skips validating the hashes of decrypted "
"content", action="store_true")
# vwii2wii WAD subcommand.
wad_v2w_parser = wad_subparsers.add_parser("vwii2wii", help="re-encrypt a vWii WAD with the common key",
description="re-encrypt a vWii WAD with the common key, allowing it to "
"be installed in Dolphin and from within Wii mode on Wii U; "
"this also fakesigns the WAD")
wad_v2w_parser.set_defaults(func=handle_wad_v2w)
wad_v2w_parser.add_argument("input", metavar="IN", type=str, help="vWii WAD file to re-encrypt")
# Parse all the args, and call the appropriate function with all of those args if a valid subcommand was passed.