From 9a89f8024725ad62c2eead8be1ef0a904dbac9ae Mon Sep 17 00:00:00 2001 From: NinjaCheetah <58050615+NinjaCheetah@users.noreply.github.com> Date: Wed, 8 Jan 2025 20:09:34 -0500 Subject: [PATCH] Added lz77 command to allow for decompressing LZ77-compressed files --- commands/archive/lz77.py | 28 ++++++++++++++++++++++++++++ commands/archive/u8.py | 9 ++++++++- wiipy.py | 25 ++++++++++++++++++++++++- 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 commands/archive/lz77.py diff --git a/commands/archive/lz77.py b/commands/archive/lz77.py new file mode 100644 index 0000000..e797158 --- /dev/null +++ b/commands/archive/lz77.py @@ -0,0 +1,28 @@ +# "commands/archive/lz77.py" from WiiPy by NinjaCheetah +# https://github.com/NinjaCheetah/WiiPy + +import pathlib +import libWiiPy +from modules.core import fatal_error + + +def handle_lz77_compress(args): + print("Compression is not implemented yet.") + + +def handle_lz77_decompress(args): + input_path = pathlib.Path(args.input) + if args.output is not None: + output_path = pathlib.Path(args.output) + else: + output_path = pathlib.Path(input_path.name + ".out") + + if not input_path.exists(): + fatal_error(f"The specified file \"{input_path}\" does not exist!") + + lz77_data = input_path.read_bytes() + data = libWiiPy.archive.decompress_lz77(lz77_data) + output_path.write_bytes(data) + + print("LZ77 file decompressed!") + diff --git a/commands/archive/u8.py b/commands/archive/u8.py index 5fe4a29..a7a128b 100644 --- a/commands/archive/u8.py +++ b/commands/archive/u8.py @@ -26,8 +26,15 @@ def handle_u8_unpack(args): if not input_path.exists(): fatal_error(f"The specified input file \"{input_path}\" does not exist!") + + u8_data = input_path.read_bytes() + # U8 archives are sometimes compressed. In the event that the provided data is LZ77 data, assume it's a compressed + # U8 archive and decompress it before continuing. Standard checks will then catch it if it was something else. + if u8_data[0:4] == b'LZ77': + u8_data = libWiiPy.archive.decompress_lz77(u8_data) + # Output path is deliberately not checked in any way because libWiiPy already has those checks, and it's easier # and cleaner to only have one component doing all the checks. - libWiiPy.archive.extract_u8(input_path.read_bytes(), str(output_path)) + libWiiPy.archive.extract_u8(u8_data, str(output_path)) print("U8 archive unpacked!") diff --git a/wiipy.py b/wiipy.py index 8d72307..6f73ac1 100644 --- a/wiipy.py +++ b/wiipy.py @@ -5,6 +5,7 @@ import argparse from importlib.metadata import version from commands.archive.ash import * +from commands.archive.lz77 import * from commands.archive.theme import * from commands.archive.u8 import * from commands.nand.emunand import * @@ -30,7 +31,7 @@ if __name__ == "__main__": # 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_subparsers = ash_parser.add_subparsers(title="emunand", dest="emunand", required=True) + ash_subparsers = ash_parser.add_subparsers(title="ash", dest="ash", required=True) # ASH compress parser. ash_compress_parser = ash_subparsers.add_parser("compress", help="compress a file into an ASH file", description="compress a file into an ASH file; by default, this " @@ -146,6 +147,28 @@ if __name__ == "__main__": iospatch_parser.add_argument("-ns", "--no-shared", action="store_true", help="set all patched content to be non-shared") + # Argument parser for the LZ77 subcommand. + lz77_parser = subparsers.add_parser("lz77", help="compress/decompress data using LZ77 compression", + description="compress/decompress data using LZ77 compression") + lz77_subparsers = lz77_parser.add_subparsers(title="lz77", dest="lz77", required=True) + # LZ77 compress parser. + lz77_compress_parser = lz77_subparsers.add_parser("compress", help="compress a file with LZ77 compression", + description="compress a file with LZ77 compression; by default, " + "this will output to .lz77") + lz77_compress_parser.set_defaults(func=handle_lz77_compress) + lz77_compress_parser.add_argument("input", metavar="IN", type=str, help="file to compress") + lz77_compress_parser.add_argument("-o", "--output", metavar="OUT", type=str, + help="file to output the compressed data to (optional)") + # LZ77 decompress parser. + lz77_decompress_parser = lz77_subparsers.add_parser("decompress", help="decompress an LZ77-compressed file", + description="decompress an LZ77-compressed file; by default, " + "this will output to .out") + lz77_decompress_parser.set_defaults(func=handle_lz77_decompress) + lz77_decompress_parser.add_argument("input", metavar="IN", type=str, + help="LZ77-compressed file to decompress") + lz77_decompress_parser.add_argument("-o", "--output", metavar="OUT", type=str, + help="file to output the decompressed data to (optional)") + # Argument parser for the NUS subcommand. nus_parser = subparsers.add_parser("nus", help="download data from the NUS", description="download from the NUS")