Add module for directly fakesigning a TMD, Ticket, or WAD

This commit is contained in:
Campbell 2024-07-22 20:11:33 -04:00
parent 3115105343
commit b82b6f3873
Signed by: NinjaCheetah
GPG Key ID: B547958AF96ED344
8 changed files with 85 additions and 38 deletions

3
.gitignore vendored
View File

@ -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

View File

@ -8,3 +8,6 @@ linux-install:
clean:
rm wiipy
rm -rd wiipy.build
rm -rd wiipy.dist
rm -rd wiipy.onefile-build

View File

@ -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

View File

@ -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

34
modules/title/fakesign.py Normal file
View File

@ -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.")

View File

@ -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

View File

@ -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.

View File

@ -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()