def parse_item_folder( folders_to_parse: set[str], filesystem: FileSystem, pak_id: str, ) -> dict[str, ItemVariant]: """Parse through the data in item/ folders. folders is a dict, with the keys set to the folder names we want. The values will be filled in with itemVariant values """ folders: dict[str, ItemVariant] = {} for fold in folders_to_parse: prop_path = 'items/' + fold + '/properties.txt' editor_path = 'items/' + fold + '/editoritems.txt' config_path = 'items/' + fold + '/vbsp_config.cfg' first_item: EditorItem | None = None extra_items: list[EditorItem] = [] try: props = filesystem.read_prop(prop_path).find_key('Properties') f = filesystem[editor_path].open_str() except FileNotFoundError as err: raise IOError('"' + pak_id + ':items/' + fold + '" not valid! ' 'Folder likely missing! ') from err with f: tok = Tokenizer(f, editor_path) for tok_type, tok_value in tok: if tok_type is Token.STRING: if tok_value.casefold() != 'item': raise tok.error('Unknown item option "{}"!', tok_value) if first_item is None: first_item = EditorItem.parse_one(tok) else: extra_items.append(EditorItem.parse_one(tok)) elif tok_type is not Token.NEWLINE: raise tok.error(tok_type) if first_item is None: raise ValueError(f'"{pak_id}:items/{fold}/editoritems.txt has no ' '"Item" block!') try: editor_vmf = VMF.parse( filesystem.read_prop(editor_path[:-3] + 'vmf')) except FileNotFoundError: pass else: editoritems_vmf.load(first_item, editor_vmf) first_item.generate_collisions() # extra_items is any extra blocks (offset catchers, extent items). # These must not have a palette section - it'll override any the user # chooses. for extra_item in extra_items: extra_item.generate_collisions() for subtype in extra_item.subtypes: if subtype.pal_pos is not None: LOGGER.warning( f'"{pak_id}:items/{fold}/editoritems.txt has ' f'palette set for extra item blocks. Deleting.') subtype.pal_icon = subtype.pal_pos = subtype.pal_name = None # In files this is specified as PNG, but it's always really VTF. try: all_icon = FSPath(props['all_icon']).with_suffix('.vtf') except LookupError: all_icon = None folders[fold] = ItemVariant( editoritems=first_item, editor_extra=extra_items, # Add the folder the item definition comes from, # so we can trace it later for debug messages. source=f'<{pak_id}>/items/{fold}', pak_id=pak_id, vbsp_config=lazy_conf.BLANK, authors=sep_values(props['authors', '']), tags=sep_values(props['tags', '']), desc=desc_parse(props, f'{pak_id}:{prop_path}', pak_id), ent_count=props['ent_count', ''], url=props['infoURL', None], icons={ prop.name: img.Handle.parse( prop, pak_id, 64, 64, subfolder='items', ) for prop in props.find_children('icon') }, all_name=props['all_name', None], all_icon=all_icon, ) if Item.log_ent_count and not folders[fold].ent_count: LOGGER.warning( '"{id}:{path}" has missing entity count!', id=pak_id, path=prop_path, ) # If we have one of the grouping icon definitions but not both required # ones then notify the author. has_name = folders[fold].all_name is not None has_icon = folders[fold].all_icon is not None if (has_name or has_icon or 'all' in folders[fold].icons) and (not has_name or not has_icon): LOGGER.warning( 'Warning: "{id}:{path}" has incomplete grouping icon ' 'definition!', id=pak_id, path=prop_path, ) folders[fold].vbsp_config = lazy_conf.from_file( utils.PackagePath(pak_id, config_path), missing_ok=True, source=folders[fold].source, ) return folders
def parse_item_folder( folders_to_parse: Set[str], filesystem: FileSystem, pak_id: str, ) -> Dict[str, ItemVariant]: """Parse through the data in item/ folders. folders is a dict, with the keys set to the folder names we want. The values will be filled in with itemVariant values """ folders: Dict[str, ItemVariant] = {} for fold in folders_to_parse: prop_path = 'items/' + fold + '/properties.txt' editor_path = 'items/' + fold + '/editoritems.txt' config_path = 'items/' + fold + '/vbsp_config.cfg' first_item: Optional[Item] = None extra_items: List[EditorItem] = [] with filesystem: try: props = filesystem.read_prop(prop_path).find_key('Properties') f = filesystem[editor_path].open_str() except FileNotFoundError as err: raise IOError('"' + pak_id + ':items/' + fold + '" not valid!' 'Folder likely missing! ') from err with f: tok = Tokenizer(f, editor_path) for tok_type, tok_value in tok: if tok_type is Token.STRING: if tok_value.casefold() != 'item': raise tok.error('Unknown item option "{}"!', tok_value) if first_item is None: first_item = EditorItem.parse_one(tok) else: extra_items.append(EditorItem.parse_one(tok)) elif tok_type is not Token.NEWLINE: raise tok.error(tok_type) if first_item is None: raise ValueError('"{}:items/{}/editoritems.txt has no ' '"Item" block!'.format(pak_id, fold)) # extra_items is any extra blocks (offset catchers, extent items). # These must not have a palette section - it'll override any the user # chooses. for extra_item in extra_items: for subtype in extra_item.subtypes: if subtype.pal_pos is not None: LOGGER.warning( '"{}:items/{}/editoritems.txt has palette set for extra' ' item blocks. Deleting.'.format(pak_id, fold)) subtype.pal_icon = subtype.pal_pos = subtype.pal_name = None try: all_icon = FSPath(props['all_icon']) except LookupError: all_icon = None folders[fold] = ItemVariant( editoritems=first_item, editor_extra=extra_items, # Add the folder the item definition comes from, # so we can trace it later for debug messages. source='<{}>/items/{}'.format(pak_id, fold), vbsp_config=Property(None, []), authors=sep_values(props['authors', '']), tags=sep_values(props['tags', '']), desc=desc_parse(props, pak_id + ':' + prop_path), ent_count=props['ent_count', ''], url=props['infoURL', None], icons={p.name: p.value for p in props['icon', []]}, all_name=props['all_name', None], all_icon=all_icon, ) if Item.log_ent_count and not folders[fold].ent_count: LOGGER.warning( '"{id}:{path}" has missing entity count!', id=pak_id, path=prop_path, ) # If we have at least 1, but not all of the grouping icon # definitions then notify the author. num_group_parts = ((folders[fold].all_name is not None) + (folders[fold].all_icon is not None) + ('all' in folders[fold].icons)) if 0 < num_group_parts < 3: LOGGER.warning( 'Warning: "{id}:{path}" has incomplete grouping icon ' 'definition!', id=pak_id, path=prop_path, ) try: with filesystem: folders[fold].vbsp_config = conf = filesystem.read_prop( config_path, ) except FileNotFoundError: folders[fold].vbsp_config = conf = Property(None, []) set_cond_source(conf, folders[fold].source) return folders