def _get_tot_size(self): tot = cur_lvl_size = align_to(self.size, self.section_header.lvls[-1].block_size) for n in reversed(range(IVFCSuperblock.IVFC_MAX_LEVEL - 1)): cur_lvl_size = align_to( cur_lvl_size // self.section_header.lvls[n].block_size * 0x20, self.section_header.lvls[n - 1].block_size) tot += cur_lvl_size return tot
def _gen_dir_metadata(self, dir_entry): if self.prev_dir is not None: self.prev_dir.next = dir_entry dir_entry.prev, self.prev_dir = self.prev_dir, dir_entry dir_entry.hash = self._calc_path_hash(dir_entry.offsets['Parent'], dir_entry.name.encode()) for child in dir_entry.childs: self.dir_nb += 1 if dir_entry.offsets['Child'] == ROMFS_ENTRY_EMPTY: dir_entry.offsets['Child'] = self.dir_table_size child.offsets['Parent'] = dir_entry.offsets['Self'] child.offsets['Self'] = self.dir_table_size self.dir_table_size += DirEntry.ROMFS_DIRENTRY_LENGTH + align_to( len(child.name), 4) self._gen_dir_metadata(child) if child != dir_entry.childs[-1]: child.offsets['Sibling'] = self.dir_table_size else: child.offsets['Sibling'] = ROMFS_ENTRY_EMPTY for file_entry in dir_entry.files: self.file_nb += 1 if self.prev_file is not None: self.prev_file.next = file_entry file_entry.prev, self.prev_file = self.prev_file, file_entry if dir_entry.offsets['File'] == ROMFS_ENTRY_EMPTY: dir_entry.offsets['File'] = self.file_table_size file_entry.offsets['Parent'] = dir_entry.offsets['Self'] file_entry.offsets['Self'] = self.file_table_size self.file_table_size += FileEntry.ROMFS_FILEENTRY_LENGTH + align_to( len(file_entry.name), 4) if file_entry != dir_entry.files[-1]: file_entry.offsets['Sibling'] = self.file_table_size else: file_entry.offsets['Sibling'] = ROMFS_ENTRY_EMPTY file_entry.hash = self._calc_path_hash( file_entry.offsets['Parent'], file_entry.name.encode()) file_entry.offsets['Size'] = file_entry.size file_entry.offsets['Data'] = self.data_size self.data_size += align_to(file_entry.size, 0x10)
def _buffered_repack(self, block_size=0x4000, disp=True): lvl_1, lvl_2, lvl_3, lvl_4, lvl_5 = self._gen_hash_tree( block_size=block_size, disp=disp) if disp: print('Writing IVFC levels...') yield lvl_1 yield (align_to(self.lvl_1_size, block_size) - self.lvl_1_size) * b'\0' yield lvl_2 yield (align_to(self.lvl_2_size, block_size) - self.lvl_2_size) * b'\0' yield lvl_3 yield (align_to(self.lvl_3_size, block_size) - self.lvl_3_size) * b'\0' yield lvl_4 yield (align_to(self.lvl_4_size, block_size) - self.lvl_4_size) * b'\0' yield lvl_5 yield (align_to(self.lvl_5_size, block_size) - self.lvl_5_size) * b'\0' if disp: print('Writing RomFS...') written = 0 for buf in super(HashTreeWrappedRomFS, self)._buffered_repack(disp=disp): written += len(buf) yield buf yield (align_to(written, block_size) - written) * b'\0'
def read(self, length=None): if self.crypto == 'None' or self.crypto == 'BTKR' or self.crypto == 'XTS': data = super(NCA3.SectionInNCA3, self).read(length) return data elif self.crypto == 'CTR': aligned_length = align_to(length + self.sector_offset, 0x10) dec_data = self.cipher.decrypt( super(NCA3.SectionInNCA3, self).read(aligned_length)) if aligned_length != length: try: return dec_data[self.sector_offset:length + self.sector_offset] finally: # Seek to what the offset should be if not for the crypto self.seek(self.ifo + self.tell() - aligned_length + length) else: return dec_data[:length]