def run(self, file_id: Union[str, int], forge_file_name: str, datafile_id: int, options: Union[List[dict], None] = None): if options is not None: self._options = options # should do some validation here file_types = [ file_type.upper() for file_type in self._options[0].get("File Types", "").split(';') ] for file_id in pyUbiForge.forge_files[forge_file_name].datafiles[ datafile_id].files.keys(): data = pyUbiForge.temp_files(file_id, forge_file_name, datafile_id) if data is None: logging.warning(f"Failed to find file {file_id:016X}") return if data.file_type not in file_types: continue output = pyUbiForge.read_file(data.file) if output is None: logging.warning(data.file_name) out_file = open( os.path.join( pyUbiForge.CONFIG.get('dumpFolder', 'output'), f'{pyUbiForge.game_identifier()}_{data.file_name}_{file_id:016X}.format' ), 'w') pyUbiForge.read_file(data.file, out_file)
def run(self, file_id: Union[str, int], forge_file_name: str, datafile_id: int, options: Union[List[dict], None] = None): if options is not None: self._options = options # should do some validation here # TODO add select directory option save_folder = pyUbiForge.CONFIG.get('dumpFolder', 'output') data = pyUbiForge.temp_files(file_id, forge_file_name, datafile_id) if data is None: logging.warning(f"Failed to find file {file_id:016X}") return data_block_name = data.file_name data_block: DataBlock = pyUbiForge.read_file(data.file) if self._options[0]["Export Method"] == 'Wavefront (.obj)': obj_handler = mesh.ObjMtl(data_block_name, save_folder) for data_block_entry_id in data_block.files: data = pyUbiForge.temp_files(data_block_entry_id) if data is None: logging.warning(f"Failed to find file {data_block_entry_id:016X}") continue if data.file_type in ('0984415E', '3F742D26'): # entity and entity group entity: Entity = pyUbiForge.read_file(data.file) if entity is None: logging.warning(f"Failed reading file {data.file_name} {data.file_id:016X}") continue for nested_file in entity.nested_files: if nested_file.file_type == 'EC658D29': # visual nested_file: Visual if '01437462' in nested_file.nested_files.keys(): # LOD selector lod_selector: LODSelector = nested_file.nested_files['01437462'] mesh_instance_data: MeshInstanceData = lod_selector.lod[self._options[0]['LOD']] elif '536E963B' in nested_file.nested_files.keys(): # Mesh instance mesh_instance_data: MeshInstanceData = nested_file.nested_files['536E963B'] else: logging.warning(f"Could not find mesh instance data for {data.file_name} {data.file_id:016X}") continue if mesh_instance_data is None: logging.warning(f"Failed to find file {data.file_name}") continue model_data = pyUbiForge.temp_files(mesh_instance_data.mesh_id) if model_data is None: logging.warning(f"Failed to find file {mesh_instance_data.mesh_id:016X}") continue model: mesh.BaseModel = pyUbiForge.read_file(model_data.file) if model is None or model.vertices is None: logging.warning(f"Failed reading model file {model_data.file_name} {model_data.file_id:016X}") continue transform = entity.transformation_matrix if len(mesh_instance_data.transformation_matrix) == 0: obj_handler.export(model, model_data.file_name, transform) else: for trm in mesh_instance_data.transformation_matrix: obj_handler.export(model, model_data.file_name, numpy.matmul(transform, trm)) logging.info(f'Exported {model_data.file_name}') else: logging.info(f'File type "{data.file_type}" is not currently supported. It has been skipped') obj_handler.save_and_close() logging.info(f'Finished exporting {data_block_name}.obj')
def run(self, file_id: Union[str, int], forge_file_name: str, datafile_id: int, options: Union[List[dict], None] = None): if options is not None: self._options = options # should do some validation here # TODO add select directory option save_folder = pyUbiForge.CONFIG.get('dumpFolder', 'output') data = pyUbiForge.temp_files(file_id, forge_file_name, datafile_id) if data is None: logging.warning(f"Failed to find file {file_id:016X}") return model_name = data.file_name if self._options[0]["Export Method"] == 'Wavefront (.obj)': model: mesh.BaseModel = pyUbiForge.read_file(data.file) if model is not None: obj_handler = mesh.ObjMtl(model_name, save_folder) obj_handler.export(model, model_name) obj_handler.save_and_close() logging.info(f'Exported {file_id:016X}') else: logging.warning(f'Failed to export {file_id:016X}') elif self._options[0]["Export Method"] == 'Collada (.dae)': obj_handler = mesh.Collada(model_name, save_folder) obj_handler.export(file_id, forge_file_name, datafile_id) obj_handler.save_and_close() logging.info(f'Exported {file_id:016X}') elif self._options[0]["Export Method"] == 'Send to Blender (experimental)': model: mesh.BaseModel = pyUbiForge.read_file(data.file) if model is not None: c = Client(('localhost', 6163)) cols = [Image.new('RGB', (1024, 1024), (128, 0, 0)) for _ in range(len(model.bones))] r = 5 for mesh_index, m in enumerate(model.meshes): c.send({ 'type': 'MESH', 'verts': tuple(tuple(vert) for vert in model.vertices), 'faces': tuple(tuple(face) for face in model.faces[mesh_index][:m['face_count']]) }) for vtx in model.vert_table: x, y = vtx['vt'].astype(numpy.float) / 2 for index, bone_index in enumerate(vtx['bn']): if vtx['bw'][index] > 0: draw = ImageDraw.Draw(cols[bone_index]) draw.ellipse((x - r, y - r, x + r, y + r), fill=(vtx['bw'][index], vtx['bw'][index], vtx['bw'][index])) for index, im in enumerate(cols): im.save(f'{save_folder}/{model_name}_{index}.png') print(f'saved {save_folder}/{model_name}_{index}.png') c.send({ 'type': 'BONES', 'bone_id': [bone.bone_id for bone in model.bones], 'mat': [bone.transformation_matrix for bone in model.bones], })
def read_file(data, file_id): try: out_file = FileObject() pyUbiForge.read_file(data.file, out_file) out_file.close( os.path.join( pyUbiForge.CONFIG.get('dumpFolder', 'output'), f'{pyUbiForge.game_identifier()}_{data.file_name}_{file_id:016X}.format' )) except Exception as e: logging.warning(e)
def run(self, file_id: Union[str, int], forge_file_name: str, datafile_id: int, options: Union[List[dict], None] = None): data = pyUbiForge.temp_files(file_id, forge_file_name, datafile_id) if data is None: logging.warning(f"Failed to find file {file_id:016X}") return if not os.path.isdir(pyUbiForge.CONFIG.get('dumpFolder', 'output')): os.makedirs(pyUbiForge.CONFIG.get('dumpFolder', 'output')) out_file = open( os.path.join( pyUbiForge.CONFIG.get('dumpFolder', 'output'), f'{pyUbiForge.game_identifier()}_{data.file_name}_{file_id:016X}.format' ), 'w' ) pyUbiForge.read_file(data.file, out_file) logging.info("Finished Formatting")
def get_material_ids(file_id: int) -> Material: data = pyUbiForge.temp_files(file_id) if data is None: logging.warning(f"Failed to find file {file_id:016X}") return Material(f'{file_id:016X}', missing_no=True) name = data.file_name material_set_id = pyUbiForge.read_file(data.file).material_set data = pyUbiForge.temp_files(material_set_id) if data is None: logging.warning(f"Failed to find file {material_set_id:016X}") return Material(name, missing_no=True) material = pyUbiForge.read_file(data.file) material.name = name return material
def run(self, file_id: Union[str, int], forge_file_name: str, datafile_id: int, options: Union[List[dict], None] = None): # TODO add select directory option save_folder = pyUbiForge.CONFIG.get('dumpFolder', 'output') data = pyUbiForge.temp_files(file_id, forge_file_name, datafile_id) if data is None: logging.warning(f"Failed to find file {file_id:016X}") return file_name = data.file_name minimap_textures: MMClass = pyUbiForge.read_file(data.file) output_image = None tile_width = tile_height = 128 for y in range(minimap_textures.width): for x in range(minimap_textures.height): texture_file_id = minimap_textures.image_ids[ y * minimap_textures.height + x] texture_data = pyUbiForge.temp_files(texture_file_id) if texture_data is None: logging.warning( f"Failed to find file {texture_file_id:016X}") continue texture: TextureClass = pyUbiForge.read_file(texture_data.file) if output_image is None: tile_width = struct.unpack('<I', texture.dwWidth)[0] tile_height = struct.unpack('<I', texture.dwHeight)[0] output_image = Image.new( 'RGBA', (tile_width * minimap_textures.width, tile_height * minimap_textures.height)) temp_image = Image.open(BytesIO(texture.dds_string)) output_image.paste( temp_image, (tile_width * x, tile_height * minimap_textures.height - tile_height * (y + 1))) logging.info(f'Written {y+1} row of {minimap_textures.width}') if output_image is None: logging.info('No Minimap to export') else: output_image.save(f'{save_folder}/{file_name}.png')
def export_dds(file_id: int, save_folder: str, forge_file_name: Union[None, str] = None, datafile_id: Union[None, int] = None): data = pyUbiForge.temp_files(file_id, forge_file_name, datafile_id) if data is None: logging.warning(f"Failed to find file {file_id:016X}") return save_path = os.path.join(save_folder, f'{data.file_name}.dds') if os.path.isfile(save_path): logging.info(f'Texture "{data.file_name}" already exported') return save_path tex = pyUbiForge.read_file(data.file) tex.export_dds(save_path) logging.info(f'Texture "{data.file_name}" exported') return save_path
def export(self, model_file_id: int, forge_file_name: str = None, datafile_id: int = None, transformation_matrix: numpy.ndarray = None) -> None: """ when called will load and export the mesh if it hasn't been :return: None """ if not self.is_exported(model_file_id): data = pyUbiForge.temp_files(model_file_id, forge_file_name, datafile_id) if data is None: logging.warning(f"Failed to find file {model_file_id:016X}") return model = pyUbiForge.read_file(data.file) if model is None: # sometimes reading the model fails return self._models_exported[model_file_id] = [] # write models for mesh_index, mesh in enumerate(model.meshes): faces = model.faces[mesh_index][:mesh['face_count']].ravel() new_value_slice, faces = numpy.unique(faces, return_inverse=True) vertices = model.vertices[new_value_slice] texture_vertices = model.texture_vertices[new_value_slice] geometry_id = f'{model_file_id}-mesh-{mesh_index}' model_name = f'{data.file_name}-{mesh_index}' material_name = f'{self._mtl_handler.get(model.materials[mesh_index]).name}-material' self._models_exported[model_file_id].append( [geometry_id, model_name, material_name]) self._dae.write( f''' <geometry id="{model_file_id}-mesh-{mesh_index}" name="{model_name}"> <mesh> <source id="{model_file_id}-mesh-positions-{mesh_index}"> <float_array id="{model_file_id}-mesh-positions-array-{mesh_index}" count="{vertices.size}">{plaintext_array(vertices)}</float_array> <technique_common> <accessor source="#{model_file_id}-mesh-positions-array-{mesh_index}" count="{len(vertices)}" stride="3"> <param name="X" type="float"/> <param name="Y" type="float"/> <param name="Z" type="float"/> </accessor> </technique_common> </source> ''') if model.normals is not None: normals = model.normals[new_value_slice] self._dae.write( f''' <source id="{model_file_id}-mesh-normals-{mesh_index}"> <float_array id="{model_file_id}-mesh-normals-array-{mesh_index}" count="{normals.size}">{plaintext_array(normals)}</float_array> <technique_common> <accessor source="#{model_file_id}-mesh-normals-array-{mesh_index}" count="{len(normals)}" stride="3"> <param name="X" type="float"/> <param name="Y" type="float"/> <param name="Z" type="float"/> </accessor> </technique_common> </source> ''') self._dae.write( f''' <source id="{model_file_id}-mesh-map-0-{mesh_index}"> <float_array id="{model_file_id}-mesh-map-0-array-{mesh_index}" count="{texture_vertices.size}">{plaintext_array(texture_vertices)}</float_array> <technique_common> <accessor source="#{model_file_id}-mesh-map-0-array-{mesh_index}" count="{len(texture_vertices)}" stride="2"> <param name="S" type="float"/> <param name="T" type="float"/> </accessor> </technique_common> </source> <vertices id="{model_file_id}-mesh-vertices-{mesh_index}"> <input semantic="POSITION" source="#{model_file_id}-mesh-positions-{mesh_index}"/> </vertices> <triangles material="{material_name}" count="{len(faces)}"> <input semantic="VERTEX" source="#{model_file_id}-mesh-vertices-{mesh_index}" offset="0"/> ''') if model.normals is not None: self._dae.write( f''' <input semantic="NORMAL" source="#{model_file_id}-mesh-normals-{mesh_index}" offset="0"/> ''') self._dae.write( f''' <input semantic="TEXCOORD" source="#{model_file_id}-mesh-map-0-{mesh_index}" offset="0" set="0"/> <p>{plaintext_array(faces)}</p> </triangles> </mesh> </geometry> ''') if transformation_matrix is None: transformation_matrix = numpy.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) for geometry_id, model_name, material_name in self._models_exported[ model_file_id]: self._library_visual_scenes.append( f''' <node id="{model_name}" name="{model_name}" type="NODE"> <matrix sid="transform">{plaintext_array(transformation_matrix)}</matrix> <instance_geometry url="#{geometry_id}" name="{model_name}"> <bind_material> <technique_common> <instance_material symbol="{material_name}" target="#{material_name}"/> </technique_common> </bind_material> </instance_geometry> </node> ''')