def main() -> None: parser = argparse.ArgumentParser(description="Tool for converting CookData in LoZ:BotW") parser.add_argument( "file", help="Filename to be converted (accepts wildcards for converting multiple files)", ) parser.add_argument("output", help="Output type: bas or yml") parser.add_argument( "-n", "--names", help="Output with human-readable names", action="store_true" ) parser.add_argument( "-d", "--digits", help="Output with machine-readable values", action="store_true" ) args = parser.parse_args() if not args.names and not args.digits: raise RuntimeError("-n or -d must be specified!") if args.names and args.digits: raise RuntimeError("Only one of -n and -d must be specified!") output_str: str = str.lower(args.output) output_yaml: bool = True if output_str == "yml" or output_str == "yaml" else False output_aamp: bool = True if output_str == "bas" or output_str == "aamp" else False if not output_yaml and not output_aamp: raise RuntimeError("Output type must be one of: 'yml', 'yaml', 'bas', or 'aamp'") folder = Path(args.file).parent.resolve() filename = Path(args.file).name for file in folder.glob(filename): ext: str = file.suffix pio: ParameterIO = ParameterIO() if ext == ".bas": pio = ParameterIO.from_binary(file.read_bytes()) elif ext == ".yml": pio = ParameterIO.from_text(file.read_text()) try: if args.names: convert.to_names(pio) else: convert.to_numbers(pio) except: output_type: str = "names" if args.names else "numbers" print(f"{file.name} already had {output_type}! Skipping...") continue new_file: Path = Path() if output_yaml: new_file = file.with_suffix(".yml") to_write = ParameterIO.to_text(pio) new_file.write_text(to_write) else: new_file = file.with_suffix(".bas") to_write = ParameterIO.to_binary(pio) new_file.write_bytes(to_write)
def nested_patch(pack: oead.Sarc, nest: dict) -> Tuple[oead.SarcWriter, dict]: new_sarc: oead.SarcWriter = oead.SarcWriter.from_sarc(pack) failures: dict = {} for file, stuff in nest.items(): file_bytes = pack.get_file(file).data yazd = file_bytes[0:4] == b"Yaz0" file_bytes = util.decompress(file_bytes) if yazd else file_bytes if isinstance(stuff, dict): sub_sarc = oead.Sarc(file_bytes) new_sub_sarc, sub_failures = nested_patch(sub_sarc, stuff) for failure in sub_failures: failure[file + "//" + failure] = sub_failures[failure] del sub_sarc new_bytes = bytes(new_sub_sarc.write()[1]) new_sarc.files[file] = new_bytes if not yazd else util.compress( new_bytes) elif isinstance(stuff, ParameterList): try: if file_bytes[0:4] == b"AAMP": aamp_contents = ParameterIO.from_binary(file_bytes) try: file_ext = os.path.splitext(file)[1] aamp_contents = shop_merge( aamp_contents, file_ext.replace(".", ""), stuff.lists["Additions"], stuff.lists["Removals"], ) aamp_bytes = ParameterIO.to_binary(aamp_contents) except: # pylint: disable=bare-except raise RuntimeError( f"AAMP file {file} could be merged.") del aamp_contents new_bytes = aamp_bytes if not yazd else util.compress( aamp_bytes) cache_merged_shop(file, new_bytes) else: raise ValueError( "Wait, what the heck, this isn't an AAMP file?!") except ValueError: new_bytes = pack.get_file(file).data print(f"Deep merging {file} failed. No changes were made.") new_sarc.files[file] = oead.Bytes(new_bytes) return new_sarc, failures
def threaded_merge(item) -> Tuple[str, dict]: """Deep merges an individual file, suitable for multiprocessing""" file, stuff = item failures = {} try: base_file = util.get_game_file(file, file.startswith(util.get_dlc_path())) except FileNotFoundError: return "", {} if (util.get_master_modpack_dir() / file).exists(): base_file = util.get_master_modpack_dir() / file file_ext = os.path.splitext(file)[1] if file_ext in util.SARC_EXTS and (util.get_master_modpack_dir() / file).exists(): base_file = util.get_master_modpack_dir() / file file_bytes = base_file.read_bytes() yazd = file_bytes[0:4] == b"Yaz0" file_bytes = file_bytes if not yazd else util.decompress(file_bytes) magic = file_bytes[0:4] if magic == b"SARC": new_sarc, sub_failures = nested_patch(oead.Sarc(file_bytes), stuff) del file_bytes new_bytes = bytes(new_sarc.write()[1]) for failure, contents in sub_failures.items(): print(f"Some patches to {failure} failed to apply.") failures[failure] = contents elif magic == b"AAMP": try: aamp_contents = ParameterIO.from_binary(file_bytes) try: aamp_contents = shop_merge( aamp_contents, file_ext.replace(".", ""), stuff.lists["Additions"], stuff.lists["Removals"], ) aamp_bytes = ParameterIO.to_binary(aamp_contents) except: # pylint: disable=bare-except raise RuntimeError(f"AAMP file {file} could be merged.") del aamp_contents new_bytes = aamp_bytes if not yazd else util.compress(aamp_bytes) except ValueError: new_bytes = file_bytes del file_bytes print(f"Deep merging file {file} failed. No changes were made.") else: raise ValueError(f"{file} is not a SARC or AAMP file.") new_bytes = new_bytes if not yazd else util.compress(new_bytes) output_file = util.get_master_modpack_dir() / file if base_file == output_file: output_file.unlink() output_file.parent.mkdir(parents=True, exist_ok=True) output_file.write_bytes(new_bytes) del new_bytes if magic == b"SARC": util.vprint(f"Finished patching files inside {file}") else: util.vprint(f"Finished patching {file}") return util.get_canon_name(file), failures