from ndspy.rom import NintendoDSRom from skytemple_files.common.types.file_types import FileType from skytemple_files.graphics.kao.handler import KaoHandler os.makedirs(os.path.join(os.path.dirname(__file__), 'dbg_output'), exist_ok=True) base_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', '..') rom = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy.nds')) bin = rom.getFileByName('FONT/kaomado.kao') kao = KaoHandler.deserialize(bin) data_length_before = 0 data_length_after = 0 for idx, subidx, kao_image in kao: if kao_image: print(f"== {idx} - {subidx} ==") im = kao_image.get() at_before = FileType.COMMON_AT.deserialize( kao_image.compressed_img_data) print(f"Size before: {at_before.length_compressed}") data_length_before += at_before.length_compressed # Test replacing the image (with the old one, so should be the same result) kao_image.set(im) at_after = FileType.COMMON_AT.deserialize( kao_image.compressed_img_data)
# # You should have received a copy of the GNU General Public License # along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. import os from random import randrange from PIL import Image, ImageDraw from ndspy.rom import NintendoDSRom from skytemple_files.graphics.kao.handler import KaoHandler rom: NintendoDSRom = NintendoDSRom.fromFile( os.path.join('..', '..', '..', '..', '..', '..', 'CLEAN_ROM', 'pmdsky.nds')) kao = KaoHandler.deserialize(rom.getFileByName('FONT/kaomado.kao')) for idx, sidx, img in kao: print(idx, sidx, img) pil = Image.new('P', (40, 40)) pal = bytearray() pil.putpalette([randrange(0, 256) for _ in range(0, 256 * 3)]) for i in range(0, 15): pil.putpixel((i, 0), i) d = ImageDraw.Draw(pil) d.text((5, 5), str(idx), fill=1) d.text((5, 20), str(sidx), fill=2) if img is not None: img.set(pil) if idx in (0, 552, 1153): os.makedirs(f'rgb/{idx:04}', exist_ok=True)
# You should have received a copy of the GNU General Public License # along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. import os from PIL import Image from ndspy.rom import NintendoDSRom from skytemple_files.graphics.kao.handler import KaoHandler dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', '..') rom = NintendoDSRom.fromFile(os.path.join(dir, 'skyworkcopy.nds')) kao_data = rom.getFileByName('FONT/kaomado.kao') kao = KaoHandler.deserialize(kao_data) #kao.get(427, 0).get().show() with open(os.path.join(dir, 'kaomado.kao'), 'wb') as f: f.write(kao_data) with open(os.path.join(dir, 'dh', 'new427.png'), 'rb') as f: i = Image.open(f) kao.get(427, 0).set(i) kao.get(427, 2).set(i) with open(os.path.join(dir, 'dh', 'new051.png'), 'rb') as f: i = Image.open(f) kao.get(51, 0).set(i) kao.get(51, 2).set(i) with open(os.path.join(dir, 'dh', 'new000.png'), 'rb') as f:
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: new_pkmn_str_region = US_NEW_PKMN_STR_REGION new_cat_str_region = US_NEW_CAT_STR_REGION file_assoc = US_FILE_ASSOC table_sf = US_TABLE_SF table_mf = US_TABLE_MF table_sp = US_TABLE_SP if config.game_region == GAME_REGION_EU: new_pkmn_str_region = EU_NEW_PKMN_STR_REGION new_cat_str_region = EU_NEW_CAT_STR_REGION file_assoc = EU_FILE_ASSOC table_sf = EU_TABLE_SF table_mf = EU_TABLE_MF table_sp = EU_TABLE_SP if not self.is_applied(rom, config): bincfg = config.binaries['arm9.bin'] binary = bytearray(get_binary_from_rom_ppmdu(rom, bincfg)) # Apply the patch for filename in get_files_from_rom_with_extension(rom, 'str'): bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) block = config.string_index_data.string_blocks['Pokemon Names'] monsters = strings.strings[block.begin:block.end] strings.strings[block.begin:block.end] = [""] * (block.end - block.begin) block = config.string_index_data.string_blocks[ 'Pokemon Categories'] cats = strings.strings[block.begin:block.end] strings.strings[block.begin:block.end] = [""] * (block.end - block.begin) for x in range(NUM_NEW_ENTRIES): if x < NUM_PREVIOUS_ENTRIES * 2: str_pkmn = monsters[x % NUM_PREVIOUS_ENTRIES] else: str_pkmn = "DmyPk%04d" % x if len(strings.strings) <= new_pkmn_str_region + x - 1: strings.strings.append(str_pkmn) else: strings.strings[new_pkmn_str_region + x - 1] = str_pkmn for x in range(NUM_NEW_ENTRIES): if x < NUM_PREVIOUS_ENTRIES * 2: str_cat = cats[x % NUM_PREVIOUS_ENTRIES] else: str_cat = "DmyCa%04d" % x if len(strings.strings) <= new_cat_str_region + x - 1: strings.strings.append(str_cat) else: strings.strings[new_cat_str_region + x - 1] = str_cat bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) sorted_list = list( enumerate(strings.strings[new_pkmn_str_region - 1:new_pkmn_str_region - 1 + NUM_NEW_ENTRIES])) sorted_list.sort(key=lambda x: normalize_string(x[1])) sorted_list = [x[0] for x in sorted_list] inv_sorted_list = [ sorted_list.index(i) for i in range(NUM_NEW_ENTRIES) ] m2n_model = ValListHandler.deserialize( rom.getFileByName(file_assoc[filename][0])) m2n_model.set_list(inv_sorted_list) rom.setFileByName(file_assoc[filename][0], ValListHandler.serialize(m2n_model)) n2m_model = ValListHandler.deserialize( rom.getFileByName(file_assoc[filename][1])) n2m_model.set_list(sorted_list) rom.setFileByName(file_assoc[filename][1], ValListHandler.serialize(n2m_model)) # Expand kao file kao_bin = rom.getFileByName('FONT/kaomado.kao') kao_model = KaoHandler.deserialize(kao_bin) kao_model.expand(NUM_NEW_ENTRIES - 1) for i in range(NUM_PREVIOUS_ENTRIES - 1): for j in range(SUBENTRIES): a = kao_model.get(i, j) b = kao_model.get(i + NUM_PREVIOUS_ENTRIES, j) if b == None and a != None: kao_model.set(i + NUM_PREVIOUS_ENTRIES, j, a) rom.setFileByName('FONT/kaomado.kao', KaoHandler.serialize(kao_model)) # Expand tbl_talk tlk_bin = rom.getFileByName('MESSAGE/tbl_talk.tlk') tlk_model = TblTalkHandler.deserialize(tlk_bin) while tlk_model.get_nb_monsters() < NUM_NEW_ENTRIES: tlk_model.add_monster_personality(DUMMY_PERSONALITY) rom.setFileByName('MESSAGE/tbl_talk.tlk', TblTalkHandler.serialize(tlk_model)) # Add monsters md_bin = rom.getFileByName('BALANCE/monster.md') md_model = MdHandler.deserialize(md_bin) while len(md_model.entries) < NUM_NEW_ENTRIES: md_model.entries.append( MdEntry.new_empty(u16_checked(len(md_model.entries)))) for i in range(NUM_PREVIOUS_ENTRIES): md_model.entries[i].entid = i if md_model.entries[NUM_PREVIOUS_ENTRIES + i].gender == Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + i].entid = NUM_PREVIOUS_ENTRIES + i else: md_model.entries[NUM_PREVIOUS_ENTRIES + i].entid = i block = bincfg.symbols['MonsterSpriteData'] data = binary[block.begin:block.end] + binary[block.begin:block. end] data += b'\x00\x00' * (NUM_NEW_ENTRIES - (len(data) // 2)) for i in range(0, len(data), 2): md_model.entries[i // 2].unk17 = data[i] md_model.entries[i // 2].unk18 = data[i + 1] md_model.entries[i // 2].bitfield1_0 = False md_model.entries[i // 2].bitfield1_1 = False md_model.entries[i // 2].bitfield1_2 = False md_model.entries[i // 2].bitfield1_3 = False x = table_sf while read_u16(rom.arm9, x) != 0: pkmn_id = read_u16(rom.arm9, x) md_model.entries[pkmn_id].bitfield1_3 = True # pylint: disable=invalid-sequence-index if md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].gender != Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_3 = True x += 2 x = table_mf while read_u16(rom.arm9, x) != 0: pkmn_id = read_u16(rom.arm9, x) md_model.entries[pkmn_id].bitfield1_2 = True # pylint: disable=invalid-sequence-index if md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].gender != Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_2 = True x += 2 ov19 = rom.loadArm9Overlays([19])[19].data for x in range(table_sp, table_sp + TABLE_SP_SIZE, 2): pkmn_id = read_u16(ov19, x) md_model.entries[pkmn_id].bitfield1_1 = True # pylint: disable=invalid-sequence-index md_model.entries[pkmn_id].bitfield1_0 = True # pylint: disable=invalid-sequence-index if md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].gender != Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_1 = True md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_0 = True rom.setFileByName('BALANCE/monster.md', MdHandler.serialize(md_model)) # Edit Mappa bin mappa_bin = rom.getFileByName('BALANCE/mappa_s.bin') mappa_model = MappaBinHandler.deserialize(mappa_bin) dl = HardcodedDungeons.get_dungeon_list(bytes(rom.arm9), config) # Handle Dojos start_floor = 0 for x in range(DOJO_DUNGEONS_FIRST, DOJO_DUNGEONS_LAST - 2): dl.append( DungeonDefinition(u8(5), DOJO_MAPPA_ENTRY, u8(start_floor), u8(0))) start_floor += 5 dl.append( DungeonDefinition(u8(1), DOJO_MAPPA_ENTRY, u8(start_floor), u8(0))) start_floor += 1 dl.append( DungeonDefinition(u8(0x30), DOJO_MAPPA_ENTRY, u8(start_floor), u8(0))) start_floor += 0x30 for dungeon in dl: for f in range(dungeon.start_after + 1, dungeon.start_after + dungeon.number_floors, 2): try: for entry in mappa_model.floor_lists[ dungeon.mappa_index][f].monsters: if entry.md_index != DUMMY_PKMN and entry.md_index < NUM_PREVIOUS_ENTRIES and \ entry.md_index + NUM_PREVIOUS_ENTRIES < len(md_model.entries) and \ md_model.entries[entry.md_index + NUM_PREVIOUS_ENTRIES].gender != Gender.INVALID: entry.md_index += NUM_PREVIOUS_ENTRIES except: print(f"{dungeon.mappa_index}, {f} is not valid.") rom.setFileByName('BALANCE/mappa_s.bin', MappaBinHandler.serialize(mappa_model)) # Add moves waza_p_bin = rom.getFileByName('BALANCE/waza_p.bin') waza_p_model = WazaPHandler.deserialize(waza_p_bin) while len(waza_p_model.learnsets) < NUM_PREVIOUS_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset waza_p_model.learnsets = waza_p_model.learnsets + waza_p_model.learnsets while len(waza_p_model.learnsets) < NUM_NEW_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset rom.setFileByName('BALANCE/waza_p.bin', WazaPHandler.serialize(waza_p_model)) # Add moves 2 waza_p_bin = rom.getFileByName('BALANCE/waza_p2.bin') waza_p_model = WazaPHandler.deserialize(waza_p_bin) while len(waza_p_model.learnsets) < NUM_PREVIOUS_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset waza_p_model.learnsets = waza_p_model.learnsets + waza_p_model.learnsets while len(waza_p_model.learnsets) < NUM_NEW_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset rom.setFileByName('BALANCE/waza_p2.bin', WazaPHandler.serialize(waza_p_model)) # Add levels level_bin = rom.getFileByName('BALANCE/m_level.bin') level_model = BinPackHandler.deserialize(level_bin) while len(level_model.get_files_bytes()) < NUM_PREVIOUS_ENTRIES: new_bytes_unpacked = bytes(LEVEL_BIN_ENTRY_LEVEL_LEN * 100) new_bytes_pkdpx = PkdpxHandler.serialize( PkdpxHandler.compress(new_bytes_unpacked)) new_bytes = Sir0Handler.serialize( Sir0Handler.wrap(new_bytes_pkdpx, [])) level_model.append(new_bytes) # Empty Levelup data for i in range(NUM_PREVIOUS_ENTRIES): level_model.append(level_model[i]) while len(level_model.get_files_bytes()) < NUM_NEW_ENTRIES: new_bytes_unpacked = bytes(LEVEL_BIN_ENTRY_LEVEL_LEN * 100) new_bytes_pkdpx = PkdpxHandler.serialize( PkdpxHandler.compress(new_bytes_unpacked)) new_bytes = Sir0Handler.serialize( Sir0Handler.wrap(new_bytes_pkdpx, [])) level_model.append(new_bytes) # Empty Levelup data rom.setFileByName('BALANCE/m_level.bin', BinPackHandler.serialize(level_model)) # Add evolutions evo_bin = rom.getFileByName('BALANCE/md_evo.bin') evo_model = MdEvoHandler.deserialize(evo_bin) while len(evo_model.evo_entries) < NUM_NEW_ENTRIES: evo_model.evo_entries.append( MdEvoEntry(bytearray(MEVO_ENTRY_LENGTH))) while len(evo_model.evo_stats) < NUM_NEW_ENTRIES: evo_model.evo_stats.append( MdEvoStats(bytearray(MEVO_STATS_LENGTH))) rom.setFileByName('BALANCE/md_evo.bin', MdEvoHandler.serialize(evo_model)) # Fixed floors ov29 = config.binaries['overlay/overlay_0029.bin'] ov29bin = bytearray(get_binary_from_rom_ppmdu(rom, ov29)) monster_list = HardcodedFixedFloorTables.get_monster_spawn_list( ov29bin, config) for m in monster_list: if m.md_idx >= NUM_PREVIOUS_MD_MAX: m.md_idx += NUM_NEW_ENTRIES - NUM_PREVIOUS_MD_MAX HardcodedFixedFloorTables.set_monster_spawn_list( ov29bin, monster_list, config) set_binary_in_rom_ppmdu(rom, ov29, bytes(ov29bin)) try: apply() except RuntimeError as ex: raise ex