def load_rstb(be: bool, file: Path = None) -> ResourceSizeTable: table = ResourceSizeTable(b"", be=be) if not file: ver = "wiiu" if be else "switch" file = EXEC_DIR / "data" / ver / "rstb.json" ref_contents = json.loads(file.read_text(), encoding="utf-8") def parse_hash(file: str) -> int: try: return int(file) except ValueError: return crc32(file.encode("utf8")) table.crc32_map = {parse_hash(k): v for k, v in ref_contents["hash_map"].items()} table.name_map = {k: v for k, v in ref_contents["name_map"].items()} return table
def load_rstb(be: bool, file: Path = None) -> ResourceSizeTable: table = ResourceSizeTable(b'', be=be) if not file: ver = 'wiiu' if be else 'switch' file = EXEC_DIR / 'data' / ver / 'rstb.json' ref_contents = json.loads(file.read_text(), encoding='utf-8') def parse_hash(file: str) -> int: try: return int(file) except ValueError: return crc32(file.encode('utf8')) table.crc32_map = { parse_hash(k): v for k, v in ref_contents['hash_map'].items() } table.name_map = {k: v for k, v in ref_contents['name_map'].items()} return table
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 _fix_rstb_resource_size(path: Path, rel_path: Path, table: rstb.ResourceSizeTable, wiiu: bool, is_aoc: bool): resource_path = _get_resource_path_for_rstb(rel_path, is_aoc) if not _should_be_listed_in_rstb(Path(resource_path), rel_path=rel_path): sys.stderr.write( f'{Fore.WHITE}{rel_path}{Style.RESET_ALL} ({resource_path})\n') return resource_size = size_calculator.calculate_file_size(str(path), wiiu=wiiu) if resource_size == 0: sys.stderr.write( f'{Fore.WHITE}{rel_path}{Style.RESET_ALL} ({resource_path}) {Style.BRIGHT}{Fore.YELLOW}*** complex ***{Style.RESET_ALL}\n' ) table.delete_entry(resource_path) return notes = [] prev_resource_size = 0 if not table.is_in_table(resource_path): notes.append(f'{Style.BRIGHT}{Fore.RED}*** NEW ***{Style.RESET_ALL}') else: prev_resource_size = table.get_size(resource_path) sys.stderr.write( f'{Fore.WHITE}{rel_path}{Style.RESET_ALL} ({resource_path}) [0x%x -> 0x%x bytes] %s\n' % (prev_resource_size, resource_size, ' '.join(notes))) table.set_size(resource_path, resource_size)
def merge_rstb(table: ResourceSizeTable, changes: dict) -> (ResourceSizeTable, List[str]): """ Merges changes from a list of RSTB mods into a single RSTB :param table: The base RSTB to merge into. This will be directly modified. :type table: class:`rstb.ResourceSizeTable` :param changes: A dict of resources and their RSTB sizes. :type changes: dict of str: int :param verbose: Whether to log changes in full detail. Defaults to false. :type verbose: bool, optional :returns: Returns a list of strings recording RSTB changes made. :rtype: list of str """ change_list = [] spaces = ' ' change_count = { 'updated': 0, 'deleted': 0, 'added': 0, 'warning': 0 } for change in changes: if zlib.crc32(change.encode()) in table.crc32_map: newsize = int(changes[change]['size']) if newsize == 0: if not changes[change]['leave']: if change.endswith('.bas') or change.endswith('.baslist'): change_list.append(( f'{spaces}WARNING: Could not calculate or safely remove RSTB size for' f'{change}. This may need to be corrected manually, or the game could ' 'become unstable', False )) change_count['warning'] += 1 continue else: table.delete_entry(change) change_list.append( (f'{spaces}Deleted RSTB entry for {change}', True)) change_count['deleted'] += 1 continue else: change_list.append( (f'{spaces}Skipped deleting RSTB entry for {change}', True)) continue oldsize = table.get_size(change) if newsize <= oldsize: if changes[change]['shrink']: table.set_size(change, newsize) change_list.append(( f'{spaces}Updated RSTB entry for {change} from {oldsize} to {newsize}', True )) change_count['updated'] += 1 continue else: change_list.append( (f'{spaces}Skipped updating RSTB entry for {change}', True)) continue elif newsize > oldsize: table.set_size(change, newsize) change_list.append( (f'{spaces}Updated RSTB entry for {change} from {oldsize} to {newsize}', True)) change_count['updated'] += 1 else: newsize = int(changes[change]['size']) if newsize == 0: change_list.append( (f'{spaces}Could not calculate size for new entry {change}, skipped', True)) continue table.set_size(change, newsize) change_list.append( (f'{spaces}Added new RSTB entry for {change} with value {newsize}', True)) change_count['added'] += 1 change_list.append(( f'RSTB merge complete: updated {change_count["updated"]} entries, deleted' f' {change_count["deleted"]} entries, added {change_count["added"]} entries', False )) return table, change_list