def get_sarc_tree(parent_sarc: Sarc) -> Tuple[dict, list]: tree = {} modded = set() for file in sorted(parent_sarc.get_files(), key=lambda f: f.name): path_parts = Path(file.name).parts magic = file.data[0:4] nest_tree = {} if magic == b"SARC" or (magic == b"Yaz0" and file.data[0x11:0x15] == b"SARC"): nest_sarc = Sarc(file.data if not magic == b"Yaz0" else decompress(file.data)) nest_tree, mods = get_sarc_tree(nest_sarc) modded.update(mods) del nest_sarc _dict_merge( tree, reduce(lambda res, cur: {cur: res}, reversed(path_parts), nest_tree), ) if util.get_hashtable(parent_sarc.get_endianness() == Endianness.Big).is_file_modded( file.name.replace(".s", "."), bytes(file.data)): modded.add(file.name) return tree, modded
def _unbuild_file( f: Path, out: Path, content: str, mod: Path, verbose: bool, skip_texts: bool = False ) -> set: of = out / f.relative_to(mod) if not of.parent.exists(): of.parent.mkdir(parents=True, exist_ok=True) names = set() canon = get_canon_name(f.relative_to(mod)) if canon: names.add(canon) if f.name in HANDLED: pass elif f.suffix in AAMP_EXTS: of.with_suffix(f"{f.suffix}.yml").write_bytes(_aamp_to_yml(f.read_bytes())) elif f.suffix in BYML_EXTS: of.with_suffix(f"{f.suffix}.yml").write_bytes(_byml_to_yml(f.read_bytes())) elif f.suffix in SARC_EXTS: with f.open("rb") as file: data = file.read() if data[0:4] == b"Yaz0": data = decompress(data) if data[0:4] != b"SARC": return names s = oead.Sarc(data) if "bactorpack" in f.suffix: names.update(_unbuild_actorpack(s, out / content)) else: names.update(_unbuild_sarc(s, of, skip_texts=skip_texts)) del s else: of.write_bytes(f.read_bytes()) if verbose: print(f"Unbuilt {f.relative_to(mod).as_posix()}") return names
def _unbuild_actorinfo(mod: Path, content: str, out: Path): file = mod / content / "Actor" / "ActorInfo.product.sbyml" actor_info = oead.byml.from_binary(decompress(file.read_bytes())) actor_info_dir = out / content / "Actor" / "ActorInfo" actor_info_dir.mkdir(parents=True, exist_ok=True) for actor in actor_info["Actors"]: output = actor_info_dir / f"{actor['name']}.info.yml" output.write_text(oead.byml.to_text(actor), encoding="utf-8")
def open_rstb(file: Path) -> ResourceSizeTable: data = file.read_bytes() if data[0:4] == b"Yaz0": data = decompress(data) be = True rstb = ResourceSizeTable(data, be=True) random_tests = { "EventFlow/PictureMemory.bfevfl", "Camera/Demo648_0/C04-0.bcamanim", "Effect/FldObj_ScaffoldIronParts_A_01.esetlist", "Physics/TeraMeshRigidBody/MainField/9-8.hktmrb", } if not any(rstb.is_in_table(f) for f in random_tests): del rstb rstb = ResourceSizeTable(data, be=False) be = False if not any(rstb.is_in_table(f) for f in random_tests): raise ValueError("File does not appeat to be a valid BOTW RSTB") return rstb, be
def from_bytes(cls, b: bytes, path: Path = None): inst = cls() if path: inst.path = path if b[0:4] == b"Yaz0": b = yaz0.decompress(b) br = BinaryReader(initial_bytes=b) while br.tell() != br.length(): br = BinaryReader(initial_bytes=br.read()) file = HKFile() file.read(br) inst.files.append(file) return inst
def is_file_modded( self, file_name: str, data: Union[ByteString, int], flag_new: bool = True ) -> bool: """Checks a file to see if it has been modified. Automatically decompresses yaz0 data. Args: file_name (str): The canonical resource path of the file to check data (Union[ByteString, int]): Either the file data (as a byteslike object) or an xxh32 hash as an int flag_new (bool, optional): Whether to flag new files (not in vanilla BOTW) as modified. Defaults to True. Returns: bool: Returns whether the file's hash matches a known version of the hash for the original version. """ if file_name not in self._table: return flag_new else: if isinstance(data, int): return data not in self._table[file_name] if data[0:4] == b"Yaz0": data = decompress(data) return xxh32_intdigest(data) not in self._table[file_name]
def _if_unyaz(data: bytes) -> bytes: return data if data[0:4] != b"Yaz0" else decompress(data)
def _aamp_to_yml(file_bytes: bytes) -> bytes: if file_bytes[0:4] == b"Yaz0": file_bytes = decompress(file_bytes) return aamp.ParameterIO.from_binary(file_bytes).to_text().encode("utf8")
def _byml_to_yml(file_bytes: bytes) -> bytes: if file_bytes[0:4] == b"Yaz0": file_bytes = decompress(file_bytes) return oead.byml.to_text(oead.byml.from_binary(file_bytes)).encode("utf8")
def unyaz_if_yazd(data: ByteString) -> ByteString: return data if data[0:4] != b"Yaz0" else memoryview(decompress(data))
def init(self): with open(self.file + '', 'rb') as f: data = yaz0.decompress(f.read()) data_dec = Sarc(data) return data_dec