def from_bytes(cls, b: bytes) -> 'DungeonRestriction': bitfield0 = read_uintle(b, 0) bitfield1 = read_uintle(b, 1) dir_bool, enemies_evolve_when_team_member_koed, enemies_grant_exp, recruiting_allowed, \ level_reset, money_allowed, leader_can_be_changed, dont_save_before_entering = \ (bool(bitfield0 >> i & 1) for i in range(8)) iq_skills_disabled, traps_remain_invisible_on_attack, enemies_can_drop_chests = \ (bool(bitfield1 >> i & 1) for i in range(3)) assert read_uintle(b, 2) == 0 assert read_uintle(b, 3) == 0 return cls( DungeonRestrictionDirection(int(dir_bool)), enemies_evolve_when_team_member_koed, enemies_grant_exp, recruiting_allowed, level_reset, money_allowed, leader_can_be_changed, dont_save_before_entering, iq_skills_disabled, traps_remain_invisible_on_attack, enemies_can_drop_chests, read_sintle(b, 4), read_sintle(b, 5), read_sintle(b, 6), read_sintle(b, 7), read_sintle(b, 8, 2), read_sintle(b, 10), read_sintle(b, 11), )
def get(cls, arm9bin: bytes, config: Pmd2Data) -> List[MonsterSpriteDataTableEntry]: """Returns the list.""" block = config.binaries['arm9.bin'].blocks['MonsterSpriteData'] lst = [] for i in range(block.begin, block.end, ENTRY_LEN): lst.append( MonsterSpriteDataTableEntry(read_uintle(arm9bin, i + 0x00, 1), read_uintle(arm9bin, i + 0x01, 1))) return lst
def _write_pair24(self, data: bytes): """Writes 4 bytes of 2 16 bit LE integers in pair24 encoding.""" assert len(data) == 4 int1 = read_uintle(data, 0, 2) int2 = read_uintle(data, 2, 2) pair24 = ((int1 & 0xff) << 16) + ((int2 & 0xf) << 12) + (int1 & 0xf00) + ((int2 & 0xff0) >> 4) if DEBUG: print(f"W {int1:02x} and {int2:02x} as {pair24:06x}") self.compressed_data[self.bytes_written:self.bytes_written+3] = pair24.to_bytes(3, 'big') self.bytes_written += 3
def get_rank_up_table(cls, arm9bin: bytes, config: Pmd2Data) -> List[Rank]: """Returns the list of ranks in the game.""" block = config.binaries['arm9.bin'].blocks['RankUpTable'] lst = [] for i in range(block.begin, block.end, ENTRY_LEN): lst.append( Rank(read_uintle(arm9bin, i + 0x00, 4), read_uintle(arm9bin, i + 0x04, 4), read_uintle(arm9bin, i + 0x08, 4), read_uintle(arm9bin, i + 0x0C, 4))) return lst
def get_monster_spawn_list(cls, overlay29: bytes, config: Pmd2Data) -> List[MonsterSpawn]: """ Returns the list of monsters that can be spawned in fixed floors. """ block = config.binaries['overlay/overlay_0029.bin'].blocks['MonsterSpawnTable'] lst = [] for i in range(block.begin, block.end, 4): lst.append(MonsterSpawn( read_uintle(overlay29, i + 0x00, 2), read_uintle(overlay29, i + 0x02, 1), read_uintle(overlay29, i + 0x03, 1) )) return lst
def get_dungeon_list(arm9bin: bytes, config: Pmd2Data) -> List[DungeonDefinition]: """Returns the list of dungeon definitions.""" block = config.binaries['arm9.bin'].blocks['DungeonList'] lst = [] for i in range(block.begin, block.end, DUNGEON_LIST_ENTRY_LEN): lst.append( DungeonDefinition( read_uintle(arm9bin, i), read_uintle(arm9bin, i + 1), read_uintle(arm9bin, i + 2), read_uintle(arm9bin, i + 3), )) return lst
def get_item_spawn_list(cls, overlay29: bytes, config: Pmd2Data) -> List[ItemSpawn]: """ Returns the list of items that can be spawned in fixed floors. """ block = config.binaries['overlay/overlay_0029.bin'].blocks['ItemSpawnTable'] lst = [] for i in range(block.begin, block.end, 8): lst.append(ItemSpawn( read_uintle(overlay29, i + 0x00, 2), read_uintle(overlay29, i + 0x02, 2), read_uintle(overlay29, i + 0x04, 2), read_uintle(overlay29, i + 0x06, 2), )) return lst
def list_from_mappa(cls, read: 'MappaBinReadContainer', pointer: int) -> List['MappaMonster']: monsters = [] while not cls._is_end_of_entries(read.data, pointer): monsters.append( MappaMonster( int( read_uintle(read.data, pointer + 0, 2) / LEVEL_MULTIPLIER), read_uintle(read.data, pointer + 2, 2), read_uintle(read.data, pointer + 4, 2), read_uintle(read.data, pointer + 6, 2), )) pointer += 8 return monsters
def get_ground_dungeon_tilesets( overlay11bin: bytes, config: Pmd2Data) -> List[GroundTilesetMapping]: """Returns the list.""" block = config.binaries['overlay/overlay_0011.bin'].blocks[ 'LevelTilemapList'] lst = [] for i in range(block.begin, block.end, 8): lst.append( GroundTilesetMapping( read_uintle(overlay11bin, i, 2), read_uintle(overlay11bin, i + 2, 1), read_uintle(overlay11bin, i + 3, 1), read_uintle(overlay11bin, i + 4, 4), )) return lst
def get_entity_spawn_table(cls, overlay29: bytes, config: Pmd2Data) -> List[EntitySpawnEntry]: """ Returns the list of entity spawns. Each entry has three references, one to each of the other three tables (item spawn, monster spawn, tile type). """ binary = config.binaries['overlay/overlay_0029.bin'] block = binary.blocks['EntitySpawnTable'] lst = [] for i in range(block.begin, block.end, 12): lst.append(EntitySpawnEntry( binary, read_uintle(overlay29, i + 0x00, 4), read_uintle(overlay29, i + 0x04, 4), read_uintle(overlay29, i + 0x08, 4), )) return lst
def _look_ahead_byte_sequence(self, cursor_process_multiplier): """Look ahead for the next byte sequence until the first repeating pattern starts""" seq = bytearray(NRL_LOOKAHEAD_COPY_BYTES_MAX_BYTES) seq_len = 0 # If the repeat counter reaches NRL_MIN_SEQ_LEN, the sequence ends NRL_MIN_SEQ_LEN entries before that repeat_counter = 0 previous_byt_at_pos = 0x100 # Impossible "null" value for now nc = self.cursor while True: byt_at_pos = read_uintle(self.uncompressed_data, nc) if byt_at_pos == previous_byt_at_pos: repeat_counter += 1 else: repeat_counter = 0 previous_byt_at_pos = byt_at_pos seq[seq_len] = byt_at_pos if repeat_counter > NRL_MIN_SEQ_LEN: seq_len -= NRL_MIN_SEQ_LEN break if seq_len + 1 >= NRL_LOOKAHEAD_COPY_BYTES_MAX_BYTES or nc >= self.length_input: break seq_len += 1 nc += cursor_process_multiplier return seq_len, seq[:seq_len]
def _get_generic(ov11: bytes, config: Pmd2Data, block_name: str, bytelen: int): block = config.binaries['overlay/overlay_0011.bin'].blocks[block_name] lst = [] for i in range(block.begin, block.end, bytelen): lst.append(read_uintle(ov11, i, bytelen)) return lst
def _read(self): """Read a single byte and increase cursor""" if self.cursor >= self.length_input: raise ValueError("BMA Collision RLE Compressor: Reached EOF while reading data.") oc = self.cursor self.cursor += 1 return 1 if read_uintle(self.uncompressed_data, oc) > 0 else 0
def get_player_md_ids(overlay13: bytes, config: Pmd2Data) -> List[int]: """Returns the monster.md indices of the player starter choices (total index, with gender form!)""" block = config.binaries['overlay/overlay_0013.bin'].blocks[ 'StartersHeroIds'] ids = [] for i in range(block.begin, block.end, 2): ids.append(read_uintle(overlay13, i, 2)) return ids
def _read(self, cursor_process_multiplier): """Read a single byte and increase cursor""" if self.cursor >= self.length_input: raise ValueError( "Generic NRL Compressor: Reached EOF while reading data.") oc = self.cursor self.cursor += cursor_process_multiplier return read_uintle(self.uncompressed_data, oc)
def get_fixed_floor_overrides(cls, overlay29: bytes, config: Pmd2Data) -> List[int]: """ Returns the list of overrides for fixed floors. """ block = config.binaries['overlay/overlay_0029.bin'].blocks['FixedFloorOverrides'] lst = [] for i in range(block.begin, block.end): lst.append(read_uintle(overlay29, i, 1)) return lst
def _look_ahead_repeats(self, data): """Look how often the byte in the input data repeats, up to RLE_MAX_LOOKAHEAD_SIZE""" nc = self.cursor repeats = 0 while read_uintle(self.uncompressed_data, nc) == data and \ repeats < RLE_MAX_LOOKAHEAD_SIZE and \ nc < self.length_input: repeats += 1 nc += 1 return repeats
def _look_ahead_repeats(self, data, cursor_process_multiplier): """Look how often the byte in the input data repeats, up to NRL_LOOKAHEAD_MAX_BYTES""" nc = self.cursor repeats = 0 while read_uintle(self.uncompressed_data, nc) == data and \ repeats < NRL_LOOKAHEAD_ZERO_MAX_BYTES and \ nc < self.length_input: repeats += 1 nc += cursor_process_multiplier return repeats
def get_fixed_floor_properties(cls, overlay10: bytes, config: Pmd2Data) -> List[FixedFloorProperties]: """ Returns the list of properties for fixed floors. """ block = config.binaries['overlay/overlay_0010.bin'].blocks['FixedFloorProperties'] lst = [] for i in range(block.begin, block.end, 12): lst.append(FixedFloorProperties( read_uintle(overlay10, i + 0x00, 4), bool(read_uintle(overlay10, i + 0x04, 1)), bool(read_uintle(overlay10, i + 0x05, 1)), bool(read_uintle(overlay10, i + 0x06, 1)), bool(read_uintle(overlay10, i + 0x07, 1)), bool(read_uintle(overlay10, i + 0x08, 1)), bool(read_uintle(overlay10, i + 0x09, 1)), bool(read_uintle(overlay10, i + 0x0A, 1)), read_uintle(overlay10, i + 0x0B, 1) )) return lst
def _process(self): len_seq, sequence = self._look_ahead_two_int_sequence() if DEBUG: cursor_before = self.cursor wr_before = self.bytes_written print(f"Read a sequence of length {len_seq}") if len_seq > NRL_MIN_SEQ_LEN: # CMD_COPY_BYTES if DEBUG: print(f"CMD_COPY_BYTES") self.cursor += len_seq * 4 cmd_byte = CMD_COPY_BYTES + (len_seq - 1) self._write_cmd(cmd_byte) for b in iter_bytes(sequence, 4): self._write_pair24(b) else: current_int_pair = self._read() repeats = self._look_ahead_repeats(current_int_pair) if DEBUG: print(f"Read {repeats} repeats of {current_int_pair:08x}") self.cursor += repeats * 4 if read_uintle(current_int_pair, 0, 4) == 0: if DEBUG: print(f"CMD_ZERO_OUT") # CMD_ZERO_OUT cmd_byte = repeats self._write_cmd(cmd_byte) else: # CMD_FILL_OUT if DEBUG: print(f"CMD_FILL_OUT") # To big for one cmd, just make it into two. if repeats > NRL_LOOKAHEAD_FILL_MAX_BYTES: repeats_byte1 = repeats - NRL_LOOKAHEAD_FILL_MAX_BYTES cmd_byte1 = CMD_FILL_OUT + (repeats_byte1 - 1) cmd_byte2 = CMD_FILL_OUT + (repeats - repeats_byte1) self._write_cmd(cmd_byte1) self._write_pair24(current_int_pair) self._write_cmd(cmd_byte2) self._write_pair24(current_int_pair) else: cmd_byte = CMD_FILL_OUT + repeats self._write_cmd(cmd_byte) self._write_pair24(current_int_pair) if DEBUG: print( f"-- cursor advancement: {self.cursor - cursor_before} -- write advancement: {self.bytes_written - wr_before}" )
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 get_monster_spawn_stats_table(cls, overlay10: bytes, config: Pmd2Data) -> List[MonsterSpawnStats]: """ Returns the list of monsters that can be spawned in fixed floors. """ block = config.binaries['overlay/overlay_0010.bin'].blocks['MonsterSpawnStatsTable'] lst = [] for i in range(block.begin, block.end, 12): lst.append(MonsterSpawnStats( read_uintle(overlay10, i + 0x00, 2), read_uintle(overlay10, i + 0x02, 2), read_uintle(overlay10, i + 0x04, 2), read_uintle(overlay10, i + 0x06, 1), read_uintle(overlay10, i + 0x07, 1), read_uintle(overlay10, i + 0x08, 1), read_uintle(overlay10, i + 0x09, 1), read_uintle(overlay10, i + 0x0A, 2) )) return lst
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 __init__(self, data: bytes, header_start: int): if not isinstance(data, memoryview): data = memoryview(data) self.list: List[Pmd2ScriptEntity] = [] pointer_start = read_uintle(data, header_start, 4) number_entries = read_uintle(data, header_start + 4, 4) for i in range(0, number_entries): start = pointer_start + (i * LEN_ACTOR_ENTRY) self.list.append( Pmd2ScriptEntity(id=i, type=read_uintle(data, start + 0, 2), entid=read_uintle(data, start + 2, 2), name=self._read_string( data, read_uintle(data, start + 4, 4)), unk3=read_uintle(data, start + 8, 2), unk4=read_uintle(data, start + 10, 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 from_mappa(cls, read: 'MappaBinReadContainer', pointer: int) -> 'MappaTrapList': weights = [] for i in range(pointer, pointer + 50, 2): weights.append(read_uintle(read.data, i, 2)) return MappaTrapList(weights)
def _is_end_of_entries(cls, data: memoryview, pointer): return read_uintle(data, pointer + 6, 2) == 0
def __init__(self, data: bytes): if not isinstance(data, memoryview): data = memoryview(data) self.mappings = [] for pos in range(0, len(data), 2): self.mappings.append(read_uintle(data, pos, 2))
def from_mappa(cls, read: 'MappaBinReadContainer', pointer: int): terrain_settings_bitflag = read_uintle(read.data, pointer + 0x0D) terrain_settings = MappaFloorTerrainSettings( *(bool(terrain_settings_bitflag >> i & 1) for i in range(8))) return cls( structure=MappaFloorStructureType( read_uintle(read.data, pointer + 0x00)), room_density=read_uintle(read.data, pointer + 0x01), tileset_id=read_uintle(read.data, pointer + 0x02), music_id=read_uintle(read.data, pointer + 0x03), weather=MappaFloorWeather(read_uintle(read.data, pointer + 0x04)), floor_connectivity=read_uintle(read.data, pointer + 0x05), initial_enemy_density=read_uintle(read.data, pointer + 0x06), kecleon_shop_chance=read_uintle(read.data, pointer + 0x07), monster_house_chance=read_uintle(read.data, pointer + 0x08), unusued_chance=read_uintle(read.data, pointer + 0x09), sticky_item_chance=read_uintle(read.data, pointer + 0x0A), dead_ends=bool(read_uintle(read.data, pointer + 0x0B)), secondary_terrain=MappaFloorSecondaryTerrainType( read_uintle(read.data, pointer + 0x0C)), terrain_settings=terrain_settings, unk_e=bool(read_uintle(read.data, pointer + 0x0E)), item_density=read_uintle(read.data, pointer + 0x0F), trap_density=read_uintle(read.data, pointer + 0x10), floor_number=read_uintle(read.data, pointer + 0x11), fixed_floor_id=read_uintle(read.data, pointer + 0x12), extra_hallway_density=read_uintle(read.data, pointer + 0x13), buried_item_density=read_uintle(read.data, pointer + 0x14), water_density=read_uintle(read.data, pointer + 0x15), darkness_level=MappaFloorDarknessLevel( read_uintle(read.data, pointer + 0x16)), max_coin_amount=read_uintle(read.data, pointer + 0x17) * 5, kecleon_shop_item_positions=read_uintle(read.data, pointer + 0x18), empty_monster_house_chance=read_uintle(read.data, pointer + 0x19), unk_hidden_stairs=read_uintle(read.data, pointer + 0x1A), hidden_stairs_spawn_chance=read_uintle(read.data, pointer + 0x1B), enemy_iq=read_uintle(read.data, pointer + 0x1C, 2), iq_booster_allowed=bool(read_uintle(read.data, pointer + 0x1E)))
def validate_mappa_sir0(data: bytes, header_start: int, content_pointer_offsets: List[int]): """ Reads through the mappa file, collects all known possible pointers and checks if all of them are in the pointer list, and no more. """ # The 5 header pointer count_pointers = 5 dungeon_list_index_start = read_uintle(data, header_start + 0x00, 4) assert header_start + 0x00 in content_pointer_offsets floor_layout_data_start = read_uintle(data, header_start + 0x04, 4) assert header_start + 0x04 in content_pointer_offsets item_spawn_list_index_start = read_uintle(data, header_start + 0x08, 4) assert header_start + 0x08 in content_pointer_offsets monster_spawn_list_index_start = read_uintle(data, header_start + 0x0C, 4) assert header_start + 0x0C in content_pointer_offsets trap_spawn_list_index_start = read_uintle(data, header_start + 0x10, 4) assert header_start + 0x10 in content_pointer_offsets # Read floor list list start = dungeon_list_index_start end = floor_layout_data_start dungeons = [] for i in range(start, end, 4): pnt_floor_index_entry = read_uintle(data, i, 4) assert i in content_pointer_offsets count_pointers += 1 # Read floor list assert not any( x in content_pointer_offsets for x in range(pnt_floor_index_entry, pnt_floor_index_entry + 18)) floor_data_pos = floor_layout_data_start + 32 * read_uintle( data, pnt_floor_index_entry + 0x00, 2) assert floor_data_pos not in content_pointer_offsets pokemon_spawn_pnt = monster_spawn_list_index_start + 4 * read_uintle( data, pnt_floor_index_entry + 0x02, 2) assert pokemon_spawn_pnt in content_pointer_offsets trap_spawn_pnt = trap_spawn_list_index_start + 4 * read_uintle( data, pnt_floor_index_entry + 0x04, 2) assert trap_spawn_pnt in content_pointer_offsets item_spawn_pnt = item_spawn_list_index_start + 4 * read_uintle( data, pnt_floor_index_entry + 0x06, 2) assert item_spawn_pnt in content_pointer_offsets item_shop_spawn_pnt = item_spawn_list_index_start + 4 * read_uintle( data, pnt_floor_index_entry + 0x08, 2) assert item_shop_spawn_pnt in content_pointer_offsets item_mhouse_spawn_pnt = item_spawn_list_index_start + 4 * read_uintle( data, pnt_floor_index_entry + 0x0A, 2) assert item_mhouse_spawn_pnt in content_pointer_offsets item_buried_spawn_pnt = item_spawn_list_index_start + 4 * read_uintle( data, pnt_floor_index_entry + 0x0C, 2) assert item_buried_spawn_pnt in content_pointer_offsets item_unk1_spawn_pnt = item_spawn_list_index_start + 4 * read_uintle( data, pnt_floor_index_entry + 0x0E, 2) assert item_unk1_spawn_pnt in content_pointer_offsets item_unk2_spawn_pnt = item_spawn_list_index_start + 4 * read_uintle( data, pnt_floor_index_entry + 0x10, 2) assert item_unk2_spawn_pnt in content_pointer_offsets return dungeons