def decompress_datafile(self, datafile_id: int): """This is the decompression method Given a numerical id of a datafile that is present in the forge file, this method will decompress that datafile, storing the data in the pyUbiForgeMain instance which was given to this class. It will populate self.datafiles[datafile_id].files with mappings from numerical id to file_name for each file within the datafile. It will also add the datafile id to self.new_datafiles so that external applications (such as the UI wrapper ACExplorer) will know which datafiles have been decompressed and have data to be added to the UI. """ repoulate_tree = self.datafiles[datafile_id].files == {} if datafile_id == 0 or datafile_id > 2 ** 40: return uncompressed_data_list = [] forge_file = open(os.path.join(self.pyUbiForge.CONFIG.game_folder(self.pyUbiForge.game_identifier), self.forge_file_name), 'rb') forge_file.seek(self.datafiles[datafile_id].raw_data_offset) raw_data_chunk = FileObjectDataWrapper.from_binary(self.pyUbiForge, forge_file.read(self.datafiles[datafile_id].raw_data_size)) forge_file.close() header = raw_data_chunk.read_str(8) format_version = 128 if header == b'\x33\xAA\xFB\x57\x99\xFA\x04\x10': # if compressed format_version, uncompressed_data_list = self._read_compressed_data_section(raw_data_chunk) if format_version == 128: if raw_data_chunk.read_str(8) == b'\x33\xAA\xFB\x57\x99\xFA\x04\x10': _, uncompressed_data_list_ = self._read_compressed_data_section(raw_data_chunk) uncompressed_data_list += uncompressed_data_list_ else: raise Exception('Compression Issue. Second compression block not found') if len(raw_data_chunk.read_rest()) != 0: raise Exception('Compression Issue. More data found') else: raw_data_chunk_rest = header + raw_data_chunk.read_rest() if b'\x33\xAA\xFB\x57\x99\xFA\x04\x10' in raw_data_chunk_rest: raise Exception('Compression Issue') else: uncompressed_data_list.append(raw_data_chunk_rest) # The file is not compressed if format_version == 0: self.pyUbiForge.temp_files.add(datafile_id, self.forge_file_name, datafile_id, 0, self.datafiles[datafile_id].file_name, raw_file=b''.join(uncompressed_data_list)) self.datafiles[datafile_id].files[datafile_id] = self.datafiles[datafile_id].file_name elif format_version == 128: uncompressed_data = FileObjectDataWrapper.from_binary(self.pyUbiForge, b''.join(uncompressed_data_list)) file_count = uncompressed_data.read_uint_16() index_table = [] for _ in range(file_count): index_table.append(uncompressed_data.read_struct('QIH')) # file_id, data_size (file_size + header), extra16_count (for next line) uncompressed_data.seek(index_table[-1][2] * 2, 1) for index in range(file_count): file_type, file_size, file_name_size = uncompressed_data.read_struct('3I') file_id = index_table[index][0] file_name = uncompressed_data.read_str(file_name_size).decode("utf-8") check_byte = uncompressed_data.read_uint_8() if check_byte == 1: uncompressed_data.seek(3, 1) unk_count = uncompressed_data.read_uint_32() uncompressed_data.seek(12 * unk_count, 1) elif check_byte != 0: raise Exception('Either something has gone wrong or a new value has been found here') raw_file = uncompressed_data.read_str(file_size) if file_name == '': file_name = f'{file_id:016X}' self.pyUbiForge.temp_files.add(file_id, self.forge_file_name, datafile_id, file_type, file_name, raw_file=raw_file) self.datafiles[datafile_id].files[file_id] = file_name if self.pyUbiForge.CONFIG['writeToDisk']: folder = os.path.join( self.pyUbiForge.CONFIG['dumpFolder'], self.pyUbiForge.game_identifier, self.forge_file_name, self.datafiles[datafile_id].file_name, f'{file_type:08X}' ) if os.path.isfile(os.path.join(folder, f'{file_name}.{self.pyUbiForge.game_identifier.lower()}')): duplicate = 1 while os.path.isfile(os.path.join(folder, f'{file_name}_{duplicate}.{self.pyUbiForge.game_identifier.lower()}')): duplicate += 1 path = os.path.join(folder, f'{file_name}_{duplicate}.{self.pyUbiForge.game_identifier.lower()}') else: path = os.path.join(folder, f'{file_name}.{self.pyUbiForge.game_identifier.lower()}') if not os.path.isdir(folder): os.makedirs(folder) try: open(path, 'wb').write(raw_file) except Exception as e: self.pyUbiForge.log.warn(__name__, f'Error saving temporary file with path "{path}"\n{e}') else: raise Exception('Format version not known. Please let the creator know where you found this.') if repoulate_tree: self.new_datafiles.append(datafile_id)
def file(self) -> FileObjectDataWrapper: """The raw data wrapped up in a custom data wrapper. See FileObjectDataWrapper for more information. """ return FileObjectDataWrapper.from_binary(self._pyUbiForge, self._raw_file)