def get_replace_strings(self): try: newline = "\n" old = self.e_name_old.toPlainText() new = self.e_name_new.toPlainText() old = old.split(newline) new = new.split(newline) if len(old) != len(new): interaction.showdialog( f"Old {len(old)} and new {len(new)} must have the same amount of lines!" ) return list(zip(old, new)) except BaseException as err: print(err)
def create_ovl(self, ovl_dir): # clear the ovl self.ovl_data = OvlFile(progress_callback=self.update_progress) self.game_changed() # read tables for constants mimes_table = {} tables_dir = os.path.join(os.getcwd(), "dicts") self.read_table(os.path.join(tables_dir, "mimes.txt"), mimes_table) try: self.ovl_data.create(ovl_dir, mime_names_dict=mimes_table) except Exception as ex: traceback.print_exc() interaction.showdialog(str(ex)) self.update_gui_table()
def closeEvent(self, event): if self.file_widget.dirty: quit_msg = f"Quit? You will lose unsaved work on {os.path.basename(self.file_widget.filepath)}!" if not interaction.showdialog(quit_msg, ask=True): event.ignore() return event.accept()
def check_length(name_tups): # Ask and return true if error is found and process should be stopped for old, new in name_tups: if len(old) != len(new): if showdialog(f"WARNING: length of '{old}' [{len(old)} chars] and '{new}' [{len(new)} chars] don't match!\n" f"Stop hashing?", ask=True): return True
def load_lua(ovl_data, lua_file_path, lua_sized_str_entry): # read lua # inject lua buffer # update sized string # IMPORTANT: all meta data of the lua except the sized str entries lua size value seems to just be meta data, can be zeroed with open(lua_file_path, "rb") as lua_stream: # load the new buffer buffer_bytes = lua_stream.read() if b"DECOMPILER ERROR" in buffer_bytes: confirmed = showdialog( f"{lua_file_path} has not been successfully decompiled and may crash your game. Inject anyway?", ask=True) if not confirmed: return buff_size = len(buffer_bytes) # update the buffer lua_sized_str_entry.data_entry.update_data((buffer_bytes, )) ss_len = len(lua_sized_str_entry.pointers[0].data) / 4 ss_data = struct.unpack("<{}I".format(int(ss_len)), lua_sized_str_entry.pointers[0].data) ss_new = struct.pack("<{}I".format(int(ss_len)), buff_size, *ss_data[1:]) lua_sized_str_entry.pointers[0].update_data(ss_new, update_copies=True)
def extract_all(self): if self.is_open_ovl(): out_dir = QtWidgets.QFileDialog.getExistingDirectory( self, 'Output folder', self.cfg.get("dir_extract", "C://"), ) if out_dir: self.cfg["dir_extract"] = out_dir try: out_paths, error_files, skip_files = self.ovl_data.extract( out_dir, self.show_temp_files) interaction.skip_messages(error_files, skip_files) except Exception as ex: traceback.print_exc() interaction.showdialog(str(ex))
def load(self): if self.file_widget.filepath: self.file_widget.dirty = False self.update_progress("Reading OVL " + self.file_widget.filepath, value=0, vmax=0) try: self.ovl_data.load(self.file_widget.filepath, commands=self.commands, hash_table=self.hash_table) self.ovl_data.load_archives() except Exception as ex: traceback.print_exc() interaction.showdialog(str(ex)) self.update_gui_table() game = get_game(self.ovl_data) self.game_container.entry.setText(game)
def inject_files(self, files): """Tries to inject files into self.ovl_data""" if files: self.cfg["dir_inject"] = os.path.dirname(files[0]) try: error_files, foreign_files = self.ovl_data.inject( files, self.show_temp_files) self.file_widget.dirty = True if foreign_files: if interaction.showdialog( f"Do you want to add {len(foreign_files)} files to this ovl?", ask=True): self.ovl_data.add_files(foreign_files) self.update_gui_table() except Exception as ex: traceback.print_exc() interaction.showdialog(str(ex))
def save_included_ovls(self): if self.is_open_ovl(): filelist_src = QtWidgets.QFileDialog.getSaveFileName( self, 'ovls.include', os.path.join(self.cfg.get("dir_ovls_out", "C://"), "ovls.include"), "Include file (*.include)", )[0] if filelist_src: try: self.ovl_data.save_included_ovls(filelist_src) self.update_progress("Operation completed!", value=1, vmax=1) except BaseException as ex: traceback.print_exc() interaction.showdialog(str(ex))
def species_dat_replacer(ovl, name_tups): logging.info(f"Replacing Species Dat contents for {name_tups}") if check_length(name_tups): return name_tups_new = [] name_tups_new2 = [] if ovl.user_version.is_jwe: suffixes = ("@", "_Var") suffixes2 = ("", "@", "_Var") for old, new in name_tups: extend_name_tuples(name_tups_new, new, old, suffixes) extend_name_tuples(name_tups_new2, new, old, suffixes2) else: suffixes = [] for gender in ("_Female", "_Male", "_Juvenile", ""): # various hardcoded suffixes for sym in ("@", "_Mat", "_Skin", "_Skin_NoDirt", "_Fur", "_Fur_Shell", "_Fur_Fin", "_Eyeball", "_Eyes", "_Eye", "_EyeMouthClaws", "_Whiskers", "_Hair", "_Feathers", "_Teeth", ""): suffixes.append(f"{gender}{sym}") # lods for i in range(7): suffixes.append(f"{gender}_l{i}") for old, new in name_tups: extend_name_tuples(name_tups_new, new, old, suffixes) try: # hash the internal buffers for archive_entry in ovl.archives: ovs = archive_entry.content for pool in ovs.pools: b = pool.data.getvalue() pool.data = io.BytesIO(replace_bytes(b, name_tups_new)) ovs.populate_pointers() for buffer_entry in ovs.buffer_entries: if ovl.user_version.is_jwe: b = buffer_entry.data buffer_entry.data = replace_bytes(b, name_tups_new2) else: b = buffer_entry.data buffer_entry.data = replace_bytes(b, name_tups_new) except Exception as err: showdialog(err) logging.info("Finished DAT replacing")
def load(self, ms2_file_path): logging.info(f"Injecting MS2") versions = get_versions(self.ovl) ms2_file = Ms2File() ms2_file.load(ms2_file_path, read_bytes=True) missing_materials = set() for model_info, mdl2_name, mdl2_entry in zip(ms2_file.model_infos, ms2_file.mdl_2_names, self.sized_str_entry.children): for material in model_info.model.materials: fgm_name = f"{material.name.lower()}.fgm" if ovl_versions.is_jwe(self.ovl) or ovl_versions.is_jwe2(self.ovl) and fgm_name == "airliftstraps.fgm": # don't cry about this continue if fgm_name not in self.ovl._ss_dict: missing_materials.add(fgm_name) if len(mdl2_entry.model_data_frags) != len(model_info.model.meshes): raise AttributeError( f"{mdl2_entry.name} ({len(model_info.model.meshes)}) doesn't have the " f"expected amount ({len(mdl2_entry.model_data_frags)}) of meshes!") if missing_materials: mats = '\n'.join(missing_materials) msg = f"The following materials are used by {self.file_entry.name}, but are missing from the OVL:\n" \ f"{mats}\n" \ f"This will crash unless you are importing the materials from another OVL. Inject anyway?" if not interaction.showdialog(msg, ask=True): logging.info("Injection was canceled by the user") return for mdl2_entry, model_info in zip(self.sized_str_entry.children, ms2_file.model_infos): logging.debug(f"Injecting {mdl2_entry.name} ") materials, lods, objects, meshes, model_info_ptr = mdl2_entry.fragments for frag, mdl2_list in ( (materials, model_info.model.materials,), (lods, model_info.model.lods), (objects, model_info.model.objects), (meshes, model_info.model.meshes)): if len(mdl2_list) > 0: data = as_bytes(mdl2_list, version_info=versions) # objects.pointers[1] has padding in stock, apparently as each entry is 4 bytes logging.debug(f"Injecting mdl2 data {len(data)} into {len(frag.pointers[1].data)} ({len(frag.pointers[1].padding)})") # frag.pointers[1].update_data(data, pad_to=8) # the above breaks injecting minmi frag.pointers[1].update_data(data) logging.debug(f"Result {len(frag.pointers[1].data)} ({len(frag.pointers[1].padding)})") # load ms2 ss data self.sized_str_entry.pointers[0].update_data(as_bytes(ms2_file.info, version_info=versions)) buffer_info_frag, model_info_frag, end_frag = self.sized_str_entry.fragments buffer_info_frag.pointers[1].update_data(as_bytes(ms2_file.buffer_info, version_info=versions), update_copies=True) model_info_frag.pointers[1].update_data(as_bytes(ms2_file.model_infos, version_info=versions)) # update ms2 data self.sized_str_entry.data_entry.update_data(ms2_file.buffers)
def dat_replacer(ovl, name_tups): logging.info(f"Replacing Dat contents for {name_tups}") if check_length(name_tups): return name_tups_new = [(name_bytes(o), name_bytes(n)) for o, n in name_tups] try: # hash the internal buffers for archive_entry in ovl.archives: ovs = archive_entry.content for pool in ovs.pools: b = pool.data.getvalue() pool.data = io.BytesIO(replace_bytes(b, name_tups_new)) ovs.populate_pointers() # for buffer_entry in ovs.buffer_entries: # b = buffer_entry.data # buffer_entry.data = replace_bytes(b, name_tups) except Exception as err: showdialog(err) logging.info("Done!")
def handle_path(self, save_over=True): # get path if self.in_folder.isChecked(): root_dir = self.get_selected_dir() if root_dir: # walk path ovls = walker.walk_type(root_dir, extension=".ovl") for ovl_path in ovls: # open ovl file self.file_widget.decide_open(ovl_path) # process each yield self.ovl_data if save_over: self.ovl_data.save(ovl_path, "") else: interaction.showdialog("Select a root directory!") # just the one that's currently open else: yield self.ovl_data
def _get_data(self, file_path): """Loads and returns the data for a LUA""" buffer_0 = self.get_content(file_path) if b"DECOMPILER ERROR" in buffer_0: confirmed = showdialog( f"{file_path} has not been successfully decompiled and may crash your game. Inject anyway?", ask=True) if not confirmed: raise UserWarning(f"Injection aborted for {file_path}") # 4 uint, 2 ptrs, 16 unused bytes ss = struct.pack("4I 2Q 2Q", len(buffer_0), 16000, 0, 0, 0, 0, 0, 0) return ss, buffer_0
def save_ovl(self): if self.is_open_ovl(): file_src = QtWidgets.QFileDialog.getSaveFileName( self, 'Save OVL', os.path.join(self.cfg.get("dir_ovls_out", "C://"), self.file_widget.filename), "OVL files (*.ovl)", )[0] if file_src: self.cfg["dir_ovls_out"], ovl_name = os.path.split(file_src) try: self.ovl_data.save(file_src, self.use_ext_dat, self.dat_widget.filepath) self.file_widget.dirty = False self.update_progress("Operation completed!", value=1, vmax=1) except BaseException as ex: traceback.print_exc() interaction.showdialog(str(ex))
def save_file_list(self): if self.is_open_ovl(): filelist_src = QtWidgets.QFileDialog.getSaveFileName( self, 'Save File List', os.path.join(self.cfg.get("dir_ovls_out", "C://"), self.file_widget.filename + ".files.txt"), "Txt file (*.txt)", )[0] if filelist_src: try: file_names = self.files_container.table.get_files() with open(filelist_src, 'w') as f: f.write("\n".join(file_names)) self.update_progress("Operation completed!", value=1, vmax=1) except BaseException as ex: traceback.print_exc() interaction.showdialog(str(ex))
def startDrag(self, actions): """Starts a drag from inside the app towards the outside""" drag = QtGui.QDrag(self) names = self.get_selected_files() print("DRAGGING", names) try: temp_dir = tempfile.gettempdir() out_paths, errors, skips = self.main_window.ovl_data.extract( temp_dir, only_names=names, show_temp_files=self.main_window.show_temp_files) data = QtCore.QMimeData() data.setUrls( [QtCore.QUrl.fromLocalFile(path) for path in out_paths]) drag.setMimeData(data) drag.exec_() except BaseException as ex: traceback.print_exc() showdialog(str(ex)) print(ex)
def drag_files(self, file_names): logging.info(f"DRAGGING {file_names}") drag = QtGui.QDrag(self) temp_dir = tempfile.mkdtemp("-cobra") try: out_paths, errors = self.ovl_data.extract( temp_dir, only_names=file_names, show_temp_files=self.show_temp_files) data = QtCore.QMimeData() data.setUrls( [QtCore.QUrl.fromLocalFile(path) for path in out_paths]) drag.setMimeData(data) drag.exec_() logging.info( f"Tried to extract {len(file_names)} files, got {len(errors)} errors" ) except BaseException as ex: traceback.print_exc() interaction.showdialog(str(ex)) logging.error(ex) shutil.rmtree(temp_dir)
def is_open_ovl(self): if not self.file_widget.filename: interaction.showdialog("You must open an OVL file first!") else: return True
def load(self): if self.file_widget.filepath: self.file_widget.dirty = False try: # runTask(self.ovl_data.load, (self.file_widget.filepath,), {"commands": self.commands,}) # test(2) # self.ovl_thread.func = self.ovl_thread.ovl_data.load # self.ovl_thread.args = (self.file_widget.filepath,) # self.ovl_thread.kwargs = {"commands": self.commands,} # self.ovl_thread.start() self.ovl_data.load(self.file_widget.filepath, commands=self.commands) # print(self.ovl_data.user_version) # print(self.ovl_data) # for a in self.ovl_data.archives: # print(a) # for a in self.ovl_data.archives[1:]: # print(a.name) # for ss in a.content.sized_str_entries: # print(ss.name) # print(self.ovl_data.mimes) # print(self.ovl_data.triplets) # for a, z in zip(self.ovl_data.archives, self.ovl_data.zlibs): # print(a, z) # print(f"zlib sum {z.zlib_thing_1 + z.zlib_thing_2 - 68}") # print(f"pool size {a.pools_end - a.pools_start}") # print(f"stream links size {12 * a.num_files}") # print(f"buffer size {sum([buff.size for buff in a.content.buffer_entries])}") # print(f"d1 size {sum([data.size_1 for data in a.content.data_entries])}") # print(f"d2 size {sum([data.size_2 for data in a.content.data_entries])}") # if a.name != "STATIC": # streams = self.ovl_data.stream_files[a.stream_files_offset: a.stream_files_offset+a.num_files] # print(a.name, streams) # print(self.ovl_data.stream_files) # for i, f in enumerate(self.ovl_data.files): # if f.ext == ".texturestream": # print(i, f.name) # offsets = list(sorted((f.file_offset, i) for i, f in enumerate(self.ovl_data.stream_files))) # # print(self.ovl_data) # print(offsets) # # for a in self.ovl_data.archives[1:]: # # print(a.content) # for sf in self.ovl_data.stream_files: # print(sf) # for a in self.ovl_data.archives: # if a.pools_start <= sf.stream_offset < a.pools_end: # print(f"is in {a.name}") # print(f"pool offset relative {sf.stream_offset - a.pools_start}") # # print(a.content.sized_str_entries) # for a in self.ovl_data.archives: # if a.name == "STATIC": # for i, pool in enumerate(a.content.pools): # if pool.offset <= sf.file_offset < pool.offset + pool.size: # print(f"static pool {i} offset relative {sf.file_offset - pool.offset}") # logging.debug(a.content) # print(self.ovl_data.user_version) except Exception as ex: traceback.print_exc() interaction.showdialog(str(ex)) print(self.ovl_data) self.update_gui_table() game = get_game(self.ovl_data)[0] self.game_choice.entry.setText(game.value) self.compression_choice.entry.setText( self.ovl_data.user_version.compression.name)