def _set_generic(ov11: bytes, config: Pmd2Data, block_name: str, bytelen: int, value: List[int]): block = config.binaries['overlay/overlay_0011.bin'].blocks[block_name] expected_length = int((block.end - block.begin) / bytelen) if len(value) != expected_length: raise ValueError( f"The list must have exactly the length of {expected_length} entries." ) for i, entry in enumerate(value): write_uintle(ov11, entry, block.begin + i * bytelen, bytelen)
def to_mappa(self): data = bytearray(8) write_uintle(data, self.level * LEVEL_MULTIPLIER, 0x00, 2) write_uintle(data, self.weight, 0x02, 2) write_uintle(data, self.weight2, 0x04, 2) write_uintle(data, self.md_index, 0x06, 2) return data
def to_bytes(self) -> bytes: buffer = bytearray(ENTRY_LEN) write_uintle(buffer, self.rank_name_str, 0x00, 4) write_uintle(buffer, self.points_needed_next, 0x04, 4) write_uintle(buffer, self.storage_capacity, 0x08, 4) write_uintle(buffer, self.item_awarded, 0x0C, 4) return buffer
def to_bytes(self) -> bytes: buffer = bytearray(8) write_uintle(buffer, self.item_id, 0, 4) write_uintle(buffer, self.null1, 2, 2) write_uintle(buffer, self.null2, 4, 2) write_uintle(buffer, self.null3, 6, 2) return buffer
def to_bytes(self) -> bytes: buffer = bytearray(4) write_uintle(buffer, self.trap_id, 0, 1) write_uintle(buffer, self.trap_data, 1, 1) write_uintle(buffer, self.room_id, 2, 1) write_uintle(buffer, self.flags, 3, 1) return buffer
def set_player_md_ids(value: List[int], overlay13: bytearray, config: Pmd2Data) -> None: """ Sets the monster.md indices of the player partner choices (in place, total index, with gender form!) The length of the list must exactly match the original ROM's length (see get_player_md_ids). """ block = config.binaries['overlay/overlay_0013.bin'].blocks[ 'StartersHeroIds'] expected_length = int((block.end - block.begin) / 2) if len(value) != expected_length: raise ValueError( f"The ID list must have exactly the length of {expected_length} entries." ) for i, v in enumerate(value): write_uintle(overlay13, v, block.begin + (i * 2), 2)
def corrupt341(): """Corrupt 341: Dungeon tiles Beach cave 2? -- No? Tiles -> Chunk mappings or similar?""" img341 = dungeon_bin[341].decompress() img341new = bytearray(img341) # Decode XOR #XOR_ROW_LEN = 7200#18 * 7 #rows_decoded = [] #row_before = bytes(XOR_ROW_LEN) #for chunk in iter_bytes(img341, XOR_ROW_LEN): # xored = bytearray(a ^ b for (a, b) in zip(chunk, row_before)) # row_before = xored # rows_decoded.append(xored) dummy_map = [ TilemapEntry(10, False, False, 0), TilemapEntry(10, True, False, 0), TilemapEntry(10, False, True, 0), TilemapEntry(5, False, False, 0), TilemapEntry(5, True, False, 0), TilemapEntry(5, False, True, 0), TilemapEntry(10, False, False, 6), TilemapEntry(10, True, False, 6), TilemapEntry(10, False, True, 6) ] for j in range(1, 300): for i, m in enumerate(dummy_map): write_uintle(img341new, m.to_int(), (j * 18) + 2 * i, 2) all_tilemaps = [] for bytes2 in iter_bytes(img341new, 2): all_tilemaps.append(TilemapEntry.from_int(read_uintle(bytes2, 0, 2))) # Encode XOR #rows_encoded = [] #row_before = bytes(XOR_ROW_LEN) #for row in rows_decoded: # xored = bytes(a ^ b for (a, b) in zip(row, row_before)) # row_before = row # rows_encoded.append(xored) #img341new = bytes(itertools.chain.from_iterable(rows_encoded)) #assert img341 == img341new with open('/tmp/corrupt.bin', 'wb') as f: f.write(img341new) dungeon_bin[341] = FileType.AT4PX.compress(img341new)
def _wrap_sir0(self, full_binary: bytes, table_data: bytes, entry_len: int, string_offs_per_entry: List[int], write_subheader) -> bytes: table_data = bytearray(table_data) out_data = bytearray() pointer_offsets = [] # 1. Write strings number_entries = 0 for i in range(0, len(table_data), entry_len): for string_off in string_offs_per_entry: new_pointer = self._push_string( full_binary, out_data, read_uintle(table_data, i + string_off, 4) - self._binary.loadaddress) pointer_offsets.append(i + string_off) write_uintle(table_data, new_pointer, i + string_off, 4) number_entries += 1 # Padding self._pad(out_data) # 2. Correct string pointer offsets pointer_offsets = [off + len(out_data) for off in pointer_offsets] # 3. Append table pointer_data_block = len(out_data) out_data += table_data # Padding self._pad(out_data) # 4. Write sub-header if write_subheader: data_pointer = len(out_data) pointer_offsets.append(len(out_data)) out_data += pointer_data_block.to_bytes(4, byteorder='little', signed=False) out_data += number_entries.to_bytes(4, byteorder='little', signed=False) else: data_pointer = pointer_data_block # 5. Convert into SIR0 return FileType.SIR0.serialize( FileType.SIR0.wrap(out_data, pointer_offsets, data_pointer))
def sir0_serialize_parts(self) -> Tuple[bytes, List[int], Optional[int]]: string_codec.init() out_data = bytearray() # 1. Write strings pointer_offsets: List[int] = [] for entry in self.list: pointer_offsets.append(len(out_data)) out_data += bytes(entry.name, string_codec.PMD2_STR_ENCODER) + b'\0' # Padding self._pad(out_data) # Write table sir0_pointer_offsets = [] pointer_data_block = len(out_data) for i, entry in enumerate(self.list): entry_buffer = bytearray(LEN_ACTOR_ENTRY) write_uintle(entry_buffer, entry.type, 0, 2) write_uintle(entry_buffer, entry.entid, 2, 2) sir0_pointer_offsets.append(len(out_data) + 4) write_uintle(entry_buffer, pointer_offsets[i], 4, 4) write_uintle(entry_buffer, entry.unk3, 8, 2) write_uintle(entry_buffer, entry.unk4, 10, 2) out_data += entry_buffer # Padding self._pad(out_data) # 4. Write sub-header data_pointer = len(out_data) sir0_pointer_offsets.append(len(out_data)) out_data += pointer_data_block.to_bytes(4, byteorder='little', signed=False) out_data += len(self.list).to_bytes(4, byteorder='little', signed=False) return out_data, sir0_pointer_offsets, data_pointer
def to_bytes(self) -> bytes: bitfield0 = generate_bitfield( (self.dont_save_before_entering, self.leader_can_be_changed, self.money_allowed, self.level_reset, self.recruiting_allowed, self.enemies_grant_exp, self.enemies_evolve_when_team_member_koed, bool(self.direction.value))) bitfield1 = generate_bitfield( (False, False, False, False, False, self.enemies_can_drop_chests, self.traps_remain_invisible_on_attack, self.iq_skills_disabled)) bitfield2 = 0 bitfield3 = 0 buff = bytearray(DUNGEON_RESTRICTIONS_ENTRY_LEN) write_uintle(buff, bitfield0, 0, 1) write_uintle(buff, bitfield1, 1, 1) write_uintle(buff, bitfield2, 2, 1) write_uintle(buff, bitfield3, 3, 1) write_sintle(buff, self.max_rescue_attempts, 4, 1) write_sintle(buff, self.max_items_allowed, 5, 1) write_sintle(buff, self.max_party_members, 6, 1) write_sintle(buff, self.null7, 7, 1) write_sintle(buff, self.turn_limit, 8, 2) write_sintle(buff, self.nullA, 10, 1) write_sintle(buff, self.nullB, 11, 1) return buff
def set_player_md_id(value: int, arm9: bytearray, config: Pmd2Data): """ Sets the monster.md index of the default player starter """ block = config.binaries['arm9.bin'].blocks['DefaultHeroId'] write_uintle(arm9, value, block.begin, 2)
def main(): os.makedirs(output_dir, exist_ok=True) rom = NintendoDSRom.fromFile(os.path.join(base_dir, 'skyworkcopy_edit.nds')) bin_before = rom.getFileByName('SCRIPT/G01P01A/enter.sse') ssa_before = SsaHandler.deserialize(bin_before) data = Pmd2XmlReader.load_default() scriptdata = data.script_data ssa_before.layer_list[0].objects = [ # TODO: 5=Width, 1=Height! SsaObject( scriptdata, 6, 5, 1, SsaPosition(scriptdata, 44, 24, 0, 0, scriptdata.directions__by_name['Down'].id), 10, -1) ] # Write NPC types npc_table_start = data.binaries['arm9.bin'].blocks['Entities'].begin NPC_TABLE_ENTRY_LEN = 0x0c # uint16: type, uint16: entid, uint32: pointer to name, unk3, unk4 # Shaymin NPC_SHEIMI 534 / V02P06A ent_id__shaymin = scriptdata.level_entities__by_name['NPC_SHEIMI'].id print( read_uintle( rom.arm9, npc_table_start + ent_id__shaymin * NPC_TABLE_ENTRY_LEN + 0x02, 2)) write_uintle( rom.arm9, 534, npc_table_start + ent_id__shaymin * NPC_TABLE_ENTRY_LEN + 0x02, 2) # Elekid NPC_SHEIMI1 266 / V02P07A ent_id__elekid = scriptdata.level_entities__by_name['NPC_SHEIMI1'].id print( read_uintle( rom.arm9, npc_table_start + ent_id__elekid * NPC_TABLE_ENTRY_LEN + 0x02, 2)) write_uintle(rom.arm9, 266, npc_table_start + ent_id__elekid * NPC_TABLE_ENTRY_LEN + 0x02, 2) # Piplup NPC_SHEIMI2 428 / V03P01A ent_id__piplup = scriptdata.level_entities__by_name['NPC_SHEIMI2'].id print( read_uintle( rom.arm9, npc_table_start + ent_id__piplup * NPC_TABLE_ENTRY_LEN + 0x02, 2)) write_uintle(rom.arm9, 428, npc_table_start + ent_id__piplup * NPC_TABLE_ENTRY_LEN + 0x02, 2) # Meowth NPC_SHEIMI3 52 / V03P02A ent_id__meowth = scriptdata.level_entities__by_name['NPC_SHEIMI3'].id print( read_uintle( rom.arm9, npc_table_start + ent_id__meowth * NPC_TABLE_ENTRY_LEN + 0x02, 2)) write_uintle(rom.arm9, 52, npc_table_start + ent_id__meowth * NPC_TABLE_ENTRY_LEN + 0x02, 2) # Buneary NPC_SHEIMI4 469 / V03P03A ent_id__buneary = scriptdata.level_entities__by_name['NPC_SHEIMI4'].id print( read_uintle( rom.arm9, npc_table_start + ent_id__buneary * NPC_TABLE_ENTRY_LEN + 0x02, 2)) write_uintle( rom.arm9, 469, npc_table_start + ent_id__buneary * NPC_TABLE_ENTRY_LEN + 0x02, 2) ssa_before.layer_list[0].actors = [ SsaActor( scriptdata, ent_id__shaymin, SsaPosition(scriptdata, 14, 24, 2, 0, scriptdata.directions__by_name['Down'].id), 5, -1), SsaActor( scriptdata, ent_id__elekid, SsaPosition(scriptdata, 20, 24, 2, 0, scriptdata.directions__by_name['Down'].id), 6, -1), SsaActor( scriptdata, ent_id__piplup, SsaPosition(scriptdata, 26, 24, 2, 0, scriptdata.directions__by_name['Down'].id), 7, -1), SsaActor( scriptdata, ent_id__meowth, SsaPosition(scriptdata, 32, 24, 2, 0, scriptdata.directions__by_name['Down'].id), 8, -1), SsaActor( scriptdata, ent_id__buneary, SsaPosition(scriptdata, 38, 24, 2, 0, scriptdata.directions__by_name['Down'].id), 9, -1), # Mimikyu NPC_PUKURIN 40 / V03P04A # Litten NPC_ZUBATTO 41 / V04P02A # Zorua NPC_DIGUDA 50 / V03P13A ] ssa_before.layer_list[0].events = [ SsaEvent(6, 2, 1, 0, SsaPosition(scriptdata, 27, 0, 0, 0, None), 65535), SsaEvent(6, 2, 2, 0, SsaPosition(scriptdata, 27, 49, 0, 0, None), 65535), ] # Exit Guild ssa_before.layer_list[1].actors = [ SsaActor( scriptdata, 0, SsaPosition(scriptdata, 29, 7, 2, 0, scriptdata.directions__by_name['Down'].id), -1, -1), SsaActor( scriptdata, 10, SsaPosition(scriptdata, 29, 4, 2, 0, scriptdata.directions__by_name['Down'].id), -1, -1) ] # Exit Town ssa_before.layer_list[2].actors = [ SsaActor( scriptdata, 0, SsaPosition(scriptdata, 29, 44, 2, 0, scriptdata.directions__by_name['Up'].id), -1, -1), SsaActor( scriptdata, 10, SsaPosition(scriptdata, 29, 47, 2, 0, scriptdata.directions__by_name['Up'].id), -1, -1) ] # Create scripts, if don't exist tpl_ssb = rom.getFileByName('SCRIPT/G01P01A/enter01.ssb') try: rom.getFileByName('SCRIPT/G01P01A/enter05.ssb') except ValueError: create_file_in_rom(rom, 'SCRIPT/G01P01A/enter05.ssb', tpl_ssb) try: rom.getFileByName('SCRIPT/G01P01A/enter06.ssb') except ValueError: create_file_in_rom(rom, 'SCRIPT/G01P01A/enter06.ssb', tpl_ssb) try: rom.getFileByName('SCRIPT/G01P01A/enter07.ssb') except ValueError: create_file_in_rom(rom, 'SCRIPT/G01P01A/enter07.ssb', tpl_ssb) try: rom.getFileByName('SCRIPT/G01P01A/enter08.ssb') except ValueError: create_file_in_rom(rom, 'SCRIPT/G01P01A/enter08.ssb', tpl_ssb) try: rom.getFileByName('SCRIPT/G01P01A/enter09.ssb') except ValueError: create_file_in_rom(rom, 'SCRIPT/G01P01A/enter09.ssb', tpl_ssb) try: rom.getFileByName('SCRIPT/G01P01A/enter10.ssb') except ValueError: create_file_in_rom(rom, 'SCRIPT/G01P01A/enter10.ssb', tpl_ssb) bin_after = SsaHandler.serialize(ssa_before) rom.setFileByName('SCRIPT/G01P01A/enter.sse', bin_after) rom.saveToFile(os.path.join(base_dir, 'skyworkcopy_edit.nds')) draw_maps_main()
def to_bytes(self): buffer = bytearray(2 * len(self.mappings)) for i, m in enumerate(self.mappings): write_uintle(buffer, m, i * 2, 2) return buffer
def to_mappa(self): data = bytearray(50) for i in range(0, 25): write_uintle(data, self.weights[MappaTrapType(i)], i * 2, 2) return data
def to_mappa(self) -> bytes: data = bytearray(32) write_uintle(data, self.structure.value, 0x00, 1) write_uintle(data, self.room_density, 0x01, 1) write_uintle(data, self.tileset_id, 0x02, 1) write_uintle(data, self.music_id, 0x03, 1) write_uintle(data, self.weather.value, 0x04, 1) write_uintle(data, self.floor_connectivity, 0x05, 1) write_uintle(data, self.initial_enemy_density, 0x06, 1) write_uintle(data, self.kecleon_shop_chance, 0x07, 1) write_uintle(data, self.monster_house_chance, 0x08, 1) write_uintle(data, self.unusued_chance, 0x09, 1) write_uintle(data, self.sticky_item_chance, 0x0A, 1) write_uintle(data, self.dead_ends, 0x0B, 1) write_uintle(data, self.secondary_terrain.value, 0x0C, 1) write_uintle(data, self.terrain_settings.to_mappa(), 0x0D, 1) write_uintle(data, self.unk_e, 0x0E, 1) write_uintle(data, self.item_density, 0x0F, 1) write_uintle(data, self.trap_density, 0x10, 1) write_uintle(data, self.floor_number, 0x11, 1) write_uintle(data, self.fixed_floor_id, 0x12, 1) write_uintle(data, self.extra_hallway_density, 0x13, 1) write_uintle(data, self.buried_item_density, 0x14, 1) write_uintle(data, self.water_density, 0x15, 1) write_uintle(data, self.darkness_level.value, 0x16, 1) write_uintle(data, int(self.max_coin_amount / 5), 0x17, 1) write_uintle(data, self.kecleon_shop_item_positions, 0x18, 1) write_uintle(data, self.empty_monster_house_chance, 0x19, 1) write_uintle(data, self.unk_hidden_stairs, 0x1A, 1) write_uintle(data, self.hidden_stairs_spawn_chance, 0x1B, 1) write_uintle(data, self.enemy_iq, 0x1C, 2) write_uintle(data, self.iq_booster_enabled, 0x1E, 1) return data
def to_bytes(self) -> bytes: buffer = bytearray(4) write_uintle(buffer, self.md_idx, 0, 2) write_uintle(buffer, self.stats_entry, 2, 1) write_uintle(buffer, self.enemy_settings.value, 3, 1) return buffer
def to_bytes(self) -> bytes: buffer = bytearray(12) write_uintle(buffer, self.item_id * 8 + self._overlay29bin.blocks['ItemSpawnTable'].begin_absolute, 0, 4) write_uintle(buffer, self.monster_id * 4 + self._overlay29bin.blocks['MonsterSpawnTable'].begin_absolute, 4, 4) write_uintle(buffer, self.tile_id * 4 + self._overlay29bin.blocks['TileSpawnTable'].begin_absolute, 8, 4) return buffer
def to_bytes(self) -> bytes: buffer = bytearray(12) write_uintle(buffer, self.music_track, 0, 4) write_uintle(buffer, self.unk4, 4, 1) write_uintle(buffer, self.unk5, 5, 1) write_uintle(buffer, self.moves_enabled, 6, 1) write_uintle(buffer, self.orbs_enabled, 7, 1) write_uintle(buffer, self.unk8, 8, 1) write_uintle(buffer, self.unk9, 9, 1) write_uintle(buffer, self.exit_floor_when_defeating_enemies, 10, 1) write_uintle(buffer, self.null, 11, 1) return buffer
def to_bytes(self) -> bytes: buffer = bytearray(12) write_uintle(buffer, self.level, 0, 2) write_uintle(buffer, self.hp, 2, 2) write_uintle(buffer, self.exp_yield, 4, 2) write_uintle(buffer, self.attack, 6, 1) write_uintle(buffer, self.special_attack, 7, 1) write_uintle(buffer, self.defense, 8, 1) write_uintle(buffer, self.special_defense, 9, 1) write_uintle(buffer, self.unkA, 10, 2) return buffer
def set_player_level(value: int, arm9: bytearray, config: Pmd2Data): """ Sets the level of the player starter """ block = config.binaries['arm9.bin'].blocks['HeroStartLevel'] write_uintle(arm9, value, block.begin, 1)