# "commands/nand/emunand.py" from WiiPy by NinjaCheetah # https://github.com/NinjaCheetah/WiiPy import math import pathlib import libWiiPy from modules.core import fatal_error def handle_emunand_info(args): emunand = libWiiPy.nand.EmuNAND(args.emunand) # Basic info. print(f"EmuNAND Info") print(f" Path: {str(emunand.emunand_root.absolute())}") is_vwii = False try: tmd = emunand.get_title_tmd("0000000100000002") is_vwii = bool(tmd.vwii) print(f" System Menu Version: {libWiiPy.title.title_ver_dec_to_standard(tmd.title_version, '0000000100000002', vwii=is_vwii)}") except FileNotFoundError: print(f" System Menu Version: None") settings_path = emunand.title_dir.joinpath("00000001", "00000002", "data", "setting.txt") if settings_path.exists(): settings = libWiiPy.nand.SettingTxt() settings.load(settings_path.read_bytes()) print(f" System Region: {settings.area}") else: print(f" System Region: N/A") if is_vwii: print(f" Type: vWii") else: print(f" Type: Wii") categories = emunand.get_installed_titles() installed_count = 0 for category in categories: if category.type != "00010000": for _ in category.titles: installed_count += 1 print(f" Installed Titles: {installed_count}") total_size = sum(file.stat().st_size for file in emunand.emunand_root.rglob('*')) total_size_blocks = math.ceil(total_size / 131072) print(f" Space Used: {total_size_blocks} blocks ({round(total_size / 1048576, 2)} MB)") print("") installed_ioses = [] installed_titles = [] disc_titles = [] for category in categories: if category.type == "00000001": ioses = [] for title in category.titles: if title != "00000002": ioses.append(int(title, 16)) ioses.sort() installed_ioses = [f"00000001{i:08X}".upper() for i in ioses] elif category.type != "00010000": for title in category.titles: installed_titles.append(f"{category.type}{title}".upper()) elif category.type == "00010000": for title in category.titles: if title != "48415A41": disc_titles.append(f"{category.type}{title}".upper()) print(f"System Titles:") for ios in installed_ioses: if ios[8:] in ["00000100", "00000101", "00000200", "00000201"]: if ios[8:] == "00000100": print(f" BC ({ios.upper()})") elif ios[8:] == "00000101": print(f" MIOS ({ios.upper()})") elif ios[8:] == "00000200": print(f" BC-NAND ({ios.upper()})") elif ios[8:] == "00000201": print(f" BC-WFS ({ios.upper()})") tmd = emunand.get_title_tmd(f"{ios}") print(f" Version: {tmd.title_version}") else: print(f" IOS{int(ios[-2:], 16)} ({ios.upper()})") tmd = emunand.get_title_tmd(f"{ios}") print(f" Version: {tmd.title_version} ({tmd.title_version_converted})") print("") print(f"Installed Titles:") missing_ioses = [] for title in installed_titles: ascii_tid = "" try: ascii_tid = (bytes.fromhex(title[8:].replace("00", "30"))).decode("ascii") except UnicodeDecodeError: pass if ascii_tid.isalnum(): print(f" {title.upper()} ({ascii_tid})") else: print(f" {title.upper()}") tmd = emunand.get_title_tmd(f"{title}") print(f" Version: {tmd.title_version}") print(f" Required IOS: IOS{int(tmd.ios_tid[-2:], 16)} ({tmd.ios_tid.upper()})", end="", flush=True) if tmd.ios_tid.upper() not in installed_ioses: print(" *") if tmd.ios_tid not in missing_ioses: missing_ioses.append(tmd.ios_tid) else: print("") print("") if disc_titles: print(f"Save data was found for the following disc titles:") for disc in disc_titles: ascii_tid = "" try: ascii_tid = (bytes.fromhex(disc[8:].replace("00", "30"))).decode("ascii") except UnicodeDecodeError: pass if ascii_tid.isalnum(): print(f" {disc.upper()} ({ascii_tid})") else: print(f" {disc.upper()}") print("") if missing_ioses: print(f"Some titles installed are missing their required IOS. These missing IOSes are marked with a * in the " f"title list above. If these IOSes are not installed, the titles requiring them will not launch. The " f"IOSes required but not installed are:") for missing in missing_ioses: print(f" IOS{int(missing[-2:], 16)} ({missing.upper()})") print("") def handle_emunand_title(args): emunand = libWiiPy.nand.EmuNAND(args.emunand) if args.skip_hash: skip_hash = True else: skip_hash = False # Code for if the --install argument was passed. if args.install: input_path = pathlib.Path(args.install) if not input_path.exists(): fatal_error("The specified WAD file does not exist!") if input_path.is_dir(): wad_files = list(input_path.glob("*.[wW][aA][dD]")) if not wad_files: fatal_error("No WAD files were found in the provided input directory!") wad_count = 0 for wad in wad_files: title = libWiiPy.title.Title() title.load_wad(open(wad, "rb").read()) try: emunand.install_title(title, skip_hash=skip_hash) wad_count += 1 except ValueError: print(f"WAD {wad} could not be installed!") print(f"Successfully installed {wad_count} WAD(s) to EmuNAND!") else: title = libWiiPy.title.Title() title.load_wad(open(input_path, "rb").read()) emunand.install_title(title, skip_hash=skip_hash) print("Successfully installed WAD to EmuNAND!") # Code for if the --uninstall argument was passed. elif args.uninstall: input_str = args.uninstall if pathlib.Path(input_str).exists(): title = libWiiPy.title.Title() title.load_wad(open(pathlib.Path(input_str), "rb").read()) target_tid = title.tmd.title_id else: target_tid = input_str if len(target_tid) != 16: fatal_error("The provided Title ID is invalid! Title IDs must be 16 characters long.") emunand.uninstall_title(target_tid) print("Title uninstalled from EmuNAND!")