Exemplo n.º 1
0
 def from_xml(cls, xml: Element, value_to_update: List[Optional[KaoImage]]):
     if len(value_to_update) != len(xml):
         raise XmlValidateError(
             f"Incompatible XML. The number of portraits don't match with the expected value of {len(value_to_update)}"
         )
     for i, xml_portrait in enumerate(xml):
         validate_xml_tag(xml_portrait, XML_PORTRAITS_PORTRAIT)
         if len(xml_portrait) > 0:
             image = None
             palette = None
             for xml_image_or_pal in xml_portrait:
                 if xml_image_or_pal.tag == XML_PORTRAITS_PORTRAIT__IMAGE:
                     image = xml_image_or_pal.text
                 elif xml_image_or_pal.tag == XML_PORTRAITS_PORTRAIT__PALETTE:
                     palette = xml_image_or_pal.text
             if image is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_PORTRAITS_PORTRAIT__IMAGE}' missing for a portrait."
                 )
             if palette is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_PORTRAITS_PORTRAIT__PALETTE}' missing for a portrait."
                 )
             try:
                 value_to_update[i] = KaoImage(
                     b64decode(palette.encode('ascii')) +
                     b64decode(image.encode('ascii')), 0)
             except Exception as err:
                 raise XmlValidateError(
                     f"Invalid XML. The portrait data of one of the portraits is invalid: {err}"
                 ) from err
         else:
             value_to_update[i] = None
Exemplo n.º 2
0
 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}')))
Exemplo n.º 3
0
 def from_xml(cls, ele: Element) -> 'XmlSerializable':
     validate_xml_tag(ele, XML_ITEM_LIST)
     categories = {}
     items = {}
     for child in ele:
         if child.tag == XML_CATEGORY:
             validate_xml_attribs(
                 child, [XML_CATEGORY__NAME, XML_CATEGORY__WEIGHT])
             name = child.get(XML_CATEGORY__NAME)
             if not hasattr(MappaItemCategory, name):
                 raise XmlValidateError(f"Unknown item category {name}.")
             weight_str = child.get(XML_CATEGORY__WEIGHT)
             weight = int(
                 weight_str) if weight_str != 'GUARANTEED' else GUARANTEED
             categories[getattr(MappaItemCategory, name)] = weight
         elif child.tag == XML_ITEM:
             validate_xml_attribs(child, [XML_ITEM__ID, XML_ITEM__WEIGHT])
             weight_str = child.get(XML_ITEM__WEIGHT)
             weight = int(
                 weight_str) if weight_str != 'GUARANTEED' else GUARANTEED
             items[Pmd2DungeonItem(int(child.get(XML_ITEM__ID)),
                                   '???')] = weight
         else:
             raise XmlValidateError(
                 f"Unexpected sub-node for {XML_ITEM_LIST}: {child.tag}")
     return cls(categories, items)
Exemplo n.º 4
0
 def from_xml(cls, ele: Element) -> 'MappaBin':
     validate_xml_tag(ele, XML_MAPPA)
     floor_lists = []
     for x_floor_list in ele:
         floor_list = []
         validate_xml_tag(x_floor_list, XML_FLOOR_LIST)
         for x_floor in x_floor_list:
             floor_list.append(MappaFloor.from_xml(x_floor))
         floor_lists.append(floor_list)
     return cls(floor_lists)
Exemplo n.º 5
0
 def to_xml(self) -> Element:
     mappa_xml = Element(XML_MAPPA)
     for i, floor_list in enumerate(self.floor_lists):
         floor_list_xml = Element(XML_FLOOR_LIST)
         for floor in floor_list:
             floor_xml = floor.to_xml()
             validate_xml_tag(floor_xml, XML_FLOOR)
             floor_list_xml.append(floor_xml)
         mappa_xml.append(floor_list_xml)
     return mappa_xml
Exemplo n.º 6
0
 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))),
     )
Exemplo n.º 7
0
 def from_xml(cls, xml: Element, value_to_update: LevelBinEntry):
     if len(xml) != 100:
         raise XmlValidateError(
             "Invalid XML. StatsGrowth must have exactly 100 levels.")
     for i, xml_level in enumerate(xml):
         validate_xml_tag(xml_level, XML_STATS_GROWTH_LEVEL)
         required_exp = None
         hp = None
         attack = None
         sp_attack = None
         defense = None
         sp_defense = None
         for xml_stat in xml_level:
             if xml_stat.tag == XML_STATS_GROWTH_LEVEL__REQUIRED_EXP:
                 required_exp = int(xml_stat.text)
             elif xml_stat.tag == XML_STATS_GROWTH_LEVEL__HP:
                 hp = int(xml_stat.text)
             elif xml_stat.tag == XML_STATS_GROWTH_LEVEL__ATTACK:
                 attack = int(xml_stat.text)
             elif xml_stat.tag == XML_STATS_GROWTH_LEVEL__SP_ATTACK:
                 sp_attack = int(xml_stat.text)
             elif xml_stat.tag == XML_STATS_GROWTH_LEVEL__DEFENSE:
                 defense = int(xml_stat.text)
             elif xml_stat.tag == XML_STATS_GROWTH_LEVEL__SP_DEFENSE:
                 sp_defense = int(xml_stat.text)
         if required_exp is None:
             raise XmlValidateError(
                 f"Invalid XML. '{XML_STATS_GROWTH_LEVEL__REQUIRED_EXP}' missing for a stats growth level entry."
             )
         if hp is None:
             raise XmlValidateError(
                 f"Invalid XML. '{XML_STATS_GROWTH_LEVEL__HP}' missing for a stats growth level entry."
             )
         if attack is None:
             raise XmlValidateError(
                 f"Invalid XML. '{XML_STATS_GROWTH_LEVEL__ATTACK}' missing for a stats growth level entry."
             )
         if sp_attack is None:
             raise XmlValidateError(
                 f"Invalid XML. '{XML_STATS_GROWTH_LEVEL__SP_ATTACK}' missing for a stats growth level entry."
             )
         if defense is None:
             raise XmlValidateError(
                 f"Invalid XML. '{XML_STATS_GROWTH_LEVEL__DEFENSE}' missing for a stats growth level entry."
             )
         if sp_defense is None:
             raise XmlValidateError(
                 f"Invalid XML. '{XML_STATS_GROWTH_LEVEL__SP_DEFENSE}' missing for a stats growth level entry."
             )
         value_to_update.levels[i] = LevelEntry(required_exp, hp, attack,
                                                sp_attack, defense,
                                                sp_defense, 0)
Exemplo n.º 8
0
    def export_to_xml(self) -> Tuple[Element, Dict[int, Image.Image]]:
        font_xml = Element(XML_FONT)

        tables = dict()
        for t in FONT_VALID_TABLES:
            tables[t] = Element(XML_TABLE, {XML_TABLE__ID: str(t)})
            font_xml.append(tables[t])
        for item in self.entries:
            if item.table in FONT_VALID_TABLES:
                xml_char = item.to_xml()
                validate_xml_tag(xml_char, XML_CHAR)
                tables[item.table].append(xml_char)
        return font_xml, self.to_pil()
Exemplo n.º 9
0
 def from_xml(cls, ele: Element) -> 'XmlSerializable':
     validate_xml_tag(ele, XML_TRAP_LIST)
     weights = {}
     for child in ele:
         validate_xml_tag(child, XML_TRAP)
         validate_xml_attribs(child, [XML_TRAP__NAME, XML_TRAP__WEIGHT])
         name = child.get(XML_TRAP__NAME)
         if not hasattr(MappaTrapType, name):
             raise XmlValidateError(f(_("Unknown trap {name}.")))
         weights[getattr(MappaTrapType, name)] = int(child.get(XML_TRAP__WEIGHT))
     try:
         return cls(weights)
     except ValueError as ex:
         raise XmlValidateError(_("Trap lists need an entry for all of the 25 traps")) from ex
Exemplo n.º 10
0
 def from_xml(cls, xml: Element, value_to_update: MoveLearnset):
     for xml_type in xml:
         if xml_type.tag == XML_MOVESET_LEVEL_UP:
             new_level_up = []
             for xml_learn in xml_type:
                 validate_xml_tag(xml_learn, XML_MOVESET_LEVEL_UP__LEARN)
                 level = None
                 move_id = None
                 for xml_level_or_move in xml_learn:
                     if xml_level_or_move.tag == XML_MOVESET_LEVEL_UP__LEVEL:
                         level = int(xml_level_or_move.text)
                     elif xml_level_or_move.tag == XML_MOVESET__MOVE_ID:
                         move_id = int(xml_level_or_move.text)
                 if level is None:
                     raise XmlValidateError(
                         f"Invalid XML. '{XML_MOVESET_LEVEL_UP__LEVEL}' missing for a level up moveset entry."
                     )
                 if move_id is None:
                     raise XmlValidateError(
                         f"Invalid XML. '{XML_MOVESET__MOVE_ID}' missing for a level up moveset entry."
                     )
                 new_level_up.append(LevelUpMove(move_id, level))
             value_to_update.level_up_moves = new_level_up
         elif xml_type.tag == XML_MOVESET_EGG:
             new_eggs = []
             for xml_move_id in xml_type:
                 validate_xml_tag(xml_move_id, XML_MOVESET__MOVE_ID)
                 new_eggs.append(int(xml_move_id.text))
             value_to_update.egg_moves = new_eggs
         elif xml_type.tag == XML_MOVESET_HM_TM:
             new_hm_tm = []
             for xml_move_id in xml_type:
                 validate_xml_tag(xml_move_id, XML_MOVESET__MOVE_ID)
                 new_hm_tm.append(int(xml_move_id.text))
             value_to_update.tm_hm_moves = new_hm_tm
Exemplo n.º 11
0
 def import_from_xml(self, xml: Element, tables: Dict[int, Image.Image]):
     self.entries = []
     validate_xml_tag(xml, XML_FONT)
     for child in xml:
         validate_xml_tag(child, 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:
             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))
                 cat = int(char.get(XML_CHAR__CAT,
                                    default=FONT_DEFAULT_CAT))
                 padding = int(
                     char.get(XML_CHAR__PADDING,
                              default=FONT_DEFAULT_PADDING))
                 x = (charid % 16) * FONT_SIR0_SIZE
                 y = (charid // 16) * FONT_SIR0_SIZE
                 self.entries.append(
                     FontSir0Entry.from_pil(
                         tables[t].crop(box=[
                             x, y, x + FONT_SIR0_SIZE, y + FONT_SIR0_SIZE
                         ]), charid, t, width, cat, padding))
     pass
Exemplo n.º 12
0
 def from_xml(cls, xml: Element, value_to_update: MdEntry):
     for sub_xml in xml:
         if sub_xml.tag in XML_GENENT__MAP__SIMPLE.keys():
             attr_name = XML_GENENT__MAP__SIMPLE[sub_xml.tag]
             if attr_name in XML_GENENT__MAP__ENUMS.keys():
                 # Enum
                 setattr(
                     value_to_update, attr_name,
                     XML_GENENT__MAP__ENUMS[attr_name](int(sub_xml.text)))
             else:
                 # Simple value
                 setattr(value_to_update, attr_name, int(sub_xml.text))
         if sub_xml.tag == XML_GENENT_EVOLUTION_REQ:
             pre_evo_index = None
             method = None
             param1 = None
             param2 = None
             for value_xml in sub_xml:
                 if value_xml.tag == XML_GENENT_EVOLUTION_REQ__PRE_EVO_INDEX:
                     pre_evo_index = int(value_xml.text)
                 elif value_xml.tag == XML_GENENT_EVOLUTION_REQ__EVO_METHOD:
                     method = EvolutionMethod(int(value_xml.text))
                 elif value_xml.tag == XML_GENENT_EVOLUTION_REQ__EVO_PRAM1:
                     param1 = int(value_xml.text)
                 elif value_xml.tag == XML_GENENT_EVOLUTION_REQ__EVO_PRAM2:
                     param2 = int(value_xml.text)
             if pre_evo_index is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_EVOLUTION_REQ__PRE_EVO_INDEX}' missing for a {XML_GENENT_EVOLUTION_REQ}."
                 )
             if method is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_EVOLUTION_REQ__EVO_METHOD}' missing for a {XML_GENENT_EVOLUTION_REQ}."
                 )
             if param1 is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_EVOLUTION_REQ__EVO_PRAM1}' missing for a {XML_GENENT_EVOLUTION_REQ}."
                 )
             if param2 is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_EVOLUTION_REQ__EVO_PRAM2}' missing for a {XML_GENENT_EVOLUTION_REQ}."
                 )
             value_to_update.pre_evo_index = pre_evo_index
             value_to_update.evo_method = method
             value_to_update.evo_param1 = param1
             value_to_update.evo_param2 = param2
         if sub_xml.tag == XML_GENENT_BASE_STATS:
             hp = None
             attack = None
             sp_attack = None
             defense = None
             sp_defense = None
             for value_xml in sub_xml:
                 if value_xml.tag == XML_GENENT_BASE_STATS__HP:
                     hp = int(value_xml.text)
                 elif value_xml.tag == XML_GENENT_BASE_STATS__ATTACK:
                     attack = int(value_xml.text)
                 elif value_xml.tag == XML_GENENT_BASE_STATS__SP_ATTACK:
                     sp_attack = int(value_xml.text)
                 elif value_xml.tag == XML_GENENT_BASE_STATS__DEFENSE:
                     defense = int(value_xml.text)
                 elif value_xml.tag == XML_GENENT_BASE_STATS__SP_DEFENSE:
                     sp_defense = int(value_xml.text)
             if hp is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BASE_STATS__HP}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             if attack is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BASE_STATS__ATTACK}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             if sp_attack is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BASE_STATS__SP_ATTACK}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             if defense is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BASE_STATS__DEFENSE}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             if sp_defense is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_STATS_GROWTH_LEVEL__SP_DEFENSE}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             value_to_update.base_hp = hp
             value_to_update.base_atk = attack
             value_to_update.base_sp_atk = sp_attack
             value_to_update.base_def = defense
             value_to_update.base_sp_def = sp_defense
         if sub_xml.tag == XML_GENENT_EXCLUSIVE_ITEMS:
             if len(sub_xml) != 4:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_EXCLUSIVE_ITEMS}' needs four item IDs."
                 )
             update = ('exclusive_item1', 'exclusive_item2',
                       'exclusive_item3', 'exclusive_item4')
             for item_xml, attr_name in zip(sub_xml, update):
                 validate_xml_tag(item_xml,
                                  XML_GENENT_EXCLUSIVE_ITEMS__ITEM_ID)
                 setattr(value_to_update, attr_name, int(item_xml.text))
         if sub_xml.tag == XML_GENENT_BITFLAG1:
             unk0 = None
             unk1 = None
             unk2 = None
             unk3 = None
             unk4 = None
             unk5 = None
             unk6 = None
             unk7 = None
             for value_xml in sub_xml:
                 if value_xml.tag == XML_GENENT_BITFLAG1__UNK0:
                     unk0 = bool(int(value_xml.text))
                 elif value_xml.tag == XML_GENENT_BITFLAG1__UNK1:
                     unk1 = bool(int(value_xml.text))
                 elif value_xml.tag == XML_GENENT_BITFLAG1__UNK2:
                     unk2 = bool(int(value_xml.text))
                 elif value_xml.tag == XML_GENENT_BITFLAG1__UNK3:
                     unk3 = bool(int(value_xml.text))
                 elif value_xml.tag == XML_GENENT_BITFLAG1__UNK4:
                     unk4 = bool(int(value_xml.text))
                 elif value_xml.tag == XML_GENENT_BITFLAG1__UNK5:
                     unk5 = bool(int(value_xml.text))
                 elif value_xml.tag == XML_GENENT_BITFLAG1__UNK6:
                     unk6 = bool(int(value_xml.text))
                 elif value_xml.tag == XML_GENENT_BITFLAG1__UNK7:
                     unk7 = bool(int(value_xml.text))
             if unk0 is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BITFLAG1__UNK0}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             if unk1 is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BITFLAG1__UNK1}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             if unk2 is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BITFLAG1__UNK2}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             if unk3 is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BITFLAG1__UNK3}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             if unk4 is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BITFLAG1__UNK4}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             if unk5 is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BITFLAG1__UNK5}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             if unk6 is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BITFLAG1__UNK6}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             if unk7 is None:
                 raise XmlValidateError(
                     f"Invalid XML. '{XML_GENENT_BITFLAG1__UNK7}' missing for a {XML_GENENT_BASE_STATS}."
                 )
             value_to_update.bitfield1_0 = unk0
             value_to_update.bitfield1_1 = unk1
             value_to_update.bitfield1_2 = unk2
             value_to_update.bitfield1_3 = unk3
             value_to_update.can_move = unk4
             value_to_update.bitfield1_5 = unk5
             value_to_update.can_evolve = unk6
             value_to_update.item_required_for_spawning = unk7
Exemplo n.º 13
0
    def to_xml(self,
               export_layout=True,
               export_monsters=True,
               export_traps=True,
               export_floor_items=True,
               export_shop_items=True,
               export_monster_house_items=True,
               export_buried_items=True,
               export_unk1_items=True,
               export_unk2_items=True) -> Element:
        floor_xml = Element(XML_FLOOR)

        if export_layout:
            layout_xml = self.layout.to_xml()
            validate_xml_tag(layout_xml, XML_FLOOR_LAYOUT)
            floor_xml.append(layout_xml)

        if export_monsters:
            monsters_xml = Element(XML_MONSTER_LIST)
            for monster in self.monsters:
                monster_xml = monster.to_xml()
                validate_xml_tag(monster_xml, XML_MONSTER)
                monsters_xml.append(monster_xml)
            floor_xml.append(monsters_xml)

        if export_traps:
            traps_xml = self.traps.to_xml()
            validate_xml_tag(traps_xml, XML_TRAP_LIST)
            floor_xml.append(traps_xml)

        if export_floor_items:
            floor_items_xml = self.floor_items.to_xml()
            validate_xml_tag(floor_items_xml, XML_ITEM_LIST)
            floor_items_xml.set(XML_ITEM_LIST__TYPE,
                                XML_ITEM_LIST__TYPE__FLOOR)
            floor_xml.append(floor_items_xml)

        if export_shop_items:
            shop_items_xml = self.shop_items.to_xml()
            validate_xml_tag(shop_items_xml, XML_ITEM_LIST)
            shop_items_xml.set(XML_ITEM_LIST__TYPE, XML_ITEM_LIST__TYPE__SHOP)
            floor_xml.append(shop_items_xml)

        if export_monster_house_items:
            monster_house_items_xml = self.monster_house_items.to_xml()
            validate_xml_tag(monster_house_items_xml, XML_ITEM_LIST)
            monster_house_items_xml.set(XML_ITEM_LIST__TYPE,
                                        XML_ITEM_LIST__TYPE__MONSTER_HOUSE)
            floor_xml.append(monster_house_items_xml)

        if export_buried_items:
            buried_items_xml = self.buried_items.to_xml()
            validate_xml_tag(buried_items_xml, XML_ITEM_LIST)
            buried_items_xml.set(XML_ITEM_LIST__TYPE,
                                 XML_ITEM_LIST__TYPE__BURIED)
            floor_xml.append(buried_items_xml)

        if export_unk1_items:
            unk_items1_xml = self.unk_items1.to_xml()
            validate_xml_tag(unk_items1_xml, XML_ITEM_LIST)
            unk_items1_xml.set(XML_ITEM_LIST__TYPE, XML_ITEM_LIST__TYPE__UNK1)
            floor_xml.append(unk_items1_xml)

        if export_unk2_items:
            unk_items2_xml = self.unk_items2.to_xml()
            validate_xml_tag(unk_items2_xml, XML_ITEM_LIST)
            unk_items2_xml.set(XML_ITEM_LIST__TYPE, XML_ITEM_LIST__TYPE__UNK2)
            floor_xml.append(unk_items2_xml)

        return floor_xml
Exemplo n.º 14
0
    def from_xml(cls, ele: Element) -> 'MappaFloorLayout':
        validate_xml_tag(ele, XML_FLOOR_LAYOUT)
        generator_settings = None
        chances = None
        terrain_settings = None
        misc = None
        for child in ele:
            if child.tag == XML_FLOOR_LAYOUT__GENSET:
                generator_settings = child
            elif child.tag == XML_FLOOR_LAYOUT__CHANCES:
                chances = child
            elif child.tag == XML_FLOOR_LAYOUT__TERRAINSET:
                terrain_settings = child
            elif child.tag == XML_FLOOR_LAYOUT__MISCSET:
                misc = child
            else:
                raise XmlValidateError(
                    f(
                        _("Unexpected sub-node for {XML_FLOOR_LAYOUT}: {child.tag}"
                          )))

        if generator_settings is None:
            raise XmlValidateError(
                f(
                    _("{XML_FLOOR_LAYOUT__GENSET} missing for {XML_FLOOR_LAYOUT}."
                      )))

        if chances is None:
            raise XmlValidateError(
                f(
                    _("{XML_FLOOR_LAYOUT__CHANCES} missing for {XML_FLOOR_LAYOUT}."
                      )))

        if terrain_settings is None:
            raise XmlValidateError(
                f(
                    _("{XML_FLOOR_LAYOUT__TERRAINSET} missing for {XML_FLOOR_LAYOUT}."
                      )))

        if misc is None:
            raise XmlValidateError(
                f(
                    _("{XML_FLOOR_LAYOUT__MISCSET} missing for {XML_FLOOR_LAYOUT}."
                      )))

        validate_xml_attribs(ele, [
            XML_FLOOR_LAYOUT__STRUCTURE, XML_FLOOR_LAYOUT__TILESET,
            XML_FLOOR_LAYOUT__BGM, XML_FLOOR_LAYOUT__WEATHER,
            XML_FLOOR_LAYOUT__NUMBER, XML_FLOOR_LAYOUT__FIXED_FLOOR_ID,
            XML_FLOOR_LAYOUT__DARKNESS_LEVEL
        ])

        validate_xml_attribs(generator_settings, [
            XML_FLOOR_LAYOUT__GENSET__ROOM_DENSITY,
            XML_FLOOR_LAYOUT__GENSET__FLOOR_CONNECTIVITY,
            XML_FLOOR_LAYOUT__GENSET__INITIAL_ENEMY_DENSITY,
            XML_FLOOR_LAYOUT__GENSET__DEAD_ENDS,
            XML_FLOOR_LAYOUT__GENSET__ITEM_DENSITY,
            XML_FLOOR_LAYOUT__GENSET__TRAP_DENSITY,
            XML_FLOOR_LAYOUT__GENSET__EXTRA_HALLWAY_DENSITY,
            XML_FLOOR_LAYOUT__GENSET__BURIED_ITEM_DENSITY,
            XML_FLOOR_LAYOUT__GENSET__WATER_DENSITY,
            XML_FLOOR_LAYOUT__GENSET__MAX_COIN_AMOUNT
        ])

        validate_xml_attribs(chances, [
            XML_FLOOR_LAYOUT__CHANCES__SHOP,
            XML_FLOOR_LAYOUT__CHANCES__MONSTER_HOUSE,
            XML_FLOOR_LAYOUT__CHANCES__UNUSED,
            XML_FLOOR_LAYOUT__CHANCES__STICKY_ITEM,
            XML_FLOOR_LAYOUT__CHANCES__EMPTY_MONSTER_HOUSE,
            XML_FLOOR_LAYOUT__CHANCES__HIDDEN_STAIRS
        ])

        validate_xml_attribs(terrain_settings, [
            XML_FLOOR_LAYOUT__TERRAINSET__SECONDARY_USED,
            XML_FLOOR_LAYOUT__TERRAINSET__SECONDARY_TYPE,
            XML_FLOOR_LAYOUT__TERRAINSET__IMPERFECT_ROOMS,
            XML_FLOOR_LAYOUT__TERRAINSET__UNK1,
            XML_FLOOR_LAYOUT__TERRAINSET__UNK3,
            XML_FLOOR_LAYOUT__TERRAINSET__UNK4,
            XML_FLOOR_LAYOUT__TERRAINSET__UNK5,
            XML_FLOOR_LAYOUT__TERRAINSET__UNK6,
            XML_FLOOR_LAYOUT__TERRAINSET__UNK7
        ])

        validate_xml_attribs(misc, [
            XML_FLOOR_LAYOUT__MISCSET__UNKE,
            XML_FLOOR_LAYOUT__MISCSET__KECLEON_SHOP_ITEM_POSITIONS,
            XML_FLOOR_LAYOUT__MISCSET__UNK_HIDDEN_STAIRS,
            XML_FLOOR_LAYOUT__MISCSET__ENEMY_IQ,
            XML_FLOOR_LAYOUT__MISCSET__IQ_BOOSTER_BOOST
        ])

        if not hasattr(MappaFloorStructureType,
                       ele.get(XML_FLOOR_LAYOUT__STRUCTURE)):
            raise XmlValidateError(
                f(
                    _("Invalid structure type {ele.get(XML_FLOOR_LAYOUT__STRUCTURE)}"
                      )))
        structure = getattr(MappaFloorStructureType,
                            ele.get(XML_FLOOR_LAYOUT__STRUCTURE))

        if not hasattr(MappaFloorWeather, ele.get(XML_FLOOR_LAYOUT__WEATHER)):
            raise XmlValidateError(
                f(
                    _("Invalid weather type {ele.get(XML_FLOOR_LAYOUT__WEATHER)}"
                      )))
        weather = getattr(MappaFloorWeather,
                          ele.get(XML_FLOOR_LAYOUT__WEATHER))

        if not hasattr(MappaFloorDarknessLevel,
                       ele.get(XML_FLOOR_LAYOUT__DARKNESS_LEVEL)):
            raise XmlValidateError(
                f(
                    _("Invalid darkness level type {ele.get(XML_FLOOR_LAYOUT__DARKNESS_LEVEL)}"
                      )))
        darkness_level = getattr(MappaFloorDarknessLevel,
                                 ele.get(XML_FLOOR_LAYOUT__DARKNESS_LEVEL))

        return cls(
            structure=structure,
            room_density=i8_checked(
                int(
                    generator_settings.get(
                        XML_FLOOR_LAYOUT__GENSET__ROOM_DENSITY))),
            tileset_id=u8_checked(int(ele.get(XML_FLOOR_LAYOUT__TILESET))),
            music_id=u8_checked(int(ele.get(XML_FLOOR_LAYOUT__BGM))),
            weather=weather,
            floor_connectivity=u8_checked(
                int(
                    generator_settings.get(
                        XML_FLOOR_LAYOUT__GENSET__FLOOR_CONNECTIVITY))),
            initial_enemy_density=i8_checked(
                int(
                    generator_settings.get(
                        XML_FLOOR_LAYOUT__GENSET__INITIAL_ENEMY_DENSITY))),
            kecleon_shop_chance=u8_checked(
                int(chances.get(XML_FLOOR_LAYOUT__CHANCES__SHOP))),
            monster_house_chance=u8_checked(
                int(chances.get(XML_FLOOR_LAYOUT__CHANCES__MONSTER_HOUSE))),
            unusued_chance=u8_checked(
                int(chances.get(XML_FLOOR_LAYOUT__CHANCES__UNUSED))),
            sticky_item_chance=u8_checked(
                int(chances.get(XML_FLOOR_LAYOUT__CHANCES__STICKY_ITEM))),
            dead_ends=bool(
                int(generator_settings.get(
                    XML_FLOOR_LAYOUT__GENSET__DEAD_ENDS))),
            secondary_terrain=u8_checked(
                int(
                    terrain_settings.get(
                        XML_FLOOR_LAYOUT__TERRAINSET__SECONDARY_TYPE))),
            terrain_settings=MappaFloorTerrainSettings(
                has_secondary_terrain=bool(
                    int(
                        terrain_settings.get(
                            XML_FLOOR_LAYOUT__TERRAINSET__SECONDARY_USED))),
                unk1=bool(
                    int(
                        terrain_settings.get(
                            XML_FLOOR_LAYOUT__TERRAINSET__UNK1))),
                generate_imperfect_rooms=bool(
                    int(
                        terrain_settings.get(
                            XML_FLOOR_LAYOUT__TERRAINSET__IMPERFECT_ROOMS))),
                unk3=bool(
                    int(
                        terrain_settings.get(
                            XML_FLOOR_LAYOUT__TERRAINSET__UNK3))),
                unk4=bool(
                    int(
                        terrain_settings.get(
                            XML_FLOOR_LAYOUT__TERRAINSET__UNK4))),
                unk5=bool(
                    int(
                        terrain_settings.get(
                            XML_FLOOR_LAYOUT__TERRAINSET__UNK5))),
                unk6=bool(
                    int(
                        terrain_settings.get(
                            XML_FLOOR_LAYOUT__TERRAINSET__UNK6))),
                unk7=bool(
                    int(
                        terrain_settings.get(
                            XML_FLOOR_LAYOUT__TERRAINSET__UNK7))),
            ),
            unk_e=bool(int(misc.get(XML_FLOOR_LAYOUT__MISCSET__UNKE))),
            item_density=u8_checked(
                int(
                    generator_settings.get(
                        XML_FLOOR_LAYOUT__GENSET__ITEM_DENSITY))),
            trap_density=u8_checked(
                int(
                    generator_settings.get(
                        XML_FLOOR_LAYOUT__GENSET__TRAP_DENSITY))),
            floor_number=u8_checked(int(ele.get(XML_FLOOR_LAYOUT__NUMBER))),
            fixed_floor_id=u8_checked(
                int(ele.get(XML_FLOOR_LAYOUT__FIXED_FLOOR_ID))),
            extra_hallway_density=u8_checked(
                int(
                    generator_settings.get(
                        XML_FLOOR_LAYOUT__GENSET__EXTRA_HALLWAY_DENSITY))),
            buried_item_density=u8_checked(
                int(
                    generator_settings.get(
                        XML_FLOOR_LAYOUT__GENSET__BURIED_ITEM_DENSITY))),
            water_density=u8_checked(
                int(
                    generator_settings.get(
                        XML_FLOOR_LAYOUT__GENSET__WATER_DENSITY))),
            darkness_level=darkness_level,
            max_coin_amount=int(
                generator_settings.get(
                    XML_FLOOR_LAYOUT__GENSET__MAX_COIN_AMOUNT)),
            kecleon_shop_item_positions=u8_checked(
                int(
                    misc.get(
                        XML_FLOOR_LAYOUT__MISCSET__KECLEON_SHOP_ITEM_POSITIONS)
                )),
            empty_monster_house_chance=u8_checked(
                int(chances.get(
                    XML_FLOOR_LAYOUT__CHANCES__EMPTY_MONSTER_HOUSE))),
            unk_hidden_stairs=u8_checked(
                int(misc.get(XML_FLOOR_LAYOUT__MISCSET__UNK_HIDDEN_STAIRS))),
            hidden_stairs_spawn_chance=u8_checked(
                int(chances.get(XML_FLOOR_LAYOUT__CHANCES__HIDDEN_STAIRS))),
            enemy_iq=u16_checked(
                int(misc.get(XML_FLOOR_LAYOUT__MISCSET__ENEMY_IQ))),
            iq_booster_boost=i16_checked(
                int(misc.get(XML_FLOOR_LAYOUT__MISCSET__IQ_BOOSTER_BOOST))),
        )