diff --git a/modules/title/emunand.py b/modules/title/emunand.py index ac7d595..078d7a7 100644 --- a/modules/title/emunand.py +++ b/modules/title/emunand.py @@ -62,18 +62,20 @@ def handle_emunand_title(args): title_dir = title_dir.joinpath(tid_lower) if not title_dir.exists(): title_dir.mkdir() - title_dir = title_dir.joinpath("content") - if not title_dir.exists(): - title_dir.mkdir() - tmd_out = open(title_dir.joinpath("title.tmd"), "wb") + content_dir = title_dir.joinpath("content") + if not content_dir.exists(): + content_dir.mkdir() + tmd_out = open(content_dir.joinpath("title.tmd"), "wb") tmd_out.write(title.wad.get_tmd_data()) tmd_out.close() for content_file in range(0, title.tmd.num_contents): if title.tmd.content_records[content_file].content_type == 1: content_file_name = f"{title.tmd.content_records[content_file].content_id:08X}".lower() - content_out = open(title_dir.joinpath(content_file_name + ".app"), "wb") + content_out = open(content_dir.joinpath(content_file_name + ".app"), "wb") content_out.write(title.get_content_by_index(content_file)) content_out.close() + if not title_dir.joinpath("data").exists(): + title_dir.joinpath("data").mkdir() # Empty directory used for save data for the title. # Shared contents need to be installed to /shared1/, with incremental names determined by /shared1/content.map. content_map_path = shared_dir.joinpath("content.map") diff --git a/modules/title/info.py b/modules/title/info.py new file mode 100644 index 0000000..2c853db --- /dev/null +++ b/modules/title/info.py @@ -0,0 +1,55 @@ +# "modules/title/info.py" from WiiPy by NinjaCheetah +# https://github.com/NinjaCheetah/WiiPy + +import pathlib +import libWiiPy + + +def _print_tmd_info(tmd: libWiiPy.title.TMD): + # Get all important keys from the TMD and print them out nicely. + print("Title Info") + print(f" Title ID: {tmd.title_id}") + # This type of version number really only applies to the System Menu and IOS. + if tmd.title_id[:8] == "00000001": + print(f" Title Version: {tmd.title_version} ({tmd.title_version_converted})") + else: + print(f" Title Version: {tmd.title_version}") + if tmd.ios_tid == "0000000000000000": + print(f" IOS Version: N/A") + else: + print(f" Required IOS: IOS{int(tmd.ios_tid[-2:], 16)} ({tmd.ios_tid})") + print(f" Region: {tmd.get_title_region()}") + print(f" Title Type: {tmd.get_title_type()}") + print(f" vWii Title: {bool(tmd.vwii)}") + print(f" DVD Video Access: {tmd.get_access_right(tmd.AccessFlags.DVD_VIDEO)}") + print(f" AHB Access: {tmd.get_access_right(tmd.AccessFlags.AHB)}") + print(f" Fakesigned: {tmd.get_is_fakesigned()}") + # Iterate over the content and print their details. + print("\nContent Info") + print(f"Total Contents: {tmd.num_contents}") + for content in tmd.content_records: + print(f" Content Index: {content.index}") + print(f" Content ID: " + f"{content.content_id:08X}".lower()) + print(f" Content Type: {tmd.get_content_type(content.index)}") + print(f" Content Size: {content.content_size} bytes") + print(f" Content Hash: {content.content_hash.decode()}") + + +def handle_info(args): + input_path = pathlib.Path(args.input) + + 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()) + _print_tmd_info(tmd) + #elif input_path.suffix.lower() == ".tik": + # tik = libWiiPy.title.Ticket() + # tik.load(open(input_path, "rb").read()) + #elif input_path.suffix.lower() == ".wad": + # title = libWiiPy.title.Title() + # title.load_wad(open(input_path, "rb").read()) + else: + raise TypeError("This does not appear to be a TMD, Ticket, or WAD! No info can be provided.") diff --git a/modules/title/iospatcher.py b/modules/title/iospatcher.py index 52d2788..f4b4dc0 100644 --- a/modules/title/iospatcher.py +++ b/modules/title/iospatcher.py @@ -5,7 +5,7 @@ import pathlib import libWiiPy -def patch_fakesigning(ios_patcher: libWiiPy.title.IOSPatcher) -> int: +def _patch_fakesigning(ios_patcher: libWiiPy.title.IOSPatcher) -> int: print("Applying fakesigning patch... ", end="", flush=True) count = ios_patcher.patch_fakesigning() if count == 1: @@ -15,7 +15,7 @@ def patch_fakesigning(ios_patcher: libWiiPy.title.IOSPatcher) -> int: return count -def patch_es_identify(ios_patcher: libWiiPy.title.IOSPatcher) -> int: +def _patch_es_identify(ios_patcher: libWiiPy.title.IOSPatcher) -> int: print("Applying ES_Identify access patch... ", end="", flush=True) count = ios_patcher.patch_es_identify() if count == 1: @@ -25,7 +25,7 @@ def patch_es_identify(ios_patcher: libWiiPy.title.IOSPatcher) -> int: return count -def patch_nand_access(ios_patcher: libWiiPy.title.IOSPatcher) -> int: +def _patch_nand_access(ios_patcher: libWiiPy.title.IOSPatcher) -> int: print("Applying /dev/flash access patch... ", end="", flush=True) count = ios_patcher.patch_nand_access() if count == 1: @@ -35,7 +35,7 @@ def patch_nand_access(ios_patcher: libWiiPy.title.IOSPatcher) -> int: return count -def patch_version_downgrading(ios_patcher: libWiiPy.title.IOSPatcher) -> int: +def _patch_version_downgrading(ios_patcher: libWiiPy.title.IOSPatcher) -> int: print("Applying version downgrading patch... ", end="", flush=True) count = ios_patcher.patch_version_downgrading() if count == 1: @@ -45,7 +45,9 @@ def patch_version_downgrading(ios_patcher: libWiiPy.title.IOSPatcher) -> int: return count -def patch_drive_inquiry(ios_patcher: libWiiPy.title.IOSPatcher) -> int: +def _patch_drive_inquiry(ios_patcher: libWiiPy.title.IOSPatcher) -> int: + print("\n/!\\ WARNING! /!\\\n" + "This drive inquiry patch is experimental, and may introduce unexpected side effects on some consoles.\n") print("Applying drive inquiry patch... ", end="", flush=True) count = ios_patcher.patch_drive_inquiry() if count == 1: @@ -82,21 +84,21 @@ def handle_iospatch(args): ios_patcher.load(title) if args.all is True: - patch_count += patch_fakesigning(ios_patcher) - patch_count += patch_es_identify(ios_patcher) - patch_count += patch_nand_access(ios_patcher) - patch_count += patch_version_downgrading(ios_patcher) + patch_count += _patch_fakesigning(ios_patcher) + patch_count += _patch_es_identify(ios_patcher) + patch_count += _patch_nand_access(ios_patcher) + patch_count += _patch_version_downgrading(ios_patcher) else: if args.fakesigning is True: - patch_count += patch_fakesigning(ios_patcher) + patch_count += _patch_fakesigning(ios_patcher) if args.es_identify is True: - patch_count += patch_es_identify(ios_patcher) + patch_count += _patch_es_identify(ios_patcher) if args.nand_access is True: - patch_count += patch_nand_access(ios_patcher) + patch_count += _patch_nand_access(ios_patcher) if args.version_downgrading is True: - patch_count += patch_version_downgrading(ios_patcher) + patch_count += _patch_version_downgrading(ios_patcher) if args.drive_inquiry is True: - patch_count += patch_drive_inquiry(ios_patcher) + patch_count += _patch_drive_inquiry(ios_patcher) print(f"\nTotal patches applied: {patch_count}") diff --git a/wiipy.py b/wiipy.py index f804b6a..7e3319a 100644 --- a/wiipy.py +++ b/wiipy.py @@ -8,6 +8,7 @@ from modules.archive.ash import * from modules.archive.u8 import * from modules.title.emunand import * from modules.title.fakesign import * +from modules.title.info import * from modules.title.iospatcher import * from modules.title.nus import * from modules.title.wad import * @@ -58,6 +59,12 @@ if __name__ == "__main__": fakesign_parser.add_argument("input", metavar="IN", type=str, help="input file") fakesign_parser.add_argument("-o", "--output", metavar="OUT", type=str, help="output file (optional)") + # Argument parser for the info command. + info_parser = subparsers.add_parser("info", help="get information about a TMD, Ticket, or WAD", + description="get information about a TMD, Ticket, or WAD") + info_parser.set_defaults(func=handle_info) + info_parser.add_argument("input", metavar="IN", type=str, help="input file") + # Argument parser for the iospatch command. iospatch_parser = subparsers.add_parser("iospatch", help="patch IOS WADs to re-enable exploits", description="patch IOS WADs to re-enable exploits; by default, this will"