def export_sprite(self, wan: bytes, dir_fn: str): with tempfile.TemporaryDirectory() as tmp_path: tmp_path = os.path.join(tmp_path, 'tmp.wan') with open(tmp_path, 'wb') as f: f.write(wan) AsyncTaskRunner.instance().run_task(self._run_gfxcrunch([tmp_path, dir_fn])) self._run_window() if self.status != GfxcrunchStatus.SUCCESS: raise RuntimeError(_("The gfxcrunch process failed."))
def on_export_clicked(self, w: Gtk.MenuToolButton): dialog = Gtk.FileChooserNative.new(_("Export font in folder..."), MainController.window(), Gtk.FileChooserAction.SELECT_FOLDER, '_Save', None) response = dialog.run() fn = dialog.get_filename() dialog.destroy() if response == Gtk.ResponseType.ACCEPT: xml, tables = self.font.export_to_xml() with open(os.path.join(fn, f'char_tables.xml'), 'w') as f: f.write(prettify(xml)) for i, table in tables.items(): table.save(os.path.join(fn, f'table-{i}.png'))
def render_graph(self): self._render_graph = False if self._level_bin_entry is None: return stack: Gtk.Stack = self.builder.get_object('graph_stack') if self.item_id < len(self._waza_p.learnsets): learnset = self._waza_p.learnsets[self.item_id] else: learnset = MoveLearnset([], [], []) graph_provider = LevelUpGraphProvider( self.module.get_entry(self.item_id), self._level_bin_entry, learnset, self._string_provider.get_all(StringType.MOVE_NAMES)) svg = graph_provider.provide(dark=is_dark_theme( MainController.window()), disable_xml_declaration=True).render() with open_utf8(self.get_tmp_html_path(), 'w') as f: f.write( render_graph_template( f'{self._string_provider.get_value(StringType.POKEMON_NAMES, self.item_id)} {_("Stats Graph")} (SkyTemple)', svg)) if not self._support_webview: graph_fallbck_box: Gtk.Box = self.builder.get_object( 'graph_fallbck_box') first = True for child in graph_fallbck_box: if first: first = False continue graph_fallbck_box.remove(child) stack.set_visible_child(graph_fallbck_box) stream = Gio.MemoryInputStream.new_from_bytes( GLib.Bytes.new( cairosvg.svg2png(bytestring=bytes(svg, 'utf-8'), dpi=72))) pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, None) img = Gtk.Image.new_from_pixbuf(pixbuf) img.show() graph_fallbck_box.pack_start(img, True, True, 0) else: stack.set_visible_child( self.builder.get_object('graph_webkit_box')) self._webview.reload() # type: ignore
def export_a_sprite__wan(self, sprite: bytes): dialog = Gtk.FileChooserNative.new(_("Export WAN sprite..."), MainController.window(), Gtk.FileChooserAction.SAVE, None, None) filter = Gtk.FileFilter() filter.set_name(_("WAN sprite (*.wan)")) filter.add_pattern("*.wan") dialog.add_filter(filter) response = dialog.run() fn = dialog.get_filename() dialog.destroy() if response == Gtk.ResponseType.ACCEPT: fn = add_extension_if_missing(fn, 'wan') with open(fn, 'wb') as f: f.write(sprite)
def on_btn_export_clicked(self, *args): dialog = Gtk.FileChooserNative.new( _("Export dungeon tileset..."), MainController.window(), Gtk.FileChooserAction.SELECT_FOLDER, None, None ) response = dialog.run() fn = dialog.get_filename() dialog.destroy() assert self.dtef is not None if response == Gtk.ResponseType.ACCEPT: try: # Write XML with open(os.path.join(fn, 'tileset.dtef.xml'), 'w') as f: f.write(prettify(self.dtef.get_xml())) # Write Tiles var0, var1, var2, rest = self.dtef.get_tiles() var0fn, var1fn, var2fn, restfn = self.dtef.get_filenames() var0.save(os.path.join(fn, var0fn)) var1.save(os.path.join(fn, var1fn)) var2.save(os.path.join(fn, var2fn)) rest.save(os.path.join(fn, restfn)) shutil.copy(get_template_file(), os.path.join(fn, 'template.png')) md = SkyTempleMessageDialog(MainController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("The tileset was successfully exported."), title=_("Success!"), is_success=True) md.run() md.destroy() except BaseException as e: display_error( sys.exc_info(), str(e), _("Error exporting the tileset.") )
def on_btn_export_clicked(self, *args): dialog: Gtk.Dialog = self.builder.get_object('export_dialog') dialog.resize(640, 560) dialog.set_attached_to(SkyTempleMainController.window()) dialog.set_transient_for(SkyTempleMainController.window()) # Fill Pokémon tree store: Gtk.TreeStore = self.builder.get_object('export_dialog_store') store.clear() monster_entries_by_base_id: Dict[int, List[MdEntry]] = {} for entry in self.module.monster_md.entries: if entry.md_index_base not in monster_entries_by_base_id: monster_entries_by_base_id[entry.md_index_base] = [] monster_entries_by_base_id[entry.md_index_base].append(entry) for baseid, entry_list in monster_entries_by_base_id.items(): name = self.module.project.get_string_provider().get_value(StringType.POKEMON_NAMES, baseid) entry_main_tree = self.module.generate_entry__entity_root(baseid, name) ent_root = store.append(None, [ -1, -1, False, entry_main_tree[0], entry_main_tree[1], False, False ]) for entry in entry_list: entry_main_tree = self.module.generate_entry__entry(entry.md_index, entry.gender) store.append( ent_root, [ entry_main_tree[4], -1, True, entry_main_tree[0], entry_main_tree[1], False, False ] ) names, md_gender1, md_gender2, moveset, moveset2, stats, portraits, portraits2 = self.module.get_export_data(self.entry) we_are_gender1 = md_gender1 == self.entry if md_gender2 is None: sw: Gtk.Switch = self.builder.get_object('export_type_other_gender') sw.set_active(False) sw.set_sensitive(False) if portraits is None: sw: Gtk.Switch = self.builder.get_object('export_type_portraits_current_gender') sw.set_active(False) sw.set_sensitive(False) if portraits2 is None: sw: Gtk.Switch = self.builder.get_object('export_type_portraits_other_gender') sw.set_active(False) sw.set_sensitive(False) if stats is None: sw: Gtk.Switch = self.builder.get_object('export_type_stats') sw.set_active(False) sw.set_sensitive(False) if moveset is None: sw: Gtk.Switch = self.builder.get_object('export_type_moveset1') sw.set_active(False) sw.set_sensitive(False) if moveset2 is None: sw: Gtk.Switch = self.builder.get_object('export_type_moveset2') sw.set_active(False) sw.set_sensitive(False) resp = dialog.run() dialog.hide() if resp == Gtk.ResponseType.APPLY: # Create output XML if not self.builder.get_object('export_type_current_gender').get_active(): if we_are_gender1: md_gender1 = None else: md_gender2 = None if not self.builder.get_object('export_type_other_gender').get_active(): if not we_are_gender1: md_gender1 = None else: md_gender2 = None if not self.builder.get_object('export_type_names').get_active(): names = None if not self.builder.get_object('export_type_stats').get_active(): stats = None if not self.builder.get_object('export_type_moveset1').get_active(): moveset = None if not self.builder.get_object('export_type_moveset2').get_active(): moveset2 = None if not self.builder.get_object('export_type_portraits_current_gender').get_active(): if we_are_gender1: portraits = None else: portraits2 = None if not self.builder.get_object('export_type_portraits_other_gender').get_active(): if not we_are_gender1: portraits = None else: portraits2 = None xml = monster_xml_export( self.module.project.get_rom_module().get_static_data().game_version, md_gender1, md_gender2, names, moveset, moveset2, stats, portraits, portraits2 ) # 1. Export to file if self.builder.get_object('export_file_switch').get_active(): save_diag = Gtk.FileChooserNative.new( _("Export Pokémon as..."), SkyTempleMainController.window(), Gtk.FileChooserAction.SAVE, None, None ) add_dialog_xml_filter(save_diag) response = save_diag.run() fn = save_diag.get_filename() if '.' not in fn: fn += '.xml' save_diag.destroy() if response == Gtk.ResponseType.ACCEPT: with open(fn, 'w') as f: f.write(prettify(xml)) else: md = SkyTempleMessageDialog(SkyTempleMainController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.WARNING, Gtk.ButtonsType.OK, "Export was canceled.") md.set_position(Gtk.WindowPosition.CENTER) md.run() md.destroy() return # 2. Import to selected Pokémon selected_monsters: List[int] = [] def collect_monsters_recurse(titer: Optional[Gtk.TreeIter]): for i in range(store.iter_n_children(titer)): child = store.iter_nth_child(titer, i) if store[child][2] and store[child][5]: # is floor and is selected selected_monsters.append(store[child][0]) collect_monsters_recurse(child) collect_monsters_recurse(None) self.module.import_from_xml(selected_monsters, xml)
def apply(self, patch: Union[Pmd2Patch, Pmd2SimplePatch], binaries: Dict[str, Pmd2Binary], patch_file_dir: str, stub_path: str, game_id: str): try: with tempfile.TemporaryDirectory() as tmp: shutil.copytree(patch_file_dir, tmp, dirs_exist_ok=True) # Build ASM file to run asm_entrypoint = '' # First read in stub with open(os.path.join(tmp, stub_path)) as f: asm_entrypoint += f.read() + '\n' if isinstance(patch, Pmd2SimplePatch): for replace in patch.string_replacements: fn = os.path.join(tmp, replace.filename) game = None for game_candidate in replace.games: if game_candidate.game_id == game_id: game = game_candidate if game is not None: with open(os.path.join(tmp, fn), 'r') as f: new_content = replace.regexp.sub(game.replace, f.read()) with open(os.path.join(tmp, fn), 'w') as f: f.write(new_content) # If it's a simple patch just output and re-import all binaries. for binary_name, binary in binaries.items(): binary_path = os.path.join(tmp, binary_name.split('/')[-1]) # Write binary to tmp dir with open(binary_path, 'wb') as f: try: f.write(get_binary_from_rom_ppmdu(self.rom, binary)) except ValueError as err: if binary_name.split('/')[-1] == 'overlay_0036.bin': continue # We ignore if End's extra overlay is missing. raise err # For simple patches we also output the overlay table as y9.bin: binary_path = os.path.join(tmp, Y9_BIN) # Write binary to tmp dir with open(binary_path, 'wb') as f: f.write(self.rom.arm9OverlayTable) # Then include other includes for include in patch.includes: asm_entrypoint += f'.include "{os.path.join(tmp, include.filename)}"\n' # Build binary blocks if isinstance(patch, Pmd2Patch): for open_bin in patch.open_bins: binary = binaries[open_bin.filepath] binary_path = os.path.join(tmp, open_bin.filepath.split('/')[-1]) os.makedirs(os.path.dirname(binary_path), exist_ok=True) # Write binary to tmp dir with open(binary_path, 'wb') as f: f.write(get_binary_from_rom_ppmdu(self.rom, binary)) asm_entrypoint += f'.open "{binary_path}", 0x{binary.loadaddress:0x}\n' for include in open_bin.includes: asm_entrypoint += f'.include "{os.path.join(tmp, include.filename)}"\n' asm_entrypoint += '.close\n' # Write final asm file with open_utf8(os.path.join(tmp, ASM_ENTRYPOINT_FN), 'w') as f: f.write(asm_entrypoint) # Run armips original_cwd = os.getcwd() os.chdir(tmp) try: prefix = "" # Under Windows, try to load from SkyTemple _resources dir first. if sys.platform.startswith('win') and os.path.exists(os.path.join(get_resources_dir(), 'armips.exe')): prefix = os.path.join(get_resources_dir(), '') result = subprocess.Popen([f'{prefix}armips', ASM_ENTRYPOINT_FN], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) retcode = result.wait() except FileNotFoundError as ex: raise ArmipsNotInstalledError(_("ARMIPS could not be found. Make sure, that " "'armips' is inside your system's PATH.")) from ex finally: # Restore cwd os.chdir(original_cwd) if retcode != 0: raise PatchError(_("ARMIPS reported an error while applying the patch."), str(result.stdout.read(), 'utf-8'), str(result.stderr.read(), 'utf-8') if result.stderr else '') # Load the binaries back into the ROM opened_binaries = {} if isinstance(patch, Pmd2SimplePatch): # Read in all binaries again opened_binaries = binaries # Also read in arm9OverlayTable binary_path = os.path.join(tmp, Y9_BIN) with open(binary_path, 'rb') as f: self.rom.arm9OverlayTable = f.read() else: # Read opened binaries again for open_bin in patch.open_bins: opened_binaries[open_bin.filepath] = binaries[open_bin.filepath] for binary_name, binary in opened_binaries.items(): binary_path = os.path.join(tmp, binary_name.split('/')[-1]) with open(binary_path, 'rb') as f: try: set_binary_in_rom_ppmdu(self.rom, binary, f.read()) except ValueError as err: if binary_name.split('/')[-1] == 'overlay_0036.bin': continue # We ignore if End's extra overlay is missing. raise err except (PatchError, ArmipsNotInstalledError): raise except BaseException as ex: raise RuntimeError(f(_("Error while applying the patch: {ex}"))) from ex