def create_folder_in_rom(rom: NintendoDSRom, path: str) -> None: """Creates a folder in the ROM.""" folder = rom.filenames.subfolder(path) if folder is not None: raise FileNotFoundError(f(_("Folder {path} already exists."))) path_list = path.split('/') par_dir_name = '/'.join(path_list[:-1]) parent_dir: Optional[Folder] = rom.filenames.subfolder(par_dir_name) if parent_dir is None: raise FileNotFoundError(f(_("Folder {dir_name} does not exist."))) found = False first_id = -1 last_child_count = -1 for s_name, s_folder in sorted(parent_dir.folders, key=lambda f: f[0]): first_id = s_folder.firstID last_child_count = len(s_folder.files) if s_name > path_list[-1]: found = True break if not found: first_id = first_id + last_child_count new_folder = Folder(firstID=first_id) parent_dir.folders.append((path_list[-1], new_folder))
def _parse_param(self, param: SsbOpParam, built_strings: Dict[str, List[str]], built_constants: List[str]) -> int: if isinstance(param, int): return param if isinstance(param, SsbOpParamConstant): try: return SsbConstant(param.name, self.rom_data.script_data).value.id except ValueError as err: raise SsbCompilerError(str(err)) from err if isinstance(param, SsbOpParamConstString): i = len(built_constants) built_constants.append(param.name) return i if isinstance(param, SsbOpParamLanguageString): i = len(built_strings[next(iter(built_strings.keys()))]) if len(param.strings.keys()) == 1: # Single language convenience mode, apply this to all languages. only_value = param.strings[next(iter(param.strings.keys()))] for lang in built_strings.keys(): built_strings[lang].append(only_value) else: # Multi language regular case. All languages must be known. for lang, string in param.strings.items(): if lang not in built_strings: raise SsbCompilerError(f(_("Unknown language for string: {lang}"))) built_strings[lang].append(string) return StringIndexPlaceholder(i) raise SsbCompilerError(f(_("Invalid parameter supplied for an operation: {param}")))
def set_cart_removed_data(img: Image.Image, arm9: bytearray, config: Pmd2Data) -> None: """ Sets the cartridge removed data """ if img.width != IMG_WIDTH and img.height != IMG_HEIGHT: raise AttributeError( f(_("The image must have dimensions {IMG_WIDTH}x{IMG_HEIGHT}.") )) block = config.binaries['arm9.bin'].symbols['CartRemovedImgData'] img = img.convert("RGB") raw_data = img.tobytes() img_data = [] for r, g, b in iter_bytes(raw_data, 3): v = (r // 8) + ((g // 8) << 5) + ((b // 8) << 10) img_data.append(v % 256) img_data.append(v // 256) data = CommonAtHandler.serialize( CommonAtHandler.compress(bytes(img_data), [CommonAtType.AT3PX])) if len(data) > block.end - block.begin: raise AttributeError( f( _("This image must be compressed better to fit in the arm9 ({len(data)} > {block.end-block.begin})." ))) arm9[block.begin:block.end] = data + bytes((block.end - block.begin) - len(data))
def on_spritebot_import_activate(self, *args): dialog = Gtk.FileChooserNative.new( _("Import portraits from PNG sheet..."), MainController.window(), Gtk.FileChooserAction.OPEN, None, None) add_dialog_png_filter(dialog) response = dialog.run() fn = dialog.get_filename() dialog.destroy() if response == Gtk.ResponseType.ACCEPT: try: for subindex, image in SpriteBotSheet.load( fn, self._get_portrait_name): try: self.kao.set_from_img(self.item_id, subindex, image) except Exception as err: name = self._get_portrait_name(subindex) logger.error(f"Failed importing image '{name}'.", exc_info=err) display_error( sys.exc_info(), f(_('Failed importing image "{name}":\n{err}')), f(_("Error for '{name}'."))) except Exception as err: logger.error(f"Failed importing portraits sheet: {err}", exc_info=err) display_error(sys.exc_info(), f(_('Failed importing portraits sheet:\n{err}')), _("Could not import.")) self.re_render() # Mark as modified self.module.mark_as_modified() self._mark_as_modified_cb()
def _init_tileset_chooser(self): store = Gtk.ListStore(int, str) # id, name for i in range(0, COUNT_VALID_TILESETS): if i >= TILESET_FIRST_BG: store.append([i, f(_("Background {i}"))]) else: store.append([i, f(_("Tileset {i}"))]) self._fast_set_comboxbox_store( self.builder.get_object('tool_choose_tileset_cb'), store, 1)
def from_xml(cls, ele: Element) -> 'MappaFloor': data = { 'layout': None, 'monsters': None, 'traps': None, 'floor_items': None, 'shop_items': None, 'monster_house_items': None, 'buried_items': None, 'unk_items1': None, 'unk_items2': None } for child in ele: if child.tag == XML_FLOOR_LAYOUT and data['layout'] is None: data['layout'] = MappaFloorLayout.from_xml(child) elif child.tag == XML_MONSTER_LIST and data['monsters'] is None: monsters = [] for monster in child: monsters.append(MappaMonster.from_xml(monster)) data['monsters'] = monsters elif child.tag == XML_TRAP_LIST and data['traps'] is None: data['traps'] = MappaTrapList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE ) == XML_ITEM_LIST__TYPE__FLOOR and data['floor_items'] is None: data['floor_items'] = MappaItemList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE ) == XML_ITEM_LIST__TYPE__SHOP and data['shop_items'] is None: data['shop_items'] = MappaItemList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE ) == XML_ITEM_LIST__TYPE__MONSTER_HOUSE and data[ 'monster_house_items'] is None: data['monster_house_items'] = MappaItemList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE ) == XML_ITEM_LIST__TYPE__BURIED and data['buried_items'] is None: data['buried_items'] = MappaItemList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE ) == XML_ITEM_LIST__TYPE__UNK1 and data['unk_items1'] is None: data['unk_items1'] = MappaItemList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE ) == XML_ITEM_LIST__TYPE__UNK2 and data['unk_items2'] is None: data['unk_items2'] = MappaItemList.from_xml(child) else: raise XmlValidateError( f(_('Floor parsing: Unexpected {child.tag}'))) for k, v in data.items(): if v is None: raise XmlValidateError(f(_('Missing {k} for Floor data.'))) return cls(**data)
def on_separate_import_activate(self, *args): md = SkyTempleMessageDialog( MainController.window(), Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, f( _("To import, select a directory to import from. Files with the pattern '{self.item_id + 1}_XX.png'\n" "will be imported, where XX is a number between 0 and 40.")), title=_("Import Portraits")) md.run() md.destroy() dialog = Gtk.FileChooserNative.new(_("Import portraits from PNGs..."), MainController.window(), Gtk.FileChooserAction.SELECT_FOLDER, None, None) response = dialog.run() fn = dialog.get_filename() dialog.destroy() if response == Gtk.ResponseType.ACCEPT: r = re.compile(rf"{self.item_id + 1}_(\d+)\.png", re.IGNORECASE) imgs = { int(match[1]): name for match, name in self._try_match_import(r, os.listdir(fn)) if match is not None and int(match[1]) <= 40 } for subindex, image_fn in imgs.items(): try: with open(os.path.join(fn, image_fn), 'rb') as f: image = Image.open(f) kao = self.kao.get(self.item_id, subindex) if kao: # Replace kao.set(image) else: # New self.kao.set(self.item_id, subindex, KaoImage.new(image)) except Exception as err: name = self._get_portrait_name(subindex) logger.error(f"Failed importing image '{name}'.", exc_info=err) display_error( sys.exc_info(), f(_('Failed importing image "{name}":\n{err}')), f(_(f"Error for '{name}'."))) # Re-render self._portrait_provider.reset() for draw in self._draws: draw.queue_draw() # Mark as modified self.module.mark_as_modified() self._mark_as_modified_cb()
def apply(self, name: str, config: Optional[Dict[str, Any]] = None) -> None: """ Apply a patch. If the patch requires parameters, values for ALL of them must be in the dict `config` (even if default values are specified in the XML config). """ if name not in self._loaded_patches: raise ValueError(f(_("The patch '{name}' was not found."))) patch = self._loaded_patches[name] if isinstance(patch, DependantPatch): for patch_name in patch.depends_on(): try: if not self.is_applied(patch_name): raise PatchDependencyError(f(_("The patch '{patch_name}' needs to be applied before you can " "apply '{name}'."))) except ValueError as err: raise PatchDependencyError(f(_("The patch '{patch_name}' needs to be applied before you can " "apply '{name}'. " "This patch could not be found."))) from err # Check config patch_data = self._config.asm_patches_constants.patches[name] if patch_data.has_parameters(): if config is None: raise PatchNotConfiguredError(_("No configuration was given."), "*", "No configuration was given.") for param in patch_data.parameters.values(): if param.name not in config: raise PatchNotConfiguredError(_("Missing configuration value."), param.name, "Not given.") if param.type == Pmd2PatchParameterType.INTEGER: val = config[param.name] if not isinstance(val, int): raise PatchNotConfiguredError(_("Invalid configuration value."), param.name, "Must be int.") if param.min is not None and val < param.min: raise PatchNotConfiguredError(_("Invalid configuration value."), param.name, _("Must be >= {}.").format(param.min)) if param.max is not None and val > param.max: raise PatchNotConfiguredError(_("Invalid configuration value."), param.name, _("Must be <= {}.").format(param.max)) if param.type == Pmd2PatchParameterType.STRING: val = config[param.name] if not isinstance(val, str): raise PatchNotConfiguredError(_("Invalid configuration value."), param.name, "Must be str.") if param.type == Pmd2PatchParameterType.SELECT: val = config[param.name] found = False for option in param.options: # type: ignore if not isinstance(val, type(option.value)) or option.value != val: continue found = True break if not found: raise PatchNotConfiguredError(_("Invalid configuration value."), param.name, "Must be one of the options.") patch.supply_parameters(config) patch.apply( partial(self._apply_armips, name, patch), self._rom, self._config )
def get_view(self) -> Gtk.Widget: self.builder = self._get_builder(__file__, 'tileset.glade') self._init_rules() self._init_rule_icon_views() self._init_chunk_picker_icon_view() self._init_secondary_terrain() self.builder.connect_signals(self) editor = self.builder.get_object('editor') root = self.builder.get_object('editor_root') root.set_current_page(self.__class__._last_open_tab_id) self.on_editor_root_switch_page(None, None, self.__class__._last_open_tab_id) self.builder.get_object('label_tileset_name').set_text(f(_('Dungeon Tileset {self.item_id} Rules'))) self.builder.get_object('label_tileset_name2').set_text(f(_('Dungeon Tileset {self.item_id}'))) return editor
def _save(self, force=False): rom = RomProject.get_current() if rom.has_modifications() or force: self._loading_dialog = self.builder.get_object('file_opening_dialog') # noinspection PyUnusedLocal rom_name = os.path.basename(rom.filename) self.builder.get_object('file_opening_dialog_label').set_label( f(_('Saving ROM "{rom_name}"...')) ) logger.debug(f(_('Saving {rom.filename}.'))) # This will trigger a signal. rom.save(self) self._loading_dialog.run()
def _open_file(self, filename: str): """Open a file""" if self._check_open_file(): self._loading_dialog = self.builder.get_object('file_opening_dialog') # noinspection PyUnusedLocal rom_name = os.path.basename(filename) self.builder.get_object('file_opening_dialog_label').set_label( f(_('Loading ROM "{rom_name}"...')) ) logger.debug(f(_('Opening {filename}.'))) RomProject.open(filename, self) # Add to the list of recent files and save self._update_recent_files(filename) # Show loading spinner self._loading_dialog.run()
def __init__(self, builder: Gtk.Builder, main_window: Gtk.Window, talk_script_names: Dict[int, str], scriptdata: Pmd2ScriptData, *, edit: SsaTrigger = None): self.builder = builder self.edit: Optional[SsaTrigger] = edit self.new_model: Optional[SsaTrigger] = None self.talk_script_names = talk_script_names self.scriptdata = scriptdata self.window: Gtk.Dialog = self.builder.get_object('dialog_event') self.window.set_transient_for(main_window) self.window.set_attached_to(main_window) if self.edit is not None: try: # noinspection PyUnusedLocal script_name = self.talk_script_names[self.edit.script_id] self.title = f( _('Edit {script_name}') ) + f' / {self.scriptdata.common_routine_info__by_id[self.edit.coroutine.id].name}' except KeyError: self.title = _('Edit Event') else: self.title = _('New Event')
def import_from_xml(self, xml: Element, tables: Dict[int, Image.Image]): self.entries = [] self.unknown = 0 pal_table = 256 validate_xml_tag(xml, XML_FONT) for child in xml: if child.tag == XML_TABLE: validate_xml_attribs(child, [XML_TABLE__ID]) t = int(child.get(XML_TABLE__ID)) if t in FONT_VALID_TABLES and t in tables: if pal_table > t: pal_table = t self.set_palette_raw( memoryview(tables[t].palette.palette)) for char in child: validate_xml_tag(char, XML_CHAR) validate_xml_attribs(char, [XML_CHAR__ID, XML_CHAR__WIDTH]) charid = int(char.get(XML_CHAR__ID)) width = int(char.get(XML_CHAR__WIDTH)) x = (charid % 16) * BANNER_FONT_SIZE y = (charid // 16) * BANNER_FONT_SIZE self.entries.append( BannerFontEntry.from_pil( tables[t].crop(box=[ x, y, x + BANNER_FONT_SIZE, y + BANNER_FONT_SIZE ]), charid, t, width)) elif child.tag == XML_HEADER: validate_xml_attribs(child, [XML_HEADER__UNKNOWN]) self.unknown = int(child.get(XML_HEADER__UNKNOWN)) else: raise XmlValidateError( f(_('Font parsing: Unexpected {child.tag}')))
def get_content(self) -> Gtk.Widget: # TODO: Adding and removing the sub scenes. return self.generate_content_label( f(_('This section contains all sub scenes for the map {self.name}.\n\n' 'These scenes can be loaded on top of the "Enter" scene,\n' 'depending on the current story progress.')) )
def _init_override_dropdown(self): store = Gtk.ListStore(int, str) # id, name store.append([0, _("No override")]) for i in range(1, 256): store.append([i, f(_("No. {i}"))]) # TRANSLATORS: Number {i} self._fast_set_comboxbox_store( self.builder.get_object('settings_override'), store, 1)
def get_view(self) -> Gtk.Widget: self.builder = self._get_builder(__file__, 'dungeon.glade') self.builder.get_object('label_dungeon_name').set_text( self.dungeon_name) edit_text = '' if not self.dungeon_info.length_can_be_edited: edit_text = _( '\nSince this is a Dojo Dungeon, the floor count can not be changed.' ) self.builder.get_object('edit_floor_count').set_sensitive(False) self.builder.get_object('dungeon_restrictions_grid').set_sensitive( False) else: self._init_dungeon_restrictions() floor_count = self.module.get_number_floors( self.dungeon_info.dungeon_id) self.builder.get_object('label_floor_count').set_text( f(_('This dungeon has {floor_count} floors.{edit_text}'))) self._init_names() self._is_loading = False self.builder.connect_signals(self) return self.builder.get_object('main_box')
def load_tree_items(self, item_store: TreeStore, root_node): self._validator = DungeonValidator(self.get_mappa().floor_lists) root = item_store.append(root_node, [ ICON_ROOT, DUNGEONS_NAME, self, MainController, 0, False, '', True ]) self._tree_model = item_store self._root_iter = root static_data = self.project.get_rom_module().get_static_data() self._fixed_floor_data = self.project.open_file_in_rom( FIXED_PATH, FileType.FIXED_BIN, static_data=static_data) self._dungeon_bin: DungeonBinPack = self.project.open_file_in_rom( DUNGEON_BIN, FileType.DUNGEON_BIN, static_data=static_data) self._validator.validate(self.get_dungeon_list()) self._fill_dungeon_tree() # Fixed rooms self._fixed_floor_root_iter = item_store.append( root_node, [ ICON_FIXED_ROOMS, FIXED_ROOMS_NAME, self, FixedRoomsController, 0, False, '', True ]) for i in range(0, len(self._fixed_floor_data.fixed_floors)): self._fixed_floor_iters.append( item_store.append(self._fixed_floor_root_iter, [ ICON_FIXED_ROOMS, f(_('Fixed Room {i}')), self, FixedController, i, False, '', True ])) recursive_generate_item_store_row_label(self._tree_model[root]) recursive_generate_item_store_row_label( self._tree_model[self._fixed_floor_root_iter])
def mappa_floor_xml_import(xml: Element, floor: MappaFloor): """Imports all data available in the mappa floor XML into the given model.""" for child in xml: if child.tag == XML_FLOOR_LAYOUT: floor_number_before = floor.layout.floor_number floor.layout = MappaFloorLayout.from_xml(child) floor.layout.floor_number = floor_number_before elif child.tag == XML_MONSTER_LIST: monsters = [] for monster in child: monsters.append(MappaMonster.from_xml(monster)) floor.monsters = monsters elif child.tag == XML_TRAP_LIST: floor.traps = MappaTrapList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE) == XML_ITEM_LIST__TYPE__FLOOR: floor.floor_items = MappaItemList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE) == XML_ITEM_LIST__TYPE__SHOP: floor.shop_items = MappaItemList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE) == XML_ITEM_LIST__TYPE__MONSTER_HOUSE: floor.monster_house_items = MappaItemList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE) == XML_ITEM_LIST__TYPE__BURIED: floor.buried_items = MappaItemList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE) == XML_ITEM_LIST__TYPE__UNK1: floor.unk_items1 = MappaItemList.from_xml(child) elif child.tag == XML_ITEM_LIST and child.get( XML_ITEM_LIST__TYPE) == XML_ITEM_LIST__TYPE__UNK2: floor.unk_items2 = MappaItemList.from_xml(child) else: raise XmlValidateError( f(_('Floor parsing: Unexpected {child.tag}')))
def get_content(self) -> Gtk.Widget: # TODO: Adding and removing the acting scenes. return self.generate_content_label( f( _('This section contains all acting scenes for the map {self.name}.\n\n' 'These scenes are used for cutscenes.\n' 'The player can usually not move the character in them.')))
def refresh(self, patch_category: PatchCategory): # ATTACH page = self._category_tabs[patch_category] page.pack_start(self.builder.get_object('patch_window'), True, True, 0) self._current_tab = patch_category tree: Gtk.TreeView = self.builder.get_object('patch_tree') model: Gtk.ListStore = tree.get_model() model.clear() self._patcher = self.module.project.create_patcher() # Load zip patches for fname in glob(os.path.join(self.patch_dir(), '*.skypatch')): try: self._patcher.add_pkg(fname) except BaseException as err: logger.error( f"Error loading patch package {os.path.basename(fname)}", exc_info=sys.exc_info()) self._error( f( _("Error loading patch package {os.path.basename(fname)}:\n{err}" ))) # List patches: for patch in sorted(self._patcher.list(), key=lambda p: p.name): if patch.category != patch_category: continue applied_str = _('Not compatible') try: applied_str = _('Applied') if self._patcher.is_applied( patch.name) else _('Compatible') except NotImplementedError: pass model.append( [patch.name, patch.author, patch.description, applied_str])
def validate_xml_attribs(ele: Element, attribs: List[str]): for attrib in attribs: if attrib not in ele.attrib: raise XmlValidateError( f( _("Invalid XML. Expected attribute {attrib} for XML tag {ele.tag}." )))
def pil_to_chunks( self, image: Image.Image, force_import=True) -> Tuple[List[bytes], List[List[int]]]: """ Imports chunks. Format same as for chunks_to_pil. Replaces tile mappings and returns the new tiles for storing them in a DPCI and the palettes for storing in a DPL. The PIL must have a palette containing the 16 sub-palettes with 16 colors each (256 colors). If a pixel in a tile uses a color outside of it's 16 color range, an error is thrown or the color is replaced with 0 of the palette (transparent). This is controlled by the force_import flag. """ tiles, all_tilemaps, palettes = from_pil(image, DPL_PAL_LEN, 16, DPCI_TILE_DIM, image.width, image.height, DPC_TILING_DIM, DPC_TILING_DIM, force_import) # Validate number of palettes palettes = palettes[:DPL_MAX_PAL] for tm in all_tilemaps: if tm.pal_idx > DPL_MAX_PAL - 1: raise ValueError( f( _("The image to import can only use the first 12 palettes. " "Tried to use palette {tm.pal_idx}"))) self.chunks = list( chunks(all_tilemaps, DPC_TILING_DIM * DPC_TILING_DIM)) self.re_fill_chunks() return tiles, palettes
def provide(self, add_title=None, dark=False, disable_xml_declaration=False) -> Graph: chart = pygal.XY( xrange=(1, len(self.level_bin_entry.levels) + 1), secondary_range=(0, max([x.experience_required for x in self.level_bin_entry.levels])), disable_xml_declaration=disable_xml_declaration ) if add_title: chart.title = add_title if dark: chart.style = DarkSolarizedStyle exps = [] hps = [] atks = [] sp_atks = [] defs = [] sp_defs = [] hp_accu = self.monster.base_hp atk_accu = self.monster.base_atk sp_atk_accu = self.monster.base_sp_atk def_accu = self.monster.base_def sp_def_accu = self.monster.base_sp_def for i, level in enumerate(self.level_bin_entry.levels): exps.append((i + 1, level.experience_required)) hp_accu += level.hp_growth # type: ignore hps.append((i + 1, hp_accu)) atk_accu += level.attack_growth # type: ignore atks.append((i + 1, atk_accu)) sp_atk_accu += level.special_attack_growth # type: ignore sp_atks.append((i + 1, sp_atk_accu)) def_accu += level.defense_growth # type: ignore defs.append((i + 1, def_accu)) sp_def_accu += level.special_defense_growth # type: ignore sp_defs.append((i + 1, sp_def_accu)) max_val: int = max(hp_accu, atk_accu, sp_atk_accu, def_accu, sp_def_accu) # type: ignore moves = [] processed_levels: Dict[int, int] = {} for lum in self.move_learnset.level_up_moves: if lum.level_id in processed_levels: processed_levels[lum.level_id] += 1 else: processed_levels[lum.level_id] = 1 count_so_far = processed_levels[lum.level_id] - 1 moves.append({ 'value': (lum.level_id, max_val + 5 + (5 * count_so_far)), 'label': self.move_strings[lum.move_id] }) chart.add(_('Exp.'), exps, secondary=True) # TRANSLATORS: Experience chart.add(_('HP'), hps) # TRANSLATORS: Health Points chart.add(_('ATK'), atks) # TRANSLATORS: Attack chart.add(_('Sp. ATK'), sp_atks) # TRANSLATORS: Special Attack chart.add(_('DEF'), defs) # TRANSLATORS: Defense chart.add(_('Sp. DEF'), sp_defs) # TRANSLATORS: Special Defense chart.add(_('Moves'), moves, stroke=False, formatter=lambda x: f(_('at level {x[0]}'))) return chart
def get_content(self) -> Gtk.Widget: if self.name is not None: return self.generate_content_label( f(_("This section contains all the map backgrounds, that start with the letter {self.name[0]}.")) ) return self.generate_content_label( _("This section contains all the map backgrounds, that don't fit in any of the other categories.") )
def __init__(self, idx: int, flip_x: bool, flip_y: bool, pal_idx: int, ignore_too_large: bool = False): self.idx = idx if idx > 0x3FF and not ignore_too_large: raise ValueError(f(_("Tile Mapping can not be processed. The tile number referenced ({idx}) is bigger " "than the maximum ({0x3FF}). If you are importing an image, please try to have " "less unique tiles."))) self.flip_x = flip_x self.flip_y = flip_y self.pal_idx = pal_idx
def _done(self, return_code): self._update_status(GfxcrunchStatus.SUCCESS if return_code == 0 else GfxcrunchStatus.ERROR) if return_code != 0: self._stderr( f( _('!! Process exited with error. Exit code: {return_code} !!' ))) self.builder.get_object('spinner').stop() self.builder.get_object('close').set_sensitive(True)
def add_manually(self, handler: AbstractPatchHandler, patch_base_dir: str): # Try to find the patch in the config if handler.name not in self._config.asm_patches_constants.patches.keys( ): raise ValueError( f( _("No patch for handler '{handler.name}' found in the configuration." ))) self._loaded_patches[handler.name] = handler self._patch_dirs[handler.name] = os.path.realpath(patch_base_dir)
def apply(self, name: str): if name not in self._loaded_patches: raise ValueError(f(_("The patch '{name}' was not found."))) patch = self._loaded_patches[name] if isinstance(patch, DependantPatch): for patch_name in patch.depends_on(): try: if not self.is_applied(patch_name): raise PatchDependencyError( f( _("The patch '{patch_name}' needs to be applied before you can " "apply '{name}'."))) except ValueError as err: raise PatchDependencyError( f( _("The patch '{patch_name}' needs to be applied before you can " "apply '{name}'. " "This patch could not be found."))) from err patch.apply(partial(self._apply_armips, name), self._rom, self._config)
def create_for(cls, string): if string == 'CLOSED': return cls.CLOSED if string == 'OPEN': return cls.OPEN if string == 'REQUEST': return cls.REQUEST if string == 'OPEN_AND_REQUEST': return cls.OPEN_AND_REQUEST raise UserValueError(f(_("Invalid DungeonMode: {string}")))
def get_view(self) -> Gtk.Widget: self.builder = self._get_builder(__file__, 'map.glade') self.builder.connect_signals(self) self.builder.get_object('title').set_text( f(_('Script Scenes for "{}"').format(self.name))) self.builder.get_object('desc').set_text( f( _('This section contains all scenes for the map {self.name}.\n\n' '"Enter (sse)" contains the scene that is loaded when the map is entered\n' 'by the player by "walking into it" (if applicable).\n\n' '"Acting (ssa)" contains the scenes used for cutscenes.\n' 'The player can usually not move the character in these scenes.\n\n' '"Sub (sss)" contains scenes that can be loaded on on top of the "Enter" scene,\n' 'depending on the current story progress.'))) self._sub_enter, self._sub_acting, self._sub_sub = self.module.get_subnodes( self.name) if self._sub_enter: self.builder.get_object('btn_add_enter').set_sensitive(False) return self.builder.get_object('box_list')