def pil_to_tiles_separate(self, images: List[Image.Image]) -> None: frames = [] first_image_dims = None for image in images: frames.append( from_pil(image, BPL_IMG_PAL_LEN, BPL_MAX_PAL, BPA_TILE_DIM, image.width, image.height, optimize=False)[0]) if first_image_dims is None: first_image_dims = (image.width, image.height) if (image.width, image.height) != first_image_dims: raise ValueError( _("The dimensions of all images must be the same.")) self.tiles = [] self.number_of_frames = u16_checked(len(frames)) self.number_of_tiles = u16_checked( int((images[0].height * images[0].width) / (BPA_TILE_DIM * BPA_TILE_DIM))) for tile in frames: self.tiles += tile self._correct_frame_info()
def pil_to_chunks(self, layer: int, image: Image.Image, force_import: bool = True) -> List[List[int]]: """ Imports chunks. Format same as for chunks_to_pil. Replaces tiles, tile mappings and therefor also chunks. "Unsets" BPA assignments! BPAs have to be manually re-assigned by using set_tile or set_chunk. BPA indices are stored after BPC tile indices. 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. Returns the palettes stored in the image for further processing (eg. replacing the BPL palettes). """ self.layers[layer].tiles, self.layers[ layer].tilemap, palettes = from_pil(image, BPL_IMG_PAL_LEN, BPL_MAX_PAL, BPC_TILE_DIM, image.width, image.height, 3, 3, force_import) self.layers[layer].number_tiles = u16_checked( len(self.layers[layer].tiles) - 1) self.layers[layer].chunk_tilemap_len = u16_checked( int( len(self.layers[layer].tilemap) / self.tiling_width / self.tiling_height)) return palettes # type: ignore
def on_wtu_height_edited(self, widget, path, text): try: u16_checked(int(text)) except ValueError: return wtu_store: Gtk.ListStore = self.builder.get_object('wtu_store') wtu_store[path][3] = text self._regenerate_wtu()
def _actions_to_bytes(self) -> bytes: actions: List[Tuple[FixedFloorActionRule, int]] = shrink_list(self.actions) buffer = bytearray(4 * len(actions)) for i, (action, n_times) in enumerate(actions): write_u16(buffer, u16_checked(int(action)), i * 4) write_u16(buffer, u16_checked(n_times - 1), i * 4 + 0x02) return buffer
def from_xml(cls, ele: Element) -> 'MappaMonster': validate_xml_tag(ele, XML_MONSTER) validate_xml_attribs(ele, [ XML_MONSTER__LEVEL, XML_MONSTER__WEIGHT, XML_MONSTER__WEIGHT2, XML_MONSTER__MD_INDEX ]) return cls( u8_checked(int(ele.get(XML_MONSTER__LEVEL))), u16_checked(int(ele.get(XML_MONSTER__WEIGHT))), u16_checked(int(ele.get(XML_MONSTER__WEIGHT2))), u16_checked(int(ele.get(XML_MONSTER__MD_INDEX))), )
def compress(cls, data: bytes) -> 'At4px': """Create a new AT4PX container from originally uncompressed data.""" from skytemple_files.common.types.file_types import FileType new_container = cls() flags, px_data = FileType.PX.compress(data) new_container.compression_flags = flags new_container.length_decompressed = u16_checked(len(data)) new_container.compressed_data = px_data new_container.length_compressed = u16_checked(len(px_data) + 0x12) return new_container
def on_cb_excl_parameter_changed(self, w, *args): try: val = u16_checked(int(w.get_text())) except ValueError: return assert self.item_sp is not None self.item_sp.parameter = val
def on_entry_move_id_changed(self, w, *args): try: val = u16_checked(int(w.get_text())) except ValueError: return self.item_p.move_id = val self.mark_as_modified()
def on_entry_base_power_changed(self, w, *args): try: val = u16_checked(int(w.get_text())) except ValueError: return self.move.base_power = val self.mark_as_modified()
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if not self.is_applied(rom, config): if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: bar_list = BAR_LIST_US if config.game_region == GAME_REGION_EU: bar_list = BAR_LIST_EU if config.game_region == GAME_REGION_JP: bar_list = BAR_LIST_JP data = rom.loadArm9Overlays([19])[19].data header = bytearray([0xFF] * (4 + 2 * NB_ITEMS)) write_u32(header, u32(4 + 2 * NB_ITEMS), 0) list_data: List[bytes] = [] for x in range(bar_list, bar_list + BAR_LIST_SIZE, BAR_LIST_ENTRY_SIZE): item_id = read_u16(data, x) cdata = bytes(data[x + 2:x + BAR_LIST_ENTRY_SIZE]) if cdata in list_data: index = list_data.index(cdata) else: index = len(list_data) list_data.append(cdata) write_u16(header, u16_checked(index), 4 + 2 * item_id) file_data = header + b''.join(list_data) if ITEM_LIST_PATH not in rom.filenames: create_file_in_rom(rom, ITEM_LIST_PATH, file_data) else: rom.setFileByName(ITEM_LIST_PATH, file_data) try: apply() except RuntimeError as ex: raise ex
def on_cr_entity_edited(self, widget, path, text): match = PATTERN_MD_ENTRY.match(text) if match is None: return try: entid = u16_checked(int(match.group(1))) except ValueError: return idx = int(self._list_store[path][0]) # Check, if this is a special entry, # in this case we update the standin entry! # TODO: it's a bit weird doing this over the color if self._list_store[path][8] == ORANGE: logger.debug(f"Updated standin for actor {idx}: {entid}") standins = self._sprite_provider.get_standin_entities() standins[idx] = entid self._sprite_provider.set_standin_entities(standins) # ent_name: try: self._list_store[path][7] = self._ent_names[entid] except KeyError as e: raise UserValueError(_("No Pokémon with this ID found.")) # entid: self._list_store[path][4] = entid # ent_icon: # If color is orange it's special. # TODO: it's a bit weird doing this over the color self._list_store[path][3] = self._get_icon( entid, idx, self._list_store[path][8] == ORANGE)
def set_palettes(self, palettes: List[List[int]]) -> None: """Sets the palette properly, adding dummy grayscale entries if needed. """ self.palettes = palettes self.number_palettes = u16_checked(len(palettes)) while len(self.palettes) < BPL_MAX_PAL: self.palettes.append([(i // 3) * BPL_MAX_PAL for i in range(BPL_MAX_PAL * 3)])
def on_entry_belly_lost_wtw_1000_changed(self, widget, *args): try: val = u16_checked(int(widget.get_text())) except ValueError: return static_data = self.module.project.get_rom_module().get_static_data() self.module.project.modify_binary(BinaryName.OVERLAY_29, lambda bin: HardcodedDungeonMisc.set_belly_loss_1000ile_walk_through_walls(val, bin, static_data)) self.module.mark_misc_settings_as_modified()
def on_intimidator_activation_chance_changed(self, widget, *args): try: val = u16_checked(int(widget.get_text())) except ValueError: return static_data = self.module.project.get_rom_module().get_static_data() self.module.project.modify_binary(BinaryName.OVERLAY_10, lambda bin: HardcodedIq.set_intimidator_chance(val, bin, static_data)) self.module.mark_misc_settings_as_modified()
def on_id_description_edited(self, widget, path, text): try: tree_store: Gtk.ListStore = self.builder.get_object('tree_store') tree_store[path][2] = u16_checked(int(text)) except ValueError: return self._regenerate_list()
def on_entry_stolen_spawn_delay_changed(self, widget, *args): try: val = u16_checked(int(widget.get_text())) except ValueError: return static_data = self.module.project.get_rom_module().get_static_data() self.module.project.modify_binary(BinaryName.OVERLAY_10, lambda bin: HardcodedSpawnRate.set_stolen_spawn_rate(val, bin, static_data)) self.module.mark_misc_settings_as_modified()
def on_entry_min_iq_item_master_changed(self, widget, *args): try: val = u16_checked(int(widget.get_text())) except ValueError: return static_data = self.module.project.get_rom_module().get_static_data() self.module.project.modify_binary(BinaryName.ARM9, lambda bin: HardcodedIq.set_min_iq_for_item_master(val, bin, static_data)) self.module.mark_iq_as_modified()
def on_entry_ginseng_3_chance_changed(self, widget, *args): try: val = u16_checked(int(widget.get_text())) except ValueError: return static_data = self.module.project.get_rom_module().get_static_data() self.module.project.modify_binary(BinaryName.OVERLAY_10, lambda bin: HardcodedDungeonMisc.set_ginseng_increase_by_3_chance(val, bin, static_data)) self.module.mark_misc_settings_as_modified()
def on_entry_life_seed_changed(self, widget, *args): try: val = u16_checked(int(widget.get_text())) except ValueError: return static_data = self.module.project.get_rom_module().get_static_data() self.module.project.modify_binary(BinaryName.OVERLAY_10, lambda bin: HardcodedHpItems.set_life_seed_hp(val, bin, static_data)) self.module.mark_misc_settings_as_modified()
def compress(cls, data: bytes) -> 'BmaCollisionRleCompressionContainer': from skytemple_files.common.types.file_types import FileType new_container = cls() compressed_data = FileType.BMA_COLLISION_RLE.compress(data) new_container.length_decompressed = u16_checked(len(data)) new_container.compressed_data = compressed_data return new_container
def on_cr_stats_unka_edited(self, widget, path, text): store: Gtk.Store = self.builder.get_object('model_stats') try: v = u16_checked(int(text)) except ValueError: return store[path][8] = text self.lst_stats[int(store[path][0])].unkA = v self._save()
def on_cr_items_item_quantity_edited(self, widget, path, text): store: Gtk.Store = self.builder.get_object('model_items') try: quantity = u16_checked(int(text)) except ValueError: return self.lst_item[int(store[path][0])].quantity = quantity store[path][3] = quantity self._save()
def compress(cls, data: bytes) -> 'GenericNrlCompressionContainer': from skytemple_files.common.types.file_types import FileType new_container = cls() compressed_data = FileType.GENERIC_NRL.compress(data) new_container.length_decompressed = u16_checked(len(data)) new_container.compressed_data = compressed_data return new_container
def compress(cls, data: bytes) -> 'BpcTilemapCompressionContainer': from skytemple_files.common.types.file_types import FileType new_container = cls() compressed_data = FileType.BPC_TILEMAP.compress(data) new_container.length_decompressed = u16_checked(len(data)) new_container.compressed_data = compressed_data return new_container
def to_bytes(self): mevo_data = bytearray(MEVO_ENTRY_LENGTH) write_u16(mevo_data, u16_checked(len(self.evos)), 0) for j, x in enumerate(self.evos): write_u16(mevo_data, x, j * 2 + 2) write_u8(mevo_data, u8_checked(len(self.eggs)), 0x12) for j, x in enumerate(self.eggs): write_u16(mevo_data, x, j * 2 + 0x14) return mevo_data
def from_pil(self, img: Image.Image, img_type: WteImageType, discard_palette: bool) -> 'Wte': """ Replace the image data by the new one passed in argument. """ if img.mode != 'P': raise AttributeError( _('Can not convert PIL image to WTE: Must be indexed image (=using a palette)' )) if img_type.has_image: try: self._adjust_actual_dimensions(img.width, img.height) except ValueError: raise ValueError( _('This image is too big to fit into a WTE file.')) self.width = u16_checked(img.width) self.height = u16_checked(img.height) dimensions = self.actual_dimensions() else: self.width = u16(0) self.height = u16(0) self.actual_dim = u8(0) self.image_type = img_type if self.image_type.has_image: raw_image = img.tobytes("raw", "P") self.image_data = bytearray(dimensions[0] // 8 * self.height * img_type.bpp) i = 0 pixels_per_byte = 8 // img_type.bpp for pix in raw_image: b = (i % pixels_per_byte) * img_type.bpp x = i // pixels_per_byte self.image_data[x] += pix << b i += 1 if i % dimensions[0] == self.width: i += dimensions[0] - self.width else: self.image_data = bytearray(0) if discard_palette: self.palette = [] else: self.palette = [x for x in memoryview(img.palette.palette)] return self
def to_bytes(self): """Convert the LSD back to bytes""" data = bytearray(2 + len(self.entries) * MAX_LEN) write_u16(data, u16_checked(len(self.entries)), 0) bytes_written = 2 for e in self.entries: data[bytes_written:bytes_written + MAX_LEN] = self._str_to_bytes(e) bytes_written += MAX_LEN return data[:bytes_written]
def on_sp_effect_id_edited(self, widget, path, text): try: if int(text) >= self.sp_effects.nb_effects() or int(text)<0: return tree_store: Gtk.ListStore = self.builder.get_object('sp_effects_store') tree_store[path][1] = u16_checked(int(text)) except ValueError: return self.sp_effects.set_item_effect_id(tree_store[path][0], tree_store[path][1]) self.on_cb_effect_ids_changed() self.module.mark_sp_effects_as_modified()
def compress(cls, data: bytes) -> 'Atupx': """Create a new ATUPX container from originally uncompressed data.""" from skytemple_files.common.types.file_types import FileType new_container = cls() compressed_data = FileType.CUSTOM_999.compress(data) new_container.compressed_data = compressed_data new_container.length_decompressed = u32_checked(len(data)) new_container.length_compressed = u16_checked( len(compressed_data) + 0xb) return new_container
def set_gummi_iq_gains(value: List[List[int]], arm9: bytearray, config: Pmd2Data, add_types_patch_applied: bool) -> None: dim, byte_size = IQ_GAINS_TABLES[add_types_patch_applied] block = config.binaries['arm9.bin'].symbols['IqGummiGain'] lst_flattened = list(chain.from_iterable(value)) if len(lst_flattened) != dim * dim: raise ValueError("IQ gain table does not match ROM size") if byte_size == 1: for i, b in enumerate(lst_flattened): write_u8(arm9, u8_checked(b), block.begin + i * byte_size) else: for i, b in enumerate(lst_flattened): write_u16(arm9, u16_checked(b), block.begin + i * byte_size)