From b82b6f3873c6ed402618ecf5cb68a3ef3d203077 Mon Sep 17 00:00:00 2001 From: NinjaCheetah <58050615+NinjaCheetah@users.noreply.github.com> Date: Mon, 22 Jul 2024 20:11:33 -0400 Subject: [PATCH] Add module for directly fakesigning a TMD, Ticket, or WAD --- .gitignore | 3 ++ Makefile | 3 ++ modules/{ => archive}/ash.py | 2 +- modules/{ => archive}/u8.py | 2 +- modules/title/fakesign.py | 34 +++++++++++++++++++ modules/{ => title}/nus.py | 2 +- modules/{ => title}/wad.py | 13 ++++---- wiipy.py | 64 ++++++++++++++++++++---------------- 8 files changed, 85 insertions(+), 38 deletions(-) rename modules/{ => archive}/ash.py (95%) rename modules/{ => archive}/u8.py (95%) create mode 100644 modules/title/fakesign.py rename modules/{ => title}/nus.py (99%) rename modules/{ => title}/wad.py (93%) diff --git a/.gitignore b/.gitignore index 7633308..7da949c 100644 --- a/.gitignore +++ b/.gitignore @@ -166,6 +166,9 @@ cython_debug/ # Allows me to keep TMD files in my repository folder for testing without accidentally publishing them *.tmd +*.tik +*.cert +*.footer *.wad *.app *.arc diff --git a/Makefile b/Makefile index db98992..e0f75c0 100644 --- a/Makefile +++ b/Makefile @@ -8,3 +8,6 @@ linux-install: clean: rm wiipy + rm -rd wiipy.build + rm -rd wiipy.dist + rm -rd wiipy.onefile-build diff --git a/modules/ash.py b/modules/archive/ash.py similarity index 95% rename from modules/ash.py rename to modules/archive/ash.py index ea57f3d..4ed4b0a 100644 --- a/modules/ash.py +++ b/modules/archive/ash.py @@ -1,4 +1,4 @@ -# "ash.py" from WiiPy by NinjaCheetah +# "modules/archive/ash.py" from WiiPy by NinjaCheetah # https://github.com/NinjaCheetah/WiiPy import pathlib diff --git a/modules/u8.py b/modules/archive/u8.py similarity index 95% rename from modules/u8.py rename to modules/archive/u8.py index 9a8caa0..3ac5e84 100644 --- a/modules/u8.py +++ b/modules/archive/u8.py @@ -1,4 +1,4 @@ -# "u8.py" from WiiPy by NinjaCheetah +# "modules/archive/u8.py" from WiiPy by NinjaCheetah # https://github.com/NinjaCheetah/WiiPy import pathlib diff --git a/modules/title/fakesign.py b/modules/title/fakesign.py new file mode 100644 index 0000000..a5f3b08 --- /dev/null +++ b/modules/title/fakesign.py @@ -0,0 +1,34 @@ +# "modules/title/fakesign.py" from WiiPy by NinjaCheetah +# https://github.com/NinjaCheetah/WiiPy + +import pathlib +import libWiiPy + + +def handle_fakesign(args): + input_path = pathlib.Path(args.input) + output_path = pathlib.Path(args.output) + + if not input_path.exists(): + raise FileNotFoundError(input_path) + + if input_path.suffix.lower() == ".tmd": + tmd = libWiiPy.title.TMD() + tmd.load(open(input_path, "rb").read()) + tmd.fakesign() + open(output_path, "wb").write(tmd.dump()) + print("TMD fakesigned successfully!") + elif input_path.suffix.lower() == ".tik": + tik = libWiiPy.title.Ticket() + tik.load(open(input_path, "rb").read()) + tik.fakesign() + open(output_path, "wb").write(tik.dump()) + print("Ticket fakesigned successfully!") + elif input_path.suffix.lower() == ".wad": + title = libWiiPy.title.Title() + title.load_wad(open(input_path, "rb").read()) + title.fakesign() + open(output_path, "wb").write(title.dump_wad()) + print("WAD fakesigned successfully!") + else: + raise TypeError("This does not appear to be a TMD, Ticket, or WAD! Cannot fakesign.") diff --git a/modules/nus.py b/modules/title/nus.py similarity index 99% rename from modules/nus.py rename to modules/title/nus.py index d129725..84221f1 100644 --- a/modules/nus.py +++ b/modules/title/nus.py @@ -1,4 +1,4 @@ -# "nus.py" from WiiPy by NinjaCheetah +# "modules/title/nus.py" from WiiPy by NinjaCheetah # https://github.com/NinjaCheetah/WiiPy import os diff --git a/modules/wad.py b/modules/title/wad.py similarity index 93% rename from modules/wad.py rename to modules/title/wad.py index ba137db..480e267 100644 --- a/modules/wad.py +++ b/modules/title/wad.py @@ -1,9 +1,8 @@ -# "wad.py" from WiiPy by NinjaCheetah +# "modules/title/wad.py" from WiiPy by NinjaCheetah # https://github.com/NinjaCheetah/WiiPy import os import pathlib -import hashlib import binascii import libWiiPy @@ -23,7 +22,7 @@ def handle_wad(args): # Get a list of all files ending in .tmd, and then make sure that that list has *only* 1 entry. More than 1 # means we can't pack a WAD because we couldn't really tell which TMD is intended for this WAD. - tmd_list = list(input_path.glob('*.tmd')) + tmd_list = list(input_path.glob('*.[tT][mM][dD]')) if len(tmd_list) > 1: raise FileExistsError("More than one TMD file was found! Only one TMD can be packed into a WAD.") elif len(tmd_list) == 0: @@ -32,7 +31,7 @@ def handle_wad(args): tmd_file = tmd_list[0] # Repeat the same process as above for all .tik files. - ticket_list = list(input_path.glob('*.tik')) + ticket_list = list(input_path.glob('*.[tT][iI][kK]')) if len(ticket_list) > 1: raise FileExistsError("More than one Ticket file was found! Only one Ticket can be packed into a WAD.") elif len(ticket_list) == 0: @@ -41,7 +40,7 @@ def handle_wad(args): ticket_file = ticket_list[0] # And one more time for all .cert files. - cert_list = list(input_path.glob('*.cert')) + cert_list = list(input_path.glob('*.[cC][eE][rR][tT]')) if len(cert_list) > 1: raise FileExistsError("More than one certificate file was found! Only one certificate can be packed into a " "WAD.") @@ -51,7 +50,7 @@ def handle_wad(args): cert_file = cert_list[0] # Make sure that there's at least one content to pack. - content_files = list(input_path.glob("*.app")) + content_files = list(input_path.glob("*.[aA][pP][pP]")) if not content_files: raise FileNotFoundError("No contents found! Cannot pack WAD.") @@ -65,7 +64,7 @@ def handle_wad(args): title.wad.set_cert_data(open(cert_file, "rb").read()) # Footers are not super common and are not required, so we don't care about one existing until we get to # the step where we'd pack it. - footer_file = list(input_path.glob("*.footer"))[0] + footer_file = list(input_path.glob("*.[fF][oO][oO][tT][eE][rR]"))[0] if footer_file.exists(): title.wad.set_meta_data(open(footer_file, "rb").read()) # Method to ensure that the title's content records match between the TMD() and ContentRegion() objects. diff --git a/wiipy.py b/wiipy.py index 50e2728..d92993e 100644 --- a/wiipy.py +++ b/wiipy.py @@ -4,10 +4,11 @@ import argparse from importlib.metadata import version -from modules.wad import * -from modules.nus import * -from modules.u8 import * -from modules.ash import * +from modules.archive.ash import * +from modules.archive.u8 import * +from modules.title.fakesign import * +from modules.title.nus import * +from modules.title.wad import * if __name__ == "__main__": # Main argument parser. @@ -17,17 +18,26 @@ if __name__ == "__main__": version=f"WiiPy v1.2.2, based on libWiiPy v{version('libWiiPy')} (from branch \'main\')") subparsers = parser.add_subparsers(dest="subcommand", required=True) - # Argument parser for the WAD subcommand. - wad_parser = subparsers.add_parser("wad", help="pack/unpack a WAD file", - description="pack/unpack a WAD file") - wad_parser.set_defaults(func=handle_wad) - wad_group = wad_parser.add_mutually_exclusive_group(required=True) - wad_group.add_argument("-p", "--pack", help="pack a directory to a WAD file", action="store_true") - wad_group.add_argument("-u", "--unpack", help="unpack a WAD file to a directory", action="store_true") - wad_parser.add_argument("input", metavar="IN", type=str, help="input file") - wad_parser.add_argument("output", metavar="OUT", type=str, help="output file") - wad_parser.add_argument("--fakesign", help="fakesign the TMD and Ticket (trucha bug)", - action="store_true") + # Argument parser for the ASH subcommand. + ash_parser = subparsers.add_parser("ash", help="compress/decompress an ASH file", + description="compress/decompress an ASH file") + ash_parser.set_defaults(func=handle_ash) + ash_group = ash_parser.add_mutually_exclusive_group(required=True) + ash_group.add_argument("-c", "--compress", help="compress a file into an ASH file", action="store_true") + ash_group.add_argument("-d", "--decompress", help="decompress an ASH file", action="store_true") + ash_parser.add_argument("input", metavar="IN", type=str, help="input file") + ash_parser.add_argument("output", metavar="OUT", type=str, help="output file") + ash_parser.add_argument("--sym-bits", metavar="SYM_BITS", type=int, + help="number of bits in each symbol tree leaf (default: 9)", default=9) + ash_parser.add_argument("--dist-bits", metavar="DIST_BITS", type=int, + help="number of bits in each distance tree leaf (default: 11)", default=11) + + # Argument parser for the fakesign subcommand. + fakesign_parser = subparsers.add_parser("fakesign", help="fakesigns a TMD, Ticket, or WAD (trucha bug)", + description="fakesigns a TMD, Ticket, or WAD (trucha bug)") + fakesign_parser.set_defaults(func=handle_fakesign) + fakesign_parser.add_argument("input", metavar="IN", type=str, help="input file") + fakesign_parser.add_argument("output", metavar="OUT", type=str, help="output file") # Argument parser for the NUS subcommand. nus_parser = subparsers.add_parser("nus", help="download data from the NUS", @@ -71,19 +81,17 @@ if __name__ == "__main__": u8_parser.add_argument("input", metavar="IN", type=str, help="input file") u8_parser.add_argument("output", metavar="OUT", type=str, help="output file") - # Argument parser for the ASH subcommand. - ash_parser = subparsers.add_parser("ash", help="compress/decompress an ASH file", - description="compress/decompress an ASH file") - ash_parser.set_defaults(func=handle_ash) - ash_group = ash_parser.add_mutually_exclusive_group(required=True) - ash_group.add_argument("-c", "--compress", help="compress a file into an ASH file", action="store_true") - ash_group.add_argument("-d", "--decompress", help="decompress an ASH file", action="store_true") - ash_parser.add_argument("input", metavar="IN", type=str, help="input file") - ash_parser.add_argument("output", metavar="OUT", type=str, help="output file") - ash_parser.add_argument("--sym-bits", metavar="SYM_BITS", type=int, - help="number of bits in each symbol tree leaf (default: 9)", default=9) - ash_parser.add_argument("--dist-bits", metavar="DIST_BITS", type=int, - help="number of bits in each distance tree leaf (default: 11)", default=11) + # Argument parser for the WAD subcommand. + wad_parser = subparsers.add_parser("wad", help="pack/unpack a WAD file", + description="pack/unpack a WAD file") + wad_parser.set_defaults(func=handle_wad) + wad_group = wad_parser.add_mutually_exclusive_group(required=True) + wad_group.add_argument("-p", "--pack", help="pack a directory to a WAD file", action="store_true") + wad_group.add_argument("-u", "--unpack", help="unpack a WAD file to a directory", action="store_true") + wad_parser.add_argument("input", metavar="IN", type=str, help="input file") + wad_parser.add_argument("output", metavar="OUT", type=str, help="output file") + wad_parser.add_argument("--fakesign", help="fakesign the TMD and Ticket (trucha bug)", + action="store_true") # Parse all the args, and call the appropriate function with all of those args if a valid subcommand was passed. args = parser.parse_args()