mirror of
https://github.com/NinjaCheetah/WiiPy.git
synced 2025-04-26 13:21:01 -04:00
Rewrote entire source to be based around argparse
This does change a large amount of the syntax for using the CLI, but I think that's for the better. This new system also allows for having help pages for each sub command, making the tool a lot easier to use. It also allows for having more arguments available for each subcommand, which is especially necessary for the ASH module.
This commit is contained in:
parent
dcba8672bc
commit
5e1bf6ed4e
@ -1,23 +1,33 @@
|
||||
# "ash.py" from WiiPy by NinjaCheetah
|
||||
# https://github.com/NinjaCheetah/WiiPy
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import libWiiPy
|
||||
|
||||
|
||||
def decompress_ash(in_file: str, out_file: str = None):
|
||||
if not os.path.isfile(in_file):
|
||||
raise FileNotFoundError(in_file)
|
||||
def handle_ash(args):
|
||||
input_path = pathlib.Path(args.input)
|
||||
output_path = pathlib.Path(args.output)
|
||||
|
||||
ash_file = open(in_file, "rb")
|
||||
if args.compress:
|
||||
print("Compression is not implemented yet.")
|
||||
|
||||
elif args.decompress:
|
||||
sym_tree_bits = args.sym_bits
|
||||
dist_tree_bits = args.dist_bits
|
||||
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
|
||||
ash_file = open(input_path, "rb")
|
||||
ash_data = ash_file.read()
|
||||
ash_file.close()
|
||||
|
||||
ash_decompressed = libWiiPy.archive.decompress_ash(ash_data)
|
||||
ash_decompressed = libWiiPy.archive.decompress_ash(ash_data, sym_tree_bits=sym_tree_bits,
|
||||
dist_tree_bits=dist_tree_bits)
|
||||
|
||||
if out_file is None:
|
||||
out_file = in_file + ".arc"
|
||||
|
||||
ash_out = open(out_file, "wb")
|
||||
ash_out = open(output_path, "wb")
|
||||
ash_out.write(ash_decompressed)
|
||||
ash_out.close()
|
||||
|
||||
print("ASH file decompressed!")
|
||||
|
@ -4,19 +4,21 @@
|
||||
import libWiiPy
|
||||
|
||||
|
||||
def download_title(title_id: str, title_version_input: str = None):
|
||||
def handle_nus(args):
|
||||
title_version = None
|
||||
if title_version_input is not None:
|
||||
if args.version is not None:
|
||||
try:
|
||||
title_version = int(title_version_input)
|
||||
title_version = int(args.version)
|
||||
except ValueError:
|
||||
print("Enter a valid integer for the Title Version.")
|
||||
return
|
||||
|
||||
title = libWiiPy.title.download_title(title_id, title_version)
|
||||
title = libWiiPy.title.download_title(args.tid, title_version)
|
||||
|
||||
file_name = title_id + "-v" + str(title.tmd.title_version) + ".wad"
|
||||
file_name = args.tid + "-v" + str(title.tmd.title_version) + ".wad"
|
||||
|
||||
wad_file = open(file_name, "wb")
|
||||
wad_file.write(title.dump_wad())
|
||||
wad_file.close()
|
||||
|
||||
print("Downloaded title with Title ID \"" + args.tid + "\"!")
|
||||
|
@ -1,29 +1,37 @@
|
||||
# "u8.py" from WiiPy by NinjaCheetah
|
||||
# https://github.com/NinjaCheetah/WiiPy
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import libWiiPy
|
||||
|
||||
|
||||
def extract_u8_to_folder(in_file: str, out_folder: str):
|
||||
if not os.path.isfile(in_file):
|
||||
raise FileNotFoundError(in_file)
|
||||
|
||||
u8_data = open(in_file, "rb").read()
|
||||
def handle_u8(args):
|
||||
input_path = pathlib.Path(args.input)
|
||||
output_path = pathlib.Path(args.output)
|
||||
|
||||
if args.pack:
|
||||
try:
|
||||
libWiiPy.archive.extract_u8(u8_data, out_folder)
|
||||
u8_data = libWiiPy.archive.pack_u8(input_path)
|
||||
except ValueError:
|
||||
print("Specified output folder already exists!")
|
||||
|
||||
|
||||
def pack_u8_from_folder(in_folder: str, out_file: str):
|
||||
try:
|
||||
u8_data = libWiiPy.archive.pack_u8(in_folder)
|
||||
except ValueError:
|
||||
print("Specified input file/folder does not exist!")
|
||||
print("Error: Specified input file/folder does not exist!")
|
||||
return
|
||||
|
||||
out_file = open(out_file, "wb")
|
||||
out_file = open(output_path, "wb")
|
||||
out_file.write(u8_data)
|
||||
out_file.close()
|
||||
|
||||
print("U8 archive packed!")
|
||||
|
||||
elif args.unpack:
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(args.input)
|
||||
|
||||
u8_data = open(input_path, "rb").read()
|
||||
|
||||
if output_path.exists():
|
||||
print("Error: Specified output directory already exists!")
|
||||
return
|
||||
|
||||
libWiiPy.archive.extract_u8(u8_data, str(output_path))
|
||||
|
||||
print("U8 archive unpacked!")
|
||||
|
128
modules/wad.py
128
modules/wad.py
@ -1,99 +1,51 @@
|
||||
# "wad.py" from WiiPy by NinjaCheetah
|
||||
# https://github.com/NinjaCheetah/WiiPy
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import binascii
|
||||
import libWiiPy
|
||||
|
||||
|
||||
def extract_wad_to_folder(in_file: str, out_folder: str):
|
||||
if not os.path.isfile(in_file):
|
||||
raise FileNotFoundError(in_file)
|
||||
out_folder = pathlib.Path(out_folder)
|
||||
if not out_folder.is_dir():
|
||||
out_folder.mkdir()
|
||||
def handle_wad(args):
|
||||
input_path = pathlib.Path(args.input)
|
||||
output_path = pathlib.Path(args.output)
|
||||
|
||||
with open(in_file, "rb") as wad_file:
|
||||
wad = libWiiPy.title.WAD()
|
||||
wad.load(wad_file.read())
|
||||
if args.pack:
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
if not input_path.is_dir():
|
||||
raise NotADirectoryError(input_path)
|
||||
|
||||
tmd = libWiiPy.title.TMD()
|
||||
tmd.load(wad.get_tmd_data())
|
||||
ticket = libWiiPy.title.Ticket()
|
||||
ticket.load(wad.get_ticket_data())
|
||||
content_region = libWiiPy.title.ContentRegion()
|
||||
content_region.load(wad.get_content_data(), tmd.content_records)
|
||||
|
||||
title_key = ticket.get_title_key()
|
||||
|
||||
cert_name = tmd.title_id + ".cert"
|
||||
cert_out = open(os.path.join(out_folder, cert_name), "wb")
|
||||
cert_out.write(wad.get_cert_data())
|
||||
cert_out.close()
|
||||
|
||||
tmd_name = tmd.title_id + ".tmd"
|
||||
tmd_out = open(os.path.join(out_folder, tmd_name), "wb")
|
||||
tmd_out.write(wad.get_tmd_data())
|
||||
tmd_out.close()
|
||||
|
||||
ticket_name = tmd.title_id + ".tik"
|
||||
ticket_out = open(os.path.join(out_folder, ticket_name), "wb")
|
||||
ticket_out.write(wad.get_ticket_data())
|
||||
ticket_out.close()
|
||||
|
||||
meta_name = tmd.title_id + ".footer"
|
||||
meta_out = open(os.path.join(out_folder, meta_name), "wb")
|
||||
meta_out.write(wad.get_meta_data())
|
||||
meta_out.close()
|
||||
|
||||
for content_file in range(0, tmd.num_contents):
|
||||
content_file_name = "000000"
|
||||
content_file_name += str(binascii.hexlify(content_file.to_bytes()).decode()) + ".app"
|
||||
content_out = open(os.path.join(out_folder, content_file_name), "wb")
|
||||
content_out.write(content_region.get_content_by_index(content_file, title_key))
|
||||
content_out.close()
|
||||
|
||||
|
||||
def pack_wad_from_folder(in_folder, out_file):
|
||||
if not os.path.exists(in_folder):
|
||||
raise FileNotFoundError(in_folder)
|
||||
if not os.path.isdir(in_folder):
|
||||
raise NotADirectoryError(in_folder)
|
||||
|
||||
out_file = pathlib.Path(out_file)
|
||||
in_folder = pathlib.Path(in_folder)
|
||||
|
||||
tmd_file = list(in_folder.glob("*.tmd"))[0]
|
||||
if not os.path.exists(tmd_file):
|
||||
tmd_file = list(input_path.glob("*.tmd"))[0]
|
||||
if not tmd_file.exists():
|
||||
raise FileNotFoundError("Cannot find a TMD! Exiting...")
|
||||
|
||||
ticket_file = list(in_folder.glob("*.tik"))[0]
|
||||
if not os.path.exists(ticket_file):
|
||||
ticket_file = list(input_path.glob("*.tik"))[0]
|
||||
if not ticket_file.exists():
|
||||
raise FileNotFoundError("Cannot find a Ticket! Exiting...")
|
||||
|
||||
cert_file = list(in_folder.glob("*.cert"))[0]
|
||||
if not os.path.exists(cert_file):
|
||||
cert_file = list(input_path.glob("*.cert"))[0]
|
||||
if not cert_file.exists():
|
||||
raise FileNotFoundError("Cannot find a cert! Exiting...")
|
||||
|
||||
content_files = list(in_folder.glob("*.app"))
|
||||
content_files = list(input_path.glob("*.app"))
|
||||
if not content_files:
|
||||
raise FileNotFoundError("Cannot find any contents! Exiting...")
|
||||
|
||||
with open(out_file, "wb") as out_file:
|
||||
with open(output_path, "wb") as output_path:
|
||||
title = libWiiPy.title.Title()
|
||||
|
||||
title.load_tmd(open(tmd_file, "rb").read())
|
||||
title.load_ticket(open(ticket_file, "rb").read())
|
||||
title.wad.set_cert_data(open(cert_file, "rb").read())
|
||||
footer_file = list(in_folder.glob("*.footer"))[0]
|
||||
if os.path.exists(footer_file):
|
||||
footer_file = list(input_path.glob("*.footer"))[0]
|
||||
if footer_file.exists():
|
||||
title.wad.set_meta_data(open(footer_file, "rb").read())
|
||||
title.load_content_records()
|
||||
|
||||
title_key = title.ticket.get_title_key()
|
||||
|
||||
content_list = list(in_folder.glob("*.app"))
|
||||
content_list = list(input_path.glob("*.app"))
|
||||
for index in range(len(title.content.content_records)):
|
||||
for content in range(len(content_list)):
|
||||
dec_content = open(content_list[content], "rb").read()
|
||||
@ -105,4 +57,44 @@ def pack_wad_from_folder(in_folder, out_file):
|
||||
# Wasn't the right content, so try again.
|
||||
pass
|
||||
|
||||
out_file.write(title.dump_wad())
|
||||
output_path.write(title.dump_wad())
|
||||
|
||||
print("WAD file packed!")
|
||||
|
||||
elif args.unpack:
|
||||
if not input_path.exists():
|
||||
raise FileNotFoundError(input_path)
|
||||
if not output_path.is_dir():
|
||||
output_path.mkdir()
|
||||
|
||||
with open(args.input, "rb") as wad_file:
|
||||
title = libWiiPy.title.Title()
|
||||
title.load_wad(wad_file.read())
|
||||
|
||||
cert_name = title.tmd.title_id + ".cert"
|
||||
cert_out = open(output_path.joinpath(cert_name), "wb")
|
||||
cert_out.write(title.wad.get_cert_data())
|
||||
cert_out.close()
|
||||
|
||||
tmd_name = title.tmd.title_id + ".tmd"
|
||||
tmd_out = open(output_path.joinpath(tmd_name), "wb")
|
||||
tmd_out.write(title.wad.get_tmd_data())
|
||||
tmd_out.close()
|
||||
|
||||
ticket_name = title.tmd.title_id + ".tik"
|
||||
ticket_out = open(output_path.joinpath(ticket_name), "wb")
|
||||
ticket_out.write(title.wad.get_ticket_data())
|
||||
ticket_out.close()
|
||||
|
||||
meta_name = title.tmd.title_id + ".footer"
|
||||
meta_out = open(output_path.joinpath(meta_name), "wb")
|
||||
meta_out.write(title.wad.get_meta_data())
|
||||
meta_out.close()
|
||||
|
||||
for content_file in range(0, title.tmd.num_contents):
|
||||
content_file_name = "000000" + str(binascii.hexlify(content_file.to_bytes()).decode()) + ".app"
|
||||
content_out = open(output_path.joinpath(content_file_name), "wb")
|
||||
content_out.write(title.get_content_by_index(content_file))
|
||||
content_out.close()
|
||||
|
||||
print("WAD file unpacked!")
|
||||
|
98
wiipy.py
98
wiipy.py
@ -1,61 +1,55 @@
|
||||
# "wiipy.py" from WiiPy by NinjaCheetah
|
||||
# https://github.com/NinjaCheetah/WiiPy
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
from modules.wad import *
|
||||
from modules.nus import *
|
||||
from modules.u8 import *
|
||||
from modules.ash import *
|
||||
|
||||
opts = [opt for opt in sys.argv[1:] if opt.startswith("-")]
|
||||
args = [arg for arg in sys.argv[1:] if not arg.startswith("-")]
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "WAD" in args:
|
||||
if "-u" in opts:
|
||||
if len(args) == 3:
|
||||
extract_wad_to_folder(args[1], args[2])
|
||||
print("Success!")
|
||||
sys.exit(0)
|
||||
if "-p" in opts:
|
||||
if len(args) == 3:
|
||||
pack_wad_from_folder(args[1], args[2])
|
||||
print("Success!")
|
||||
sys.exit(0)
|
||||
raise SystemExit(f"Usage: {sys.argv[0]} WAD (-u | -p) <input> <output>")
|
||||
elif "NUS" in args:
|
||||
if "-d" in opts:
|
||||
if len(args) == 2:
|
||||
download_title(args[1])
|
||||
print("Success!")
|
||||
sys.exit(0)
|
||||
elif len(args) == 3:
|
||||
download_title(args[1], args[2])
|
||||
print("Success!")
|
||||
sys.exit(0)
|
||||
raise SystemExit(f"Usage: {sys.argv[0]} NUS -d <Title ID> <Title Version (Optional)>")
|
||||
elif "U8" in args:
|
||||
if "-u" in opts:
|
||||
if len(args) == 3:
|
||||
extract_u8_to_folder(args[1], args[2])
|
||||
print("Success!")
|
||||
sys.exit(0)
|
||||
elif "-p" in opts:
|
||||
if len(args) == 3:
|
||||
pack_u8_from_folder(args[1], args[2])
|
||||
print("Success!")
|
||||
sys.exit(0)
|
||||
raise SystemExit(f"Usage: {sys.argv[0]} U8 (-u | -p) <input> <output>")
|
||||
elif "ASH" in args:
|
||||
if "-d" in opts:
|
||||
if len(args) == 2:
|
||||
decompress_ash(args[1])
|
||||
print("Success!")
|
||||
sys.exit(0)
|
||||
elif len(args) == 3:
|
||||
decompress_ash(args[1], args[2])
|
||||
print("Success!")
|
||||
sys.exit(0)
|
||||
raise SystemExit(f"Usage: {sys.argv[0]} ASH -d <input> <output (Optional)>")
|
||||
else:
|
||||
raise SystemExit(f"Usage: {sys.argv[0]} WAD (-u | -p) <input> <output>")
|
||||
parser = argparse.ArgumentParser(description="WiiPy is a simple CLI tool to manage file formats used by the Wii.")
|
||||
subparsers = parser.add_subparsers(dest="subcommand", required=True)
|
||||
|
||||
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")
|
||||
|
||||
nus_parser = subparsers.add_parser("nus", help="download a title from the NUS",
|
||||
description="download a title from the NUS")
|
||||
nus_parser.set_defaults(func=handle_nus)
|
||||
nus_parser.add_argument("tid", metavar="TID", type=str, help="Title ID to download")
|
||||
nus_parser.add_argument("-v", "--version", metavar="VERSION", type=int,
|
||||
help="Version to download")
|
||||
|
||||
u8_parser = subparsers.add_parser("u8", help="pack/unpack a U8 archive",
|
||||
description="pack/unpack a U8 archive")
|
||||
u8_parser.set_defaults(func=handle_u8)
|
||||
u8_group = u8_parser.add_mutually_exclusive_group(required=True)
|
||||
u8_group.add_argument("-p", "--pack", help="pack a directory to a U8 archive", action="store_true")
|
||||
u8_group.add_argument("-u", "--unpack", help="unpack a U8 archive to a directory", action="store_true")
|
||||
u8_parser.add_argument("input", metavar="IN", type=str, help="input file")
|
||||
u8_parser.add_argument("output", metavar="OUT", type=str, help="output file")
|
||||
|
||||
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)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
args.func(args)
|
||||
|
Loading…
x
Reference in New Issue
Block a user