mirror of
https://github.com/NinjaCheetah/WiiPy.git
synced 2025-04-26 13:21:01 -04:00
Rewrote error output to be much clearer, no longer raises Python exceptions
This commit is contained in:
parent
19dc956d25
commit
6336791be0
@ -3,6 +3,7 @@
|
||||
|
||||
import pathlib
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def handle_ash_compress(args):
|
||||
@ -21,7 +22,7 @@ def handle_ash_decompress(args):
|
||||
dist_tree_bits = args.dist_bits
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error(f"The specified ASH file \"{input_path}\" does not exist!")
|
||||
|
||||
ash_data = input_path.read_bytes()
|
||||
# Decompress ASH file using the provided symbol/distance tree widths.
|
||||
|
@ -7,6 +7,7 @@ import shutil
|
||||
import tempfile
|
||||
import zipfile
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def handle_apply_mym(args):
|
||||
@ -15,9 +16,9 @@ def handle_apply_mym(args):
|
||||
output_path = pathlib.Path(args.output)
|
||||
|
||||
if not mym_path.exists():
|
||||
raise FileNotFoundError(mym_path)
|
||||
fatal_error(f"The specified MYM file \"{mym_path}\" does not exist!")
|
||||
if not base_path.exists():
|
||||
raise FileNotFoundError(base_path)
|
||||
fatal_error(f"The specified base file \"{base_path}\" does not exist!")
|
||||
if output_path.suffix != ".csm":
|
||||
output_path = output_path.with_suffix(".csm")
|
||||
|
||||
@ -29,21 +30,18 @@ def handle_apply_mym(args):
|
||||
with zipfile.ZipFile(mym_path) as mym:
|
||||
mym.extractall(tmp_path.joinpath("mym_out"))
|
||||
except zipfile.BadZipfile:
|
||||
print("Error: The provided MYM theme is not valid!")
|
||||
exit(1)
|
||||
fatal_error("The provided MYM theme is not valid!")
|
||||
mym_tmp_path = pathlib.Path(tmp_path.joinpath("mym_out"))
|
||||
# Extract the asset archive into the temp directory.
|
||||
try:
|
||||
libWiiPy.archive.extract_u8(base_path.read_bytes(), str(tmp_path.joinpath("base_out")))
|
||||
except ValueError:
|
||||
print("Error: The provided base assets are not valid!")
|
||||
exit(1)
|
||||
fatal_error("The provided base assets are not valid!")
|
||||
base_temp_path = pathlib.Path(tmp_path.joinpath("base_out"))
|
||||
# Parse the mym.ini file in the root of the extracted MYM file.
|
||||
mym_ini = configparser.ConfigParser()
|
||||
if not mym_tmp_path.joinpath("mym.ini").exists():
|
||||
print("Error: mym.ini could not be found! The provided theme is not valid.")
|
||||
exit(1)
|
||||
fatal_error("mym.ini could not be found in the theme! The provided theme is not valid.")
|
||||
mym_ini.read(mym_tmp_path.joinpath("mym.ini"))
|
||||
# Iterate over every key in the ini file and apply the theme based the source and target of each key.
|
||||
for section in mym_ini.sections():
|
||||
@ -53,9 +51,8 @@ def handle_apply_mym(args):
|
||||
source_file = source_file.joinpath(piece)
|
||||
# Check that this source file is actually valid, and error out if it isn't.
|
||||
if not source_file.exists():
|
||||
print(f"Error: A source file specified in mym.ini, \"{mym_ini[section]['source']}\", does not exist! "
|
||||
f"The provided theme is not valid.")
|
||||
exit(1)
|
||||
fatal_error(f"A source file specified in mym.ini, \"{mym_ini[section]['source']}\", does not exist! "
|
||||
f"The provided theme is not valid.")
|
||||
# Build the target path the same way.
|
||||
target_file = base_temp_path
|
||||
for piece in mym_ini[section]["file"].replace("\\", "/").split("/"):
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
import pathlib
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def handle_u8_pack(args):
|
||||
@ -12,8 +13,7 @@ def handle_u8_pack(args):
|
||||
try:
|
||||
u8_data = libWiiPy.archive.pack_u8(input_path)
|
||||
except ValueError:
|
||||
print("Error: Specified input file/folder does not exist!")
|
||||
return
|
||||
fatal_error(f"The specified input file/folder \"{input_path}\" does not exist!")
|
||||
|
||||
out_file = open(output_path, "wb")
|
||||
out_file.write(u8_data)
|
||||
@ -27,7 +27,7 @@ def handle_u8_unpack(args):
|
||||
output_path = pathlib.Path(args.output)
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(args.input)
|
||||
fatal_error(f"The specified input file \"{input_path}\" does not exist!")
|
||||
|
||||
u8_data = open(input_path, "rb").read()
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
import pathlib
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def handle_emunand_title(args):
|
||||
@ -17,12 +18,12 @@ def handle_emunand_title(args):
|
||||
input_path = pathlib.Path(args.install)
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
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:
|
||||
raise FileNotFoundError("No WAD files were found in the provided input directory!")
|
||||
fatal_error("No WAD files were found in the provided input directory!")
|
||||
wad_count = 0
|
||||
for wad in wad_files:
|
||||
title = libWiiPy.title.Title()
|
||||
@ -50,7 +51,7 @@ def handle_emunand_title(args):
|
||||
target_tid = input_str
|
||||
|
||||
if len(target_tid) != 16:
|
||||
raise ValueError("Invalid Title ID! Title IDs must be 16 characters long.")
|
||||
fatal_error("The provided Title ID is invalid! Title IDs must be 16 characters long.")
|
||||
|
||||
emunand.uninstall_title(target_tid)
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
import pathlib
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def handle_setting_decrypt(args):
|
||||
@ -13,7 +14,7 @@ def handle_setting_decrypt(args):
|
||||
output_path = pathlib.Path(input_path.stem + "_dec" + input_path.suffix)
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error("The specified setting file does not exist!")
|
||||
|
||||
# Load and decrypt the provided file.
|
||||
setting = libWiiPy.nand.SettingTxt()
|
||||
@ -31,7 +32,7 @@ def handle_setting_encrypt(args):
|
||||
output_path = pathlib.Path("setting.txt")
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error("The specified setting file does not exist!")
|
||||
|
||||
# Load and encrypt the provided file.
|
||||
setting = libWiiPy.nand.SettingTxt()
|
||||
@ -44,11 +45,11 @@ def handle_setting_encrypt(args):
|
||||
def handle_setting_gen(args):
|
||||
# Validate the provided SN. It should be 2 or 3 letters followed by 9 numbers.
|
||||
if len(args.serno) != 11 and len(args.serno) != 12:
|
||||
raise ValueError("The provided Serial Number is not valid!")
|
||||
fatal_error("The provided Serial Number is not valid!")
|
||||
try:
|
||||
int(args.serno[-9:])
|
||||
except ValueError:
|
||||
raise ValueError("The provided Serial Number is not valid!")
|
||||
fatal_error("The provided Serial Number is not valid!")
|
||||
prefix = args.serno[:-9]
|
||||
# Detect the console revision based on the SN.
|
||||
match prefix[0].upper():
|
||||
@ -64,11 +65,13 @@ def handle_setting_gen(args):
|
||||
# of 11 characters, while other regions have a three-letter prefix for a total length of 12 characters.
|
||||
valid_regions = ["USA", "EUR", "JPN", "KOR"]
|
||||
if args.region not in valid_regions:
|
||||
raise ValueError("The provided region is not valid!")
|
||||
fatal_error(f"The provided region \"{args.region}\" is not valid!")
|
||||
if len(prefix) == 2 and args.region != "USA":
|
||||
raise ValueError("The provided region does not match the provided Serial Number!")
|
||||
fatal_error(f"The provided region \"{args.region}\" does not match the provided Serial Number "
|
||||
f"\"{args.serno}\"!\"")
|
||||
elif len(prefix) == 3 and args.region == "USA":
|
||||
raise ValueError("The provided region does not match the provided Serial Number!")
|
||||
fatal_error(f"The provided region \"{args.region}\" does not match the provided Serial Number "
|
||||
f"\"{args.serno}\"!\"")
|
||||
# Get the values for VIDEO and GAME.
|
||||
video = ""
|
||||
game = ""
|
||||
|
@ -6,6 +6,7 @@ import os
|
||||
import xml.etree.ElementTree as ET
|
||||
import pathlib
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def build_cios(args):
|
||||
@ -18,11 +19,11 @@ def build_cios(args):
|
||||
output_path = pathlib.Path(args.output)
|
||||
|
||||
if not base_path.exists():
|
||||
raise FileNotFoundError(base_path)
|
||||
fatal_error(f"The specified base IOS file \"{base_path}\" does not exist!")
|
||||
if not map_path.exists():
|
||||
raise FileNotFoundError(map_path)
|
||||
fatal_error(f"The specified cIOS map file \"{map_path}\" does not exist!")
|
||||
if not modules_path.exists():
|
||||
raise FileNotFoundError(modules_path)
|
||||
fatal_error(f"The specified cIOS modules directory \"{modules_path}\" does not exist!")
|
||||
|
||||
title = libWiiPy.title.Title()
|
||||
title.load_wad(open(base_path, 'rb').read())
|
||||
@ -39,7 +40,7 @@ def build_cios(args):
|
||||
target_cios = child
|
||||
break
|
||||
if target_cios is None:
|
||||
raise ValueError("The target cIOS could not be found in the provided map!")
|
||||
fatal_error(f"The target cIOS \"{args.cios_ver}\" could not be found in the provided map!")
|
||||
|
||||
# Iterate over all bases in the target cIOS to find a base that matches the provided WAD. If one is found, ensure
|
||||
# that the version of the base in the map matches the version of the IOS WAD.
|
||||
@ -51,15 +52,16 @@ def build_cios(args):
|
||||
target_base = child
|
||||
break
|
||||
if target_base is None:
|
||||
raise ValueError("The provided base IOS doesn't match any bases found in the provided map!")
|
||||
fatal_error(f"The provided base (IOS{provided_base}) doesn't match any bases found in the provided map!")
|
||||
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!")
|
||||
fatal_error(f"The provided base (IOS{provided_base} v{title.tmd.title_version}) doesn't match the required "
|
||||
f"version (v{base_version})!")
|
||||
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 commands...")
|
||||
print("Patching existing modules...")
|
||||
for content in target_base.findall("content"):
|
||||
patches = content.findall("patch")
|
||||
if patches:
|
||||
@ -85,14 +87,14 @@ def build_cios(args):
|
||||
content_data.seek(offset)
|
||||
content_data.write(new_data)
|
||||
else:
|
||||
raise Exception("An error occurred while patching! Please make sure your base IOS is valid.")
|
||||
fatal_error("An error occurred while patching! Please make sure your base IOS is valid.")
|
||||
content_data.seek(0x0)
|
||||
dec_content = content_data.read()
|
||||
# Set the content in the title to the newly-patched content, and set the type to normal.
|
||||
title.set_content(dec_content, content_index, content_type=libWiiPy.title.ContentType.NORMAL)
|
||||
|
||||
# Next phase of cIOS building is to add the required extra commands.
|
||||
print("Adding required additional commands...")
|
||||
# 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:
|
||||
@ -101,7 +103,7 @@ def build_cios(args):
|
||||
cid = int(content.get("id"), 16)
|
||||
target_path = modules_path.joinpath(target_module + ".app")
|
||||
if not target_path.exists():
|
||||
raise Exception(f"A required module \"{target_module}.app\" could not be found!")
|
||||
fatal_error(f"A required module \"{target_module}\" could not be found!")
|
||||
# Check where this module belongs. If it's -1, add it to the end. If it's any other value, this module needs
|
||||
# to go at the index specified.
|
||||
new_module = target_path.read_bytes()
|
||||
@ -120,11 +122,11 @@ def build_cios(args):
|
||||
tid = title.tmd.title_id[:-2] + f"{slot:02X}"
|
||||
title.set_title_id(tid)
|
||||
else:
|
||||
raise ValueError(f"The provided slot \"{slot}\" is not valid!")
|
||||
fatal_error(f"The specified slot \"{slot}\" is not valid!")
|
||||
try:
|
||||
title.set_title_version(args.version)
|
||||
except ValueError:
|
||||
raise ValueError(f"The provided version \"{args.version}\" is not valid!")
|
||||
fatal_error(f"The specified 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
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
import pathlib
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def handle_fakesign(args):
|
||||
@ -13,7 +14,7 @@ def handle_fakesign(args):
|
||||
output_path = pathlib.Path(args.input)
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error(f"The specified input file \"{input_path}\" does not exist!")
|
||||
|
||||
if input_path.suffix.lower() == ".tmd":
|
||||
tmd = libWiiPy.title.TMD()
|
||||
@ -34,4 +35,4 @@ def handle_fakesign(args):
|
||||
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.")
|
||||
fatal_error("The provided file does not appear to be a TMD, Ticket, or WAD and cannot be fakesigned!")
|
||||
|
@ -4,6 +4,7 @@
|
||||
import pathlib
|
||||
import binascii
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def _print_tmd_info(tmd: libWiiPy.title.TMD):
|
||||
@ -136,7 +137,7 @@ def handle_info(args):
|
||||
input_path = pathlib.Path(args.input)
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error(f"The specified input file \"{input_path}\" does not exist!")
|
||||
|
||||
if input_path.suffix.lower() == ".tmd":
|
||||
tmd = libWiiPy.title.TMD()
|
||||
@ -151,4 +152,4 @@ def handle_info(args):
|
||||
title.load_wad(open(input_path, "rb").read())
|
||||
_print_wad_info(title)
|
||||
else:
|
||||
raise TypeError("This does not appear to be a TMD, Ticket, or WAD! No info can be provided.")
|
||||
fatal_error("This does not appear to be a TMD, Ticket, or WAD! No information can be provided.")
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
import pathlib
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def _patch_fakesigning(ios_patcher: libWiiPy.title.IOSPatcher) -> int:
|
||||
@ -60,14 +61,14 @@ def _patch_drive_inquiry(ios_patcher: libWiiPy.title.IOSPatcher) -> int:
|
||||
def handle_iospatch(args):
|
||||
input_path = pathlib.Path(args.input)
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error(f"The specified IOS file \"{input_path}\" does not exist!")
|
||||
|
||||
title = libWiiPy.title.Title()
|
||||
title.load_wad(open(input_path, "rb").read())
|
||||
|
||||
tid = title.tmd.title_id
|
||||
if tid[:8] != "00000001" or tid[8:] == "00000001" or tid[8:] == "00000002":
|
||||
raise ValueError("This WAD does not appear to contain an IOS! Patching cannot continue.")
|
||||
fatal_error(f"The provided WAD does not appear to contain an IOS! No patches can be applied.")
|
||||
|
||||
patch_count = 0
|
||||
|
||||
@ -105,8 +106,8 @@ def handle_iospatch(args):
|
||||
print(f"\nTotal patches applied: {patch_count}")
|
||||
|
||||
if patch_count == 0 and args.version is None and args.slot is None:
|
||||
raise ValueError("No patches were applied! Please select patches to apply, and ensure that selected patches are"
|
||||
" compatible with this IOS.")
|
||||
fatal_error("No patches were applied! Please specify patches to apply, and ensure that selected patches are "
|
||||
"compatible with this IOS.")
|
||||
|
||||
if patch_count > 0 or args.version is not None or args.slot is not None:
|
||||
# Set patched content to non-shared if that argument was passed.
|
||||
|
@ -5,6 +5,7 @@ import hashlib
|
||||
import pathlib
|
||||
import binascii
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def handle_nus_title(args):
|
||||
@ -23,8 +24,7 @@ def handle_nus_title(args):
|
||||
try:
|
||||
title_version = int(args.version)
|
||||
except ValueError:
|
||||
print("Enter a valid integer for the Title Version.")
|
||||
return
|
||||
fatal_error("The specified Title Version must be a valid integer!")
|
||||
|
||||
# If --wad was passed, check to make sure the path is okay.
|
||||
if args.wad is not None:
|
||||
@ -37,7 +37,7 @@ def handle_nus_title(args):
|
||||
output_dir = pathlib.Path(args.output)
|
||||
if output_dir.exists():
|
||||
if output_dir.is_file():
|
||||
raise ValueError("A file already exists with the provided directory name!")
|
||||
fatal_error("A file already exists with the provided directory name!")
|
||||
else:
|
||||
output_dir.mkdir()
|
||||
|
||||
@ -73,8 +73,7 @@ def handle_nus_title(args):
|
||||
# ticket so that they aren't attempted later.
|
||||
print(" - No Ticket is available!")
|
||||
if wad_file is not None and output_dir is None:
|
||||
print("--wad was passed, but this title cannot be packed into a WAD!")
|
||||
return
|
||||
fatal_error("--wad was passed, but this title has no common ticket and cannot be packed into a WAD!")
|
||||
|
||||
# Load the content records from the TMD, and begin iterating over the records.
|
||||
title.load_content_records()
|
||||
@ -137,7 +136,7 @@ def handle_nus_content(args):
|
||||
try:
|
||||
content_id = int.from_bytes(binascii.unhexlify(cid))
|
||||
except binascii.Error:
|
||||
raise ValueError("Invalid Content ID! Content ID must be in format \"000000xx\"!")
|
||||
fatal_error("The provided Content ID is invalid! The Content ID must be in the format \"000000xx\"!")
|
||||
|
||||
# Use the supplied output path if one was specified, otherwise generate one using the Content ID.
|
||||
if args.output is None:
|
||||
@ -148,15 +147,14 @@ def handle_nus_content(args):
|
||||
|
||||
# Ensure that a version was supplied before downloading, because we need the matching TMD for decryption to work.
|
||||
if decrypt_content is True and version is None:
|
||||
print("You must specify the version that the requested content belongs to for decryption!")
|
||||
return
|
||||
fatal_error("You must specify the version that the requested content belongs to for decryption!")
|
||||
|
||||
# Try to download the content, and catch the ValueError libWiiPy will throw if it can't be found.
|
||||
print("Downloading content with Content ID " + cid + "...")
|
||||
try:
|
||||
content_data = libWiiPy.title.download_content(tid, content_id)
|
||||
except ValueError:
|
||||
raise ValueError("The Title ID or Content ID you specified could not be found!")
|
||||
fatal_error("The specified Title ID or Content ID could not be found!")
|
||||
|
||||
if decrypt_content is True:
|
||||
output_path = output_path.with_suffix(".app")
|
||||
@ -167,8 +165,7 @@ def handle_nus_content(args):
|
||||
ticket = libWiiPy.title.Ticket()
|
||||
ticket.load(libWiiPy.title.download_ticket(tid, wiiu_endpoint=True))
|
||||
except ValueError:
|
||||
print("No Ticket is available! Content cannot be decrypted!")
|
||||
return
|
||||
fatal_error("No Ticket is available! Content cannot be decrypted.")
|
||||
|
||||
content_hash = 'gggggggggggggggggggggggggggggggggggggggg'
|
||||
content_size = 0
|
||||
@ -182,17 +179,16 @@ def handle_nus_content(args):
|
||||
# If the default hash never changed, then a content record matching the downloaded content couldn't be found,
|
||||
# which most likely means that the wrong version was specified.
|
||||
if content_hash == 'gggggggggggggggggggggggggggggggggggggggg':
|
||||
print("Content was not found in the TMD from the specified version! Content cannot be decrypted!")
|
||||
return
|
||||
fatal_error("Content was not found in the TMD for the specified version! Content cannot be decrypted.")
|
||||
|
||||
# Manually decrypt the content and verify its hash, which is what libWiiPy's get_content() methods do. We just
|
||||
# can't really use that here because that require setting up a lot more of the title than is necessary.
|
||||
content_dec = libWiiPy.title.decrypt_content(content_data, ticket.get_title_key(), content_index, content_size)
|
||||
content_dec_hash = hashlib.sha1(content_dec).hexdigest()
|
||||
if content_hash != content_dec_hash:
|
||||
raise ValueError("The decrypted content provided does not match the record at the provided index. \n"
|
||||
"Expected hash is: {}\n".format(content_hash) +
|
||||
"Actual hash is: {}".format(content_dec_hash))
|
||||
fatal_error("The decrypted content provided does not match the record at the provided index. \n"
|
||||
"Expected hash is: {}\n".format(content_hash) +
|
||||
"Actual hash is: {}".format(content_dec_hash))
|
||||
output_path.write_bytes(content_dec)
|
||||
else:
|
||||
output_path.write_bytes(content_data)
|
||||
@ -208,7 +204,7 @@ def handle_nus_tmd(args):
|
||||
try:
|
||||
version = int(args.version)
|
||||
except ValueError:
|
||||
raise ValueError("Enter a valid integer for the TMD Version.")
|
||||
fatal_error("The specified TMD version must be a valid integer!")
|
||||
else:
|
||||
version = None
|
||||
|
||||
@ -227,7 +223,7 @@ def handle_nus_tmd(args):
|
||||
try:
|
||||
tmd_data = libWiiPy.title.download_tmd(tid, version)
|
||||
except ValueError:
|
||||
raise ValueError("The Title ID or version you specified could not be found!")
|
||||
fatal_error("The specified Title ID or version could not be found!")
|
||||
|
||||
output_path.write_bytes(tmd_data)
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
import pathlib
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def handle_tmd_remove(args):
|
||||
@ -13,7 +14,7 @@ def handle_tmd_remove(args):
|
||||
output_path = pathlib.Path(args.input)
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error("The specified TMD files does not exist!")
|
||||
|
||||
tmd = libWiiPy.title.TMD()
|
||||
tmd.load(input_path.read_bytes())
|
||||
@ -21,7 +22,7 @@ def handle_tmd_remove(args):
|
||||
if args.index is not None:
|
||||
# Make sure the target index exists, then remove it from the TMD.
|
||||
if args.index >= len(tmd.content_records):
|
||||
raise ValueError("The provided index could not be found in this TMD!")
|
||||
fatal_error("The specified index could not be found in the provided TMD!")
|
||||
tmd.content_records.pop(args.index)
|
||||
tmd.num_contents -= 1
|
||||
# Auto fakesign because we've edited the TMD.
|
||||
@ -31,14 +32,14 @@ def handle_tmd_remove(args):
|
||||
|
||||
elif args.cid is not None:
|
||||
if len(args.cid) != 8:
|
||||
raise ValueError("The provided Content ID is invalid!")
|
||||
fatal_error("The specified Content ID is invalid!")
|
||||
target_cid = int(args.cid, 16)
|
||||
# List Contents IDs in the title, and ensure that the target Content ID exists.
|
||||
valid_ids = []
|
||||
for record in tmd.content_records:
|
||||
valid_ids.append(record.content_id)
|
||||
if target_cid not in valid_ids:
|
||||
raise ValueError("The provided Content ID could not be found in this TMD!")
|
||||
fatal_error("The specified Content ID could not be found in the provided TMD!")
|
||||
tmd.content_records.pop(valid_ids.index(target_cid))
|
||||
tmd.num_contents -= 1
|
||||
# Auto fakesign because we've edited the TMD.
|
||||
|
@ -4,6 +4,7 @@
|
||||
import pathlib
|
||||
from random import randint
|
||||
import libWiiPy
|
||||
from modules.core import fatal_error
|
||||
|
||||
|
||||
def handle_wad_add(args):
|
||||
@ -15,9 +16,9 @@ def handle_wad_add(args):
|
||||
output_path = pathlib.Path(args.input)
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error(f"The specified WAD file \"{input_path}\" does not exist!")
|
||||
if not content_path.exists():
|
||||
raise FileNotFoundError(content_path)
|
||||
fatal_error(f"The specified content file \"{content_path}\" does not exist!")
|
||||
|
||||
title = libWiiPy.title.Title()
|
||||
title.load_wad(input_path.read_bytes())
|
||||
@ -27,11 +28,11 @@ def handle_wad_add(args):
|
||||
# We need to both validate that this is a real CID, and also that it isn't already taken by another content.
|
||||
if args.cid is not None:
|
||||
if len(args.cid) != 8:
|
||||
raise ValueError("The provided Content ID is invalid!")
|
||||
fatal_error("The specified Content ID is not valid!")
|
||||
target_cid = int(args.cid, 16)
|
||||
for record in title.content.content_records:
|
||||
if target_cid == record.content_id:
|
||||
raise ValueError("The provided Content ID is already being used by this title!")
|
||||
fatal_error("The specified Content ID is already in use by this title!")
|
||||
print(f"Using provided Content ID \"{target_cid:08X}\".")
|
||||
# If we weren't given a CID, then we need to randomly assign one, and ensure it isn't being used.
|
||||
else:
|
||||
@ -53,7 +54,7 @@ def handle_wad_add(args):
|
||||
case "dlc":
|
||||
target_type = libWiiPy.title.ContentType.DLC
|
||||
case _:
|
||||
raise ValueError("The provided content type is invalid!")
|
||||
fatal_error(f"The provided content type \"{args.type}\" is not valid!")
|
||||
else:
|
||||
target_type = libWiiPy.title.ContentType.NORMAL
|
||||
|
||||
@ -76,7 +77,7 @@ def handle_wad_convert(args):
|
||||
elif args.vwii:
|
||||
target = "vWii"
|
||||
else:
|
||||
raise ValueError("No valid target was provided!")
|
||||
fatal_error("No valid encryption target was specified!")
|
||||
|
||||
if args.output is None:
|
||||
match target:
|
||||
@ -87,22 +88,22 @@ def handle_wad_convert(args):
|
||||
case "vWii":
|
||||
output_path = pathlib.Path(input_path.stem + "_vWii" + input_path.suffix)
|
||||
case _:
|
||||
raise ValueError("No valid target was provided!")
|
||||
fatal_error("No valid encryption target was specified!")
|
||||
else:
|
||||
output_path = pathlib.Path(args.output)
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error(f"The specified WAD file \"{input_path}\" does not exist!")
|
||||
|
||||
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!")
|
||||
fatal_error("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!")
|
||||
fatal_error("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!")
|
||||
fatal_error("This is already a vWii WAD!")
|
||||
# Get the current type to display later.
|
||||
if title.ticket.is_dev:
|
||||
source = "development"
|
||||
@ -143,40 +144,39 @@ def handle_wad_pack(args):
|
||||
# Make sure input path both exists and is a directory. Separate checks because this provides more relevant
|
||||
# errors than just a NotADirectoryError if the actual issue is that there's nothing at all.
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error(f"The specified input directory \"{input_path}\" does not exist!")
|
||||
if not input_path.is_dir():
|
||||
raise NotADirectoryError(input_path)
|
||||
fatal_error(f"The specified input path \"{input_path}\" is not a directory!")
|
||||
|
||||
# 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('*.[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.")
|
||||
fatal_error("More than one TMD file was found! Only one TMD can be packed into a WAD.")
|
||||
elif len(tmd_list) == 0:
|
||||
raise FileNotFoundError("No TMD file found! Cannot pack WAD.")
|
||||
fatal_error("No TMD file was found! Cannot pack WAD.")
|
||||
tmd_file = pathlib.Path(tmd_list[0])
|
||||
|
||||
# Repeat the same process as above for all .tik files.
|
||||
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.")
|
||||
fatal_error("More than one Ticket file was found! Only one Ticket can be packed into a WAD.")
|
||||
elif len(ticket_list) == 0:
|
||||
raise FileNotFoundError("No Ticket file found! Cannot pack WAD.")
|
||||
fatal_error("No Ticket file was found! Cannot pack WAD.")
|
||||
ticket_file = pathlib.Path(ticket_list[0])
|
||||
|
||||
# And one more time for all .cert files.
|
||||
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.")
|
||||
fatal_error("More than one certificate file was found! Only one certificate can be packed into a WAD.")
|
||||
elif len(cert_list) == 0:
|
||||
raise FileNotFoundError("No certificate file found! Cannot pack WAD.")
|
||||
fatal_error("No certificate file was found! Cannot pack WAD.")
|
||||
cert_file = pathlib.Path(cert_list[0])
|
||||
|
||||
# Make sure that there's at least one content to pack.
|
||||
content_files = list(input_path.glob("*.[aA][pP][pP]"))
|
||||
if not content_files:
|
||||
raise FileNotFoundError("No contents found! Cannot pack WAD.")
|
||||
fatal_error("No content files were found! Cannot pack WAD.")
|
||||
|
||||
# Open the output file, and load all the component files that we've now verified we have into a libWiiPy Title()
|
||||
# object.
|
||||
@ -222,7 +222,7 @@ def handle_wad_remove(args):
|
||||
output_path = pathlib.Path(args.input)
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error(f"The specified WAD file \"{input_path}\" does not exist!")
|
||||
|
||||
title = libWiiPy.title.Title()
|
||||
title.load_wad(input_path.read_bytes())
|
||||
@ -234,7 +234,7 @@ def handle_wad_remove(args):
|
||||
for record in title.content.content_records:
|
||||
valid_indices.append(record.index)
|
||||
if args.index not in valid_indices:
|
||||
raise ValueError("The provided content index could not be found in this title!")
|
||||
fatal_error("The specified content index could not be found in this title!")
|
||||
title.content.remove_content_by_index(args.index)
|
||||
# Auto fakesign because we've edited the title.
|
||||
title.fakesign()
|
||||
@ -243,14 +243,14 @@ def handle_wad_remove(args):
|
||||
|
||||
elif args.cid is not None:
|
||||
if len(args.cid) != 8:
|
||||
raise ValueError("The provided Content ID is invalid!")
|
||||
fatal_error("The specified Content ID is not valid!")
|
||||
target_cid = int(args.cid, 16)
|
||||
# List Contents IDs in the title, and ensure that the target Content ID exists.
|
||||
valid_ids = []
|
||||
for record in title.content.content_records:
|
||||
valid_ids.append(record.content_id)
|
||||
if target_cid not in valid_ids:
|
||||
raise ValueError("The provided Content ID could not be found in this title!")
|
||||
fatal_error("The specified Content ID could not be found in this title!")
|
||||
title.content.remove_content_by_cid(target_cid)
|
||||
# Auto fakesign because we've edited the title.
|
||||
title.fakesign()
|
||||
@ -267,9 +267,9 @@ def handle_wad_set(args):
|
||||
output_path = pathlib.Path(args.input)
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error(f"The specified WAD file \"{input_path}\" does not exist!")
|
||||
if not content_path.exists():
|
||||
raise FileNotFoundError(content_path)
|
||||
fatal_error(f"The specified content file \"{content_path}\" does not exist!")
|
||||
|
||||
title = libWiiPy.title.Title()
|
||||
title.load_wad(input_path.read_bytes())
|
||||
@ -285,7 +285,7 @@ def handle_wad_set(args):
|
||||
case "dlc":
|
||||
target_type = libWiiPy.title.ContentType.DLC
|
||||
case _:
|
||||
raise ValueError("The provided content type is invalid!")
|
||||
fatal_error(f"The provided content type \"{args.type}\" is not valid!\"")
|
||||
else:
|
||||
target_type = None
|
||||
|
||||
@ -295,7 +295,7 @@ def handle_wad_set(args):
|
||||
for record in title.content.content_records:
|
||||
existing_indices.append(record.index)
|
||||
if args.index not in existing_indices:
|
||||
raise ValueError("The provided index could not be found in this title!")
|
||||
fatal_error("The specified index could not be found in this title!")
|
||||
if target_type:
|
||||
title.set_content(content_data, args.index, content_type=target_type)
|
||||
else:
|
||||
@ -309,13 +309,13 @@ def handle_wad_set(args):
|
||||
elif args.cid is not None:
|
||||
# If we're replacing based on the CID, then make sure the specified CID is valid and exists.
|
||||
if len(args.cid) != 8:
|
||||
raise ValueError("The provided Content ID is invalid!")
|
||||
fatal_error("The specified Content ID is not valid!")
|
||||
target_cid = int(args.cid, 16)
|
||||
existing_cids = []
|
||||
for record in title.content.content_records:
|
||||
existing_cids.append(record.content_id)
|
||||
if target_cid not in existing_cids:
|
||||
raise ValueError("The provided Content ID could not be found in this title!")
|
||||
fatal_error("The specified Content ID could not be found in this title!")
|
||||
target_index = title.content.get_index_from_cid(target_cid)
|
||||
if target_type:
|
||||
title.set_content(content_data, target_index, content_type=target_type)
|
||||
@ -332,11 +332,11 @@ def handle_wad_unpack(args):
|
||||
output_path = pathlib.Path(args.output)
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
fatal_error(f"The specified WAD file \"{input_path}\" does not exist!")
|
||||
# Check if the output path already exists, and if it does, ensure that it is both a directory and empty.
|
||||
if output_path.exists():
|
||||
if output_path.is_file():
|
||||
raise ValueError("A file already exists with the provided directory name!")
|
||||
fatal_error(f"A file already exists with the provided output directory name!")
|
||||
else:
|
||||
output_path.mkdir()
|
||||
|
||||
|
9
modules/core.py
Normal file
9
modules/core.py
Normal file
@ -0,0 +1,9 @@
|
||||
# "modules/core.py" from WiiPy by NinjaCheetah
|
||||
# https://github.com/NinjaCheetah/WiiPy
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def fatal_error(msg: str) -> None:
|
||||
print(f"\033[31mError:\033[0m {msg}")
|
||||
sys.exit(-1)
|
2
wiipy.py
2
wiipy.py
@ -65,7 +65,7 @@ if __name__ == "__main__":
|
||||
cios_parser.add_argument("output", metavar="OUT", type=str, help="file to output the cIOS to")
|
||||
cios_parser.add_argument("-c", "--cios-ver", metavar="CIOS", type=str,
|
||||
help="cIOS version from the map to build", required=True)
|
||||
cios_parser.add_argument("-m", "--commands", metavar="MODULES", type=str,
|
||||
cios_parser.add_argument("-m", "--modules", metavar="MODULES", type=str,
|
||||
help="directory to look for cIOS commands in (optional, defaults to current directory)")
|
||||
cios_parser.add_argument("-s", "--slot", metavar="SLOT", type=int,
|
||||
help="slot that this cIOS will install to (optional, defaults to 249)", default=249)
|
||||
|
Loading…
x
Reference in New Issue
Block a user